View Full Version : Open Session In View - only working on IIS 6.0/7.0?
Ismar Slomic
05-14-2007, 08:37 PM
After getting the Exception:
Could not initialize proxy - the owning Session was closed.
Exception Details: NHibernate.LazyInitializationException: Could not initialize proxy - the owning Session was closed.
and reading a bit about it on this forum i realized that I have to pay attention on opening session in view. Since im using Spring.Net NHibernate module and the helping NHIbernateTemplate class the session handling is hidden for me, i tried to do some configuration that I'm not sure if is right.
I'm developing and testing the Web-app in Windows XP environment (IIS 5.1). Reason why I mention the environment is this article (http://www.codeproject.com/aspnet/NHibernateBestPractices.asp) i read where they point that there are different ways to use open session in view depending on the IIS version you are running.
After trying to do configuration on my own i finally get rid of the exception, but only when I start debugging the page from VS2005. But when i choose "View in Browser" for the very same page I still get the same Exception. Is this a IIS version issue, or what? My configurations is like pasted below.
Dao.xml
<object id="PersonDAOImpl" type="ELMS.DAO.Impl.PersonDAOImpl, ELMS.DAO.Impl">
<property name="SessionFactory" ref="sessionFactory"/>
</object>
Services.xml
<object id="PersonServiceImpl" type="ELMS.Service.Impl.PersonServiceImpl, ELMS.Service.Impl">
<property name="Dao" ref="PersonDAOImpl"/>
</object>
<import resource="~/Config/DeclarativeServicesTxProxyFactoryDriven.xml"/>
NHibernate.xml
<object type="Spring.Objects.Factory.Config.PropertyPlaceholderC onfigurer, Spring.Core">
<property name="ConfigSections" value="databaseSettings"/>
</object>
<!-- Database and NHibernate Configuration -->
<db:dbProvider id="DbProvider"
provider="System.Data.SqlClient"
connectionString="${db.connectionString}"/>
<!-- Session Factory Object -->
<object id="sessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate12">
<property name="DbProvider" ref="DbProvider"/>
<property name="MappingAssemblies">
<list>
<value>ELMS.DO</value>
</list>
</property>
<property name="HibernateProperties">
<dictionary>
<entry key="hibernate.connection.provider"
value="NHibernate.Connection.DriverConnectionProvider"/>
<entry key="hibernate.dialect"
value="NHibernate.Dialect.MySQLDialect"/>
<entry key="hibernate.connection.driver_class"
value="NHibernate.Driver.MySqlDataDriver"/>
</dictionary>
</property>
</object>
<!-- Hibernate Transaction Manager -->
<object id="HibernateTransactionManager"
type="Spring.Data.NHibernate.HibernateTransactionManager , Spring.Data.NHibernate12">
<property name="DbProvider" ref="DbProvider"/>
<property name="SessionFactory" ref="sessionFactory"/>
</object>
DeclarativeServicesTxProxyFactoryDriven.xml
<object id="TxProxyConfigurationTemplate" abstract="true"
type="Spring.Transaction.Interceptor.TransactionProxyFac toryObject, Spring.Data">
<property name="PlatformTransactionManager" ref="HibernateTransactionManager"/>
<property name="TransactionAttributes">
<name-values>
<!-- Add common methods across your services here -->
<add key="Delete*" value="PROPAGATION_REQUIRED"/>
<add key="Update*" value="PROPAGATION_REQUIRED"/>
<add key="Get*" value="PROPAGATION_REQUIRED"/>
<add key="GetAll*" value="PROPAGATION_REQUIRED"/>
<add key="Create*" value="PROPAGATION_REQUIRED"/>
<add key="Count*" value="PROPAGATION_REQUIRED"/>
</name-values>
</property>
</object>
<object id="PersonServiceTx" parent="TxProxyConfigurationTemplate">
<property name="Target" ref="PersonServiceImpl"/>
</object>
Web.config (most significant spring + nhibernate config)
<spring>
<parsers>
<parser namespace="http://www.springframework.net/database" type="Spring.Data.DatabaseConfigParser, Spring.Data" schemaLocation="assembly://Spring.Data/Spring.Data/spring-database.xsd"/>
</parsers>
<context>
<resource uri="~/Config/Services.xml"/>
<resource uri="~/Config/Dao.xml"/>
<resource uri="~/Config/Web.xml"/>
<resource uri="~/Config/NHibernate.xml"/>
</context>
</spring>
<system.web>
<httpModules>
<add name="Spring" type="Spring.Context.Support.WebSupportModule, Spring.Web"/>
<add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add name="OpenSessionInView" type="Spring.Data.NHibernate.Support.OpenSessionInViewMo dule, Spring.Data.NHibernate12"/>
</httpModules>
</system.web>
<appSettings>
<add key="Spring.Data.NHibernate.Support.OpenSessionInViewMo dule.SessionFactoryObjectName" value="SessionFactory" />
</appSettings>
Ismar Slomic
05-15-2007, 04:22 PM
Sorry for replying on my own question post, but im really stucked here..Anyone with any usefull info here? Thanks!
//Ismar
Bruno Baia
05-15-2007, 04:45 PM
Hi,
Can you copy paste your configSection configuration (Web.Config) ?
- Bruno
Ismar Slomic
05-15-2007, 04:48 PM
Hi Bruno,
Here is the config from Web.config, configSections
<configSections>
<sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectio nGroup, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<sectionGroup name="scripting" type="System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<section name="scriptResourceHandler" type="System.Web.Configuration.ScriptingScriptResourceHa ndlerSection, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false"/>
<sectionGroup name="webServices" type="System.Web.Configuration.ScriptingWebServicesSecti onGroup, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<section name="jsonSerialization" type="System.Web.Configuration.ScriptingJsonSerializatio nSection, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false"/>
<section name="profileService" type="System.Web.Configuration.ScriptingProfileServiceSe ction, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false"/>
<section name="authenticationService" type="System.Web.Configuration.ScriptingAuthenticationSe rviceSection, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false"/>
</sectionGroup>
</sectionGroup>
</sectionGroup>
<sectionGroup name="spring">
<section name="typeAliases" type="Spring.Context.Support.TypeAliasesSectionHandler, Spring.Core"/>
<section name="parsers" type="Spring.Context.Support.ConfigParsersSectionHandler , Spring.Core"/>
<section name="context" type="Spring.Context.Support.WebContextHandler, Spring.Web"/>
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core"/>
</sectionGroup>
<sectionGroup name="common">
<section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging" />
</sectionGroup>
<section name="databaseSettings" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
</configSections>
Ismar Slomic
05-16-2007, 06:50 PM
You can take a look on the source files here:
http://www.slomic.no/ELMS.zip
AJAX
Web project is using AJAX extensions, or there is configurations in Web.Config for AJAX, but I dont actually use any controls yet. So if you want to test application you need to install AJAX http://www.microsoft.com/downloads/details.aspx?FamilyID=ca9d90fa-e8c9-42e3-aa19-08e2c027f5d6&displaylang=en
or remove AJAX configurations from ELMS.Web/Web.Config and <asp:ScriptManager > tag from Edit.aspx and Default.aspx.
Take also look at the ELMS.WEB/Logs/Log.txt for NHibernate, Spring and other loggins. Can be usefull to track the problem...
MySQL sql:
CREATE DATABASE `test`;
DROP TABLE IF EXISTS `test`.`person`;
CREATE TABLE `test`.`person` (
`ID` bigint(20) NOT NULL default '0',
`LASTNAME` varchar(255) character set latin1 collate latin1_bin default NULL,
`FIRSTNAME` varchar(255) character set latin1 collate latin1_bin default NULL,
`COMPANY` varchar(255) character set latin1 collate latin1_bin default NULL,
`COUNTRY` varchar(255) character set latin1 collate latin1_bin default NULL,
`TLF` varchar(255) character set latin1 collate latin1_bin default NULL,
`EMAIL` varchar(255) character set latin1 collate latin1_bin default NULL,
`VERSION` bigint(20) default '0',
`ISACTIVE` tinyint(4) default NULL,
`USERNAME` varchar(255) default NULL,
`PASSWORD` varchar(255) default NULL,
KEY `ID` (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
DROP TABLE IF EXISTS `test`.`role`;
CREATE TABLE `test`.`role` (
`ID` bigint(20) NOT NULL default '0',
`NAME` varchar(255) character set latin1 collate latin1_bin default NULL,
`VERSION` bigint(20) default '0',
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Bruno Baia
05-16-2007, 09:16 PM
Hi,
To reproduce your error, I add to remove everything related to ASP.NET AJAX 1.0 and use Sql Server Express 2005 instead of MySql.
I got the error when I try to edit a person.
I make it work using "transfer" instead of "redirect" for the page results but this is not a solution.
I can't go deeper as I never used NHibernate before...
- Bruno
Ismar Slomic
05-16-2007, 09:33 PM
Hi Bruno,
tnx for your help! I hope that someone with NHibernate exp can give some usefull hints..
Regards Ismar
Erich Eichinger
05-17-2007, 12:14 AM
Hi Ismar,
your problem has nothing to do with your IIS version.
Your are using lazy loading across different pages - thus you need to ensure, that *all* properties of a person object are loaded into memory before placing it into the session (in Default.aspx, EditPerson() ) :
PersonDO person = personService.Get(personId);
// load all required properties here
string name = person.Name;
...
Session[Constants.SessionEditPersonKey] = person;
Since your are getting a proxy object from nhibernate, I suggest a safer way of handling this: create a "copy constructor" on your person class and place a copy of the person instance you retrieved from nhibernate into the session:
PersonDO person = personService.Get(personId);
Session[Constants.SessionEditPersonKey] = new PersonDO( person );
cheers,
Erich
Ismar Slomic
05-17-2007, 11:26 AM
Hi Erich!
Finally it works! Thank you, Bruno and Mark very much! You made my day now!
Have a very nice day!
Cheers,Ismar
Ismar Slomic
05-22-2007, 11:06 PM
Hi again!
I recognized, like you also pointed, that all properties of the Person object has to be copied new Person instance. That means that if Person object contains some collection properties ( one-to-many mappings ), each item in the collection also need to be copied. And then i put the Person object in the Session and go to Edit.aspx.
When I'm finished with editing the Person in the Edit.aspx and want to do updates to db, i try something like
PersonDO person = (PersonDO)Session[Constants.SessionEditPersonKey];
personService.Update(person);
Update method in DAO is like:
HibernateTemplate.Update(instance);
Then I get the Spring.Dao.InvalidDataAccessApiUsageException
Write operations are not allowed in read-only mode (FlushMode.NEVER) - turn your Session into FlushMode.AUTO or remove 'readOnly' marker from transaction definition
I have been reading little bit about FlushMode in this forum. I have tried to use DeclarativeServicesTxProxyFactoryDriven.xml and the DeclarativeServicesAttributeDriven.xml files from Northwind example, but i still get the same exception. Im sure that in methods for saving/updating (and also other) has [Transaction()] above.
I also want to ask the Spring.Net team if there is any example out there that gives a introduction in how to use 3-tier approach with help of Spring.Net + NHibernate and where the presentation layer is Web. I think alot of users could find this very usefull, because I have spent a lot of time on reading and asking questions about configurations. And my web-app can still not do the basic operations like CRUD.
Erich Eichinger
05-29-2007, 02:09 PM
Hi,
I updated the Northwind example for Spring.Data.NHibernate to reflect your usecase. Please have a look into this sample for a working example of loading/modifying entities via hibernate and keeping them in the session.
The important steps are:
- configure "OpenSessionInViewModule"
- wrap your modifiying hibernate operations in a transaction
cheers,
Erich
Ismar Slomic
06-02-2007, 10:21 AM
Hi Erich!
I just want to thank you for creating such a example. It really helped me out, and showed me new ways of solving the CRUD with spring, nhibernate and .NET. I think tutorial can be very usefull for other users as well.
Congratulate with release of the M1, you guys do really good job! Thank you for that!
Regards Ismar
Hi,
I updated the Northwind example for Spring.Data.NHibernate to reflect your usecase. Please have a look into this sample for a working example of loading/modifying entities via hibernate and keeping them in the session.
The important steps are:
- configure "OpenSessionInViewModule"
- wrap your modifiying hibernate operations in a transaction
cheers,
Erich
Ismar Slomic
06-14-2007, 03:41 PM
Hi Erich
I discovered while debuging the Spring.Data.NHibernate.Northwind example that when the Customer is loaded in the CustomerEditor.cs, Orders collection got this exception:
{"Failed to lazily initialize a collection - no session"}
NHibernate.HibernateException {NHibernate.LazyInitializationException}
And i got the same exception for my one-to-many mappings. Could you help me out?
steinard
06-15-2007, 07:42 PM
Hi!
Hvordan er været i Oslo?
Basically your session is closed when you are accessing the property and the exception is thrown. If you are using OSIV then you should open a new session and invoke the property or change the mapping indicating that the property should be fetched eagerly the first time. This should work out well.
Cheers,
Steinar.
Some thoughts on this (http://forum.springframework.net/showthread.php?p=7499#post7499) exception.
Ismar Slomic
06-16-2007, 12:01 AM
Hi Steinar!
Fint vær i Oslo, men ikke like fint som for par dager siden ;)
Im aware of that this exception is caused of the lazy loading of the collection. But since I use OSIV, shouldn't the session management (opening/closing) in the presentation layer be automatically handled? I did read you post about this theme, and it seems that there is no elegant way of handling this kind of issue. So, do you suggest that I open a new session in the presentation layer when I want to recieve collection from one Domain Object, and close it after its recieved?
I didnt catch up what u ment with changing the mapping indicating that the property should be fetched eagerly the first time. Could you please give a example?
Thanks for your help! ;)
Regards Ismar
steinard
06-16-2007, 09:18 PM
Hi!
As far as I know the session cannot be automatically reopened at the presentation layer. You would have to make a network trip to your middleware server and there you must open a new session. If you use the OSIV-module, then OSIV should do this for you (either bound to your request/response or wrapping a facade method as shown here (http://forum.springframework.net/showthread.php?p=6285#post6285)). Even though I currently make winforms, the concept should be the same for web applications as well.
So when you are at the client side in the presentation layer in a three tired client-server application, then you must catch the exception somehow, send the domain object to the middleware. There OSIV will provide you with a session so it can be reattached and populate the lazy property. In terms of performance, this could be bad. Therefore I try to identify how much data I really need to satisfy the usecase, and try to find a balance/tradeoff between when to fetch eagerly and when to use lazy loading.
When it comes to handling lazy exceptions elegantly at the presentation side of the network boundary, I've been thinking of some sort of ExceptionManager (that will encapsulate and hold all logic needed to go back to the middleware and initialize the lazy property), but I haven't had any time to put it into code. The manager could be injected through spring DI or use strategy pattern together with a custom factory. I haven't had time to think this through yet.
In a web application this problem should be simpler, because you only have code executing on the server side. Thus, if OSIV works as expected, a new session should have been opened for you just after IIS starts processing your request and closed just before your html document is sent back to the client. You are making a web application, aren't you? (When it comes to making web applications I've only done this in Java with Tapestry and Spring using OSIV with Tomcat under the hood, and it worked great ;).
When it comes to modifying the mappings, then you could just insert laxy=false in either you .hbm.xml file or in the annotations like I do in my code:
[Set(0, Lazy = false, Cascade = CascadeStyle.SaveUpdate)]
[Key(1, Column = "PriceProfileId", ForeignKey = "FK_NAME")]
[OneToMany(2, ClassType = typeof(PriceProfileData))]
public virtual ICollection<IPriceProfileData> PriceProfileData
{
get { return priceProfileData; }
set { priceProfileData = value; }
}
I hope you solve your problem soon.
Cheers,
Steinar.
Ismar Slomic
06-17-2007, 11:15 PM
Hi again!
Thanks for your response. Yes I'm working on a web app, and are using OSIV. First i didnt managed how to config spring to use OSIV, but after Erich created Northwind example it was more clear to me. And i thought that the session issue was solved, but naah. I know that turning OFF the lazy loading of the collections can solve my problem. This can be a good solution (like you self pointed) in some cases. But I believe that OSIV can help solving the session issue with lazy loading, but don't know how. So I hope some of the Spring contributors can helping me on this one.
Regards
Ismar
steinard
06-19-2007, 08:57 AM
Hi!
I haven't yet tested the Northwind web example, but I'll check it out this evening. I know that some of the services we have created should also have web interfaces. So this could be a nice way to prepare for that! But first I have to check out the ActiveMQ integration.
The only issue OSIV solves for you is automatically provide you with a session when you're within a boundary (often the middleware server), and the OSIV module will also control the life of this session, and close it when you leave that boundary. You will get the lazy exception when you invoke a property outside of the controlled session scope/boundary. Using a copy-constructor will get you around this problem because you will throw away the generated proxy that can throw this exception. I guess there is a downside by using copy-constructors this way (I believe I got duplicates when save operations cascaded), I'm not sure because I stick with the proxy these days.
Hope this clears up what OSIV will do for you.
Cheers,
Steinar.
Ismar Slomic
06-21-2007, 08:40 PM
I am still waiting for any suggestion from the Spring contributors on how to solve this issue. Could Mark og Erich please point me to the right direction to solve this.
Tnx in advance
Ismar
Erich Eichinger
06-22-2007, 01:05 PM
Hi Ismar,
sorry for the late response. I'm currently on vacation ;-)
I expect you followed the northwind sample by specifiying OSIV in <httpModules>.
What I'm missing: Could you please post a complete stacktrace for your error? If you're using OSIV, there should be a session available even during rendering the page+controls. Do you maybe spawn some thread?
tx,
Erich
Ismar Slomic
06-23-2007, 10:34 AM
Hi Erich!
Sorry for bothering you in your vacationperiod. But I really find it hard completing the project without solving this issue. So thanks for helping! :)
The lazy loading problem is not only in my project, but also in the NorthWind example. What i tried to do is this to add this to CustomerEditor.cs:
override protected void InitializeDataBindings()
{
base.InitializeDataBindings();
IList allOrders = CurrentCustomer.Orders;
orders.DataSource = allOrders;
orders.DataTextField = "Id";
orders.DataValueField = "Id";
}
And this to CustomerEditor.aspx:
<asp:DropDownList runat="server" ID="orders"/>
I dont get any exception when I open the page in the browser, but nothing is filled in the dropdownlist. When i debug the page to se what allOrders contains, I find this:
base
"Failed to lazily initialize a collection - no session"
System.ApplicationException {NHibernate.LazyInitializationException}
StackTrace
"at NHibernate.Collection.AbstractPersistentCollection .Initialize(Boolean writing)
at NHibernate.Collection.AbstractPersistentCollection .Read()
at NHibernate.Collection.PersistentBag.get_Count()
at NHibernate.DebugHelpers.CollectionProxy.get_Items( )"
Easiest way to help me could be if you could add this extension to existing Northwind example and get it working.
Tnx again for your help!
Ismar
Bruno Baia
06-25-2007, 12:57 AM
Hi,
Maybe you are facing the same problem you got here (http://forum.springframework.net/showthread.php?t=2708).
Erich's answer (http://forum.springframework.net/showpost.php?p=7143&postcount=8) should be the solution.
Bruno
Ismar Slomic
06-25-2007, 07:02 AM
Hi Bruno!
Copy constructor is one solution that I can use,but the Northwind example Erich made doesn't use copy constructors for getting one specific Person. So my original post (http://forum.springframework.net/showpost.php?p=7143&postcount=8) where i explain the lazy loading issue is actually solved with use of OSIV. Or do i take wrong here?
/Ismar
Erich Eichinger
06-25-2007, 09:45 PM
hi ismar,
I'm still missing your complete stacktrace. Plz post it here - hopefully this will allow for deeper insights.
I'm not sure, if the northwind sample exactly applies to your problem. In any way you *must* ensure, that the collection has been initialized, *before* the objectgraph is detached from the nh-session. Another solution is the ensure, that an objectgraph is reattached to a session before trying to access childcollections. I don't have the nh docs at hand so plz have a look at the nh docs (i think the session interface contains such a helper method)
tx,
Erich
Ismar Slomic
06-25-2007, 09:57 PM
Hi Erich!
Im not sure how to provide more specific stacktrace to you since i dont get any exception in the web page. I can only access the local variables when i debug the application, and i see the exception in the value column. Could you please tell me how to provide you more information?
cheers ismar
Erich Eichinger
07-03-2007, 07:55 AM
Hi Ismar,
an easy way to catch exceptions that do not occur during page processing is to implement the method Application_Error() in your global.asax. If you are using threads, you should also register for AppDomain.UnhandledException.
Here's an example of what your HttpApplication class might look like:
public class MyApp : HttpApplication
{
static private ILog Log = LogManager.GetLogger(typeof(MyApp));
static MyApp()
{
// register for unhandled exceptions
AppDomain.UnhandledException += new UnhandledExceptionEventHandler(MyHandler);
}
static void MyHandler(object sender, UnhandledExceptionEventArgs args)
{
Exception e = (Exception) args.ExceptionObject;
Log.Fatal("Unhandled error on external thread", e);
}
void Application_Error(Object sender, EventArgs e)
{
Exception lastError = Server.GetLastError();
Log.Fatal("Unhandled application error", lastError);
}
}
Having these handlers in place you should get the stacktrace we need.
cheers,
Erich
Ismar Slomic
07-03-2007, 10:28 PM
Hi Erich,
I have done what you suggested and with help of logger i tried to get more information, but the problem is that when i call collection Order for Customer
IList allOrders = CurrentCustomer.Orders;
without running in debug modus, I dont get any exception, neither on the web page or in the log. And the dropdownlist with orders in the CustomerEditor is just empty.
But if I put a breakpoint right after the line above,and then run the application in the debug modus, after expanding the "allOrders" in the tree in "Locals View", a error is written to logger txt file and i dont get more information than:
2007-07-03 23:17:46,218 [7720] ERROR NHibernate.LazyInitializationException [(null)] - Failed to lazily initialize a collection - no session
NHibernate.LazyInitializationException: Failed to lazily initialize a collection - no session
I have zipped the NorthWind project here (http://www.slomic.no/Northwind.TimeTracker.zip).
Hope this is helping you more to understand what the problem is.
Cheers Ismar
Erich Eichinger
07-04-2007, 07:28 AM
Hi Ismar,
I just had a look into your download. Unfort. there are a lot of things missing there, including .aspx / .ascx files, there is no Spring / Spring-Configuration etc.?
-Erich
Ismar Slomic
07-04-2007, 10:42 PM
Hi Erich, I did point the link to the wrong .zip. Sorry!
Here (www.slomic.no/Spring.Data.NHibernate.Northwind.zip) is the right one.
Cheers,
Ismar
Erich Eichinger
07-05-2007, 08:53 PM
Hi,
Tx for the code. There have been a couple of issues with the code but I could reproduce the problem now. Please have a look at the most current version of the Northwind sample code. I will update it during next week to make it more complete.
It's rather clear why the code fails: Before loading the collection, the object must be re-attached to the nhibernate session.
Here's a really interesting thread about dealing with lazy-loading (http://forum.springframework.net/showthread.php?t=2775). Here's another idea on the java forum (http://forum.springframework.org/showthread.php?p=29857)
As always, it depends on your needs which way to go. Personally I like the approach of jwray from the java forum to be able to specify the level of details to be fetched in one round-trip. For smaller applications Steinar's approach of catching LazyLoading exceptions and re-attaching the model to the session is most likely completely sufficient.
hope this helps,
Erich
Ismar Slomic
07-05-2007, 09:01 PM
Hi Erich!
Tnx for you help. I expected that reason for the exception was that session was closed after getting customer to the presentation layer. Then when i tried to get collection, there was no session and the lazyloading exception got thrown. But i would like you to upload a complete example of the Northwind so i can see your solution. Please let me know when you have uploaded new version by replying at this thread, or sending me a private message.
Tnx for your help!
Cheers,
Ismar
steinard
07-06-2007, 07:52 AM
Hi Erich, back from vacation?
For smaller applications Steinar's approach of catching LazyLoading exceptions and re-attaching the model to the session is most likely completely sufficient.
At the moment this is a small application, but the organization is considering converting most applications to c# (to ease maintenance and make it easier to hire more programmers) and use the framework that I've built on top of Spring.Net and NHibernate. This means that it will become a rather big application (we have lot's of custmers all across Europe).
So therefore I need to know as early as possible what challanges my approach will pose (what is unsufficient here), as I probably still have some months before the first product hits the market...
Thanks,
Steinar.
Ismar Slomic
07-12-2007, 08:14 PM
Hi Erich!
I just wonder if you have managed to have a look at the NorthWind example. If you have, is it checked in the repository?
cheers, ismar
Ismar Slomic
08-11-2007, 10:25 AM
Hi!
What is the status about the Northwind examle, or does anyone have any good solution on this issue? Maybe full example to learn from?
Cheers Ismar
Erich Eichinger
09-10-2007, 11:34 AM
Hi Ismar,
I'm sorry for pinging back that late. Until yet I didn't have time to work out the Northwind sample.
But user Steinard on this forum posted a solution for handling the lazy-loading problem by leveraging the power of AOP here (http://forum.springframework.net/showthread.php?p=8004#post8004)
cheers,
Erich
Erich Eichinger
09-29-2007, 10:50 PM
Hi,
it took a while but finally I made it. I extended the Northwind sample with a demonstration for lazy-loading support.
When looking at the sample, note the following:
- Orders are declared lazy in the .hbm file
- The new NHibernateCustomerEditController does the work of reattaching a nhibernate entity to the current nh-session each time it is requested.
- The "CustomerEditController" object declared in web.xml has "session"-scope
If you click on "View Orders" in the customer list, the customer object is stored in the NHibernateCustomerEditController and a redirect to CustomerOrders.aspx is performed - which means, that the nh-session the customer object is attached to will be closed. On the next request, a new session is created by OSIV and the CustomerOrders page retrieves the customer's orders from CustomerEditController.CurrentCustomer, which ensure, that the customer is attached to the currently active nh-session.
If you have any further questions, don't hesitate to ask!
cheers,
Erich
Markus
02-07-2008, 09:09 AM
hello everybody!
I'm sorry I have to bother you again with this topic - especially because it has already been solved, but never the less i encountered exactly the same exception yesterday, when I added Lazyloading support via the OpenSessionInViewModule.
Then I get the Spring.Dao.InvalidDataAccessApiUsageException
Write operations are not allowed in read-only mode (FlushMode.NEVER) - turn your Session into FlushMode.AUTO or remove 'readOnly' marker from transaction definition
First I went through the whole thread and then I debuged through the Northwind sample. As I'm not as much into Spring as you guys are, the CustomerEditController thing sounded kinda weird to me. I'm not a 100% sure i I got the idea of having a NHibernateController in the weblayer right.
I cannot live without lazy loading in my project, as instantiation of all childelements at once is very expensive.
Back to my question: for a quickfix i added the following code to my save/update methods:
this.Session.FlushMode = NHibernate.FlushMode.Auto;
this.HibernateTemplate.Update(_object);
this.HibernateTemplate.Flush();
// should I set the Session.FlushMode back to something else? What is the default flushmode here?
At the moment everything works fine, but I can't let got the feeling that I miss some basic concepts - as this seems very simple too me, compared to the approach you guys have suggested.
Could you please explain drawbacks of this approach and if I might run into serious trouble if I continue using this approach?
steinard
02-10-2008, 01:27 PM
Hi Markus!
As far as I can see this is not a lazy-loading issue. It's a FlushMode issue. A readonly session will have FlushMode.Never. If you use OSIV to open a session and later open a transaction within that session scope, and within that transaction-scope you try to save or update, then you will get the exception Spring.Dao.InvalidDataAccessApiUsageException.
The LazyInitializationException is thrown when you load an entity with lazy properties and you outside of the session-scope accesses one of these properties. The entity will have a reference to it's original session and try to load the requested data. If the session is dead then the exception is raised. The standard way of solving this is to reattach the entity to a new session so that the needed data can be loaded. This is termed as working with detached entities. Another way of solving the issue is to always load all the data you need before leaving the session scope. This is not lazy-loading though.
When it comes to your quickfix then it fixes your FlushMode exception because you modify your FlushMode setting. This quickfix would not fix any lazy-loading problems though.
I hope things are clearer now,
cheers Steinar.
Markus
02-11-2008, 07:20 AM
Steinard,
Thx a lot for your answer. The next time I worked on the code again, I realized the actual problem. For now it's clear to me that I missinterpreted the problem as a FlushMode/SavingUpdate issue.
Now it is clear to me what you guys are talking about.
I guess the reason why a session can't be kept in the SessionStore (knowing that a NHibernate session has nothing to do with a the SessionStore :-)) is that they would conflict with Nhibernate Sessions of other users, right? That's why we need to reattach it?!
Unfortunately time is running out for these things, and I (must) consider loading depths (what should be loaded, what children etc. etc.) as a usecase issue in my businesslogic - for example "BL.LoadChildren(string _id);" as suggested in the Java Forum.
Nevertheless I'll keep an eye on this topic, hoping for a simple and clean reattach mechanism.
regards!
Markus
steinard
02-12-2008, 11:47 AM
Hi again!
I guess the reason why a session can't be kept in the SessionStore (knowing that a NHibernate session has nothing to do with a the SessionStore :-)) is that they would conflict with Nhibernate Sessions of other users, right? That's why we need to reattach it?!
Yes, or at least potentially conflict if they update the same data. For long-running-sessions, the chance of someone updating the same entities as you have loaded into your session cache increases the longer the session lives. When this happens, your session will not hold any information about what other sessions has done to these entities. Thus there is an increasing chance of running into StaleDataExceptions. To overcome the problem of stale data one must evict the stale entities and reload them into the session (a big overhead if you ask me). Since the session is alive, the entities has never become detached and thus there is no need to reattach them either.
We can reduce this problem by working with sessions that have a shorter life span. Usually this involves working with the entities after the session that loaded them has been closed. In this case, the entities has become detached and must be reattached if they are to participate in hibernates persistence context later. Even detached objects may become stale, this can be checked for while reattaching them to a new session by using
session.Lock(entity, LockMode.Read);
This will force NHibernate to do a version check and throw StaleDataException if the entity has been modified (become stale). If you have made modifications to the entity, then no reattachment or version check is needed, just update it straight away:
session.Update(entity);
You should (or I should as well) create a unit test and do the following:
- Create two sessions, session A and B.
- Let session A and B load the same entity.
- Close session A and B.
- Create two new session, session C and D.
- Now modify entity loaded by session B, use session D to update, flush and close session.
- Then do a similar modification on object loaded by session A. Use session C to update and flush the changes. Observe if StaleDataExceptions can be thrown in these cases, I'm unsure...
But sill, if you ever get StaleDataExceptions, then you need to evict the entity and reload it into the session. You can use session.Find(..) as it guarantees that no stale data will be returned.
Unfortunately time is running out for these things, and I (must) consider loading depths (what should be loaded, what children etc. etc.) as a usecase issue in my businesslogic - for example "BL.LoadChildren(string _id);" as suggested in the Java Forum.
I've discussed with Erich E. on this forum the possibility to use a LazyInitializer that can load your object graph to depth n. This means that one can get rid of the many queries to fetch the same entities with varying object graph depth. Of course, if you have very high performance requirements then a good HQL query will be the best solution, but at least for those applications I write, then there is a varying demand for performance and thus using the LazyInitializer is a nice way to reduce querying logic. Another thing you could do is to invest some time in defining your default fetch plan. Specifying the eager/laziness could save you a lot of code and work as the default fetch plan should serve the most common case.
Cheers,
Steinar.
Powered by vBulletin® Version 4.1.5 Copyright © 2012 vBulletin Solutions, Inc. All rights reserved.