Link to home
Start Free TrialLog in
Avatar of TheFACTORY_Max
TheFACTORY_MaxFlag for South Africa

asked on

Hibernate 'unsaved transient instance' error on stress

Hello Experts,

I have a problem with my application (which uses hibernate, spring MVC and struts) which only happens when I stress the application.

When I test the application with one user it works flawlessly, however I have started stress testing using JMeter which can simulate as many simultaneous visits as you want, while trying this with 10+ simultaneous visits at the time it gives an hibernate error every now and then.
The error given on the page is the following:

javax.servlet.ServletException: org.springframework.dao.InvalidDataAccessApiUsageException: object references an unsaved transient instance - save the transient instance before flushing: com.GolfOnline.Beans.Course; nested exception is org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.GolfOnline.Beans.Course

The stack gives this as error:
object references an unsaved transient instance - save the transient instance before flushing: com.GolfOnline.Beans.Course

I am using the getHibernateTemplate helper class for the hibernate requests

Firstly I am not exactly sure why it gives me this error, I think it's because in one request I retrieve a 'Course' object and in this same request I use this to retrieve a 'GolferType' object using an association to a 'Course' object. With many simultaneous requests at the same time the first retrieved 'Course' object becomes detached for some reason and can't be used again in a query then??

Could you advise me on which way to go in order to solve the problem?
All relevant code is below and I have prepared a setup to reproduce the problem at:
http://www.maxvandenboom.nl/testprogram.html, this holds the example source (Netbeans project), database SQL and JMeter (and it's test plan to reproduce the error)

Any help solving this problem would be greatly appreciated!!!
Max
------------------------------------------------------- applicationContext.xml
 
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
    
    <!-- Hibernate (persistence) -->
    
    <bean id="golfOnlineDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
         --censored--
</bean>
 
    <bean id="golfOnlineSessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="dataSource" ref="golfOnlineDataSource"/>
        <property name="mappingResources">
            <list>
                <value>com/GolfOnline/Hibernate/Course.hbm.xml</value>
                <value>com/GolfOnline/Hibernate/GolferType.hbm.xml</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.current_session_context_class">thread</prop>
                <prop key="hibernate.connection.pool_size">0</prop>
                <prop key="hibernate.dbcp.maxActive">15</prop>
                <prop key="hibernate.dbcp.maxIdle">5</prop>
                <prop key="hibernate.dbcp.maxWait">120000</prop>
                <prop key="hibernate.dbcp.whenExhaustedAction">1</prop>
                <prop key="hibernate.dbcp.testOnBorrow">true</prop>
                <prop key="hibernate.dbcp.testOnReturn">true</prop>
                <prop key="hibernate.dbcp.validationQuery">--censored--</prop>
                <prop key="hibernate.dbcp.ps.maxActive">0</prop>
                <prop key="hibernate.dbcp.ps.maxIdle">0</prop>
                <prop key="hibernate.dbcp.ps.maxWait">-1</prop>
                <prop key="hibernate.dbcp.ps.whenExhaustedAction">2</prop>
            </props>
        </property>
    </bean>
    
    <bean id="golfOnlineTransactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory">
            <ref local="golfOnlineSessionFactory"/>
        </property>
    </bean>
    
    <!-- Service objects -->
    <bean id="courseService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">	
        <property name="transactionManager"><ref local="golfOnlineTransactionManager"/></property>
        <property name="target"><ref local="courseServiceTarget"/></property>
        <property name="transactionAttributes">
            <props>
                <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
                <prop key="search*">PROPAGATION_REQUIRED,readOnly</prop>
                <prop key="check*">PROPAGATION_REQUIRED,readOnly</prop>
                <prop key="add*">PROPAGATION_REQUIRED</prop>
                <prop key="update*">PROPAGATION_REQUIRED</prop>
                <prop key="delete*">PROPAGATION_REQUIRED</prop>
            </props>
        </property>
    </bean>
    
    <!-- Service target objects -->
    <bean id="courseServiceTarget" class="com.GolfOnline.Services.CourseServiceImpl">
        <property name="courseDAO"><ref local="courseDAO"/></property>
        <property name="golferTypeDAO"><ref local="golferTypeDAO"/></property>
    </bean>
    
    <!-- DAO objects -->
    <bean id="courseDAO" class="com.GolfOnline.DAOs.CourseDAOImpl">
        <property name="sessionFactory" ref="golfOnlineSessionFactory"/>
    </bean>
    <bean id="golferTypeDAO" class="com.GolfOnline.DAOs.GolferTypeDAOImpl">
        <property name="sessionFactory" ref="golfOnlineSessionFactory"/>
    </bean>
    
</beans>
 
------------------------------------------------------- course.hbm
 
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
  <class name="com.GolfOnline.Beans.Course" table="tbl_Course">
    <id column="cr_ID" name="id" type="short">
      <generator class="native"/>
    </id>
    <property column="cr_Name" name="name"/>
  </class>
</hibernate-mapping>
 
------------------------------------------------------- golfertype.hbm
 
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
  <class name="com.GolfOnline.Beans.GolferType" table="tbl_GolferType">
    <id column="gt_ID" name="id" type="short">
      <generator class="native"/>
    </id>
    <many-to-one class="com.GolfOnline.Beans.Course" column="gt_Course_cr_ID" name="course"/>
    <property column="gt_Title" name="title"/>
  </class>
</hibernate-mapping>

Open in new window

Avatar of ChristoferDutz
ChristoferDutz
Flag of Germany image

This error generally means, that you are trying to save an Instance which has related data which has not yet been persisted.
Are you sure that all courses are persisted before persisting the course?
One szenario could be that in your stresstesting, you are adding courses very fast and are adding the courses to a collection manually. This way the Course could be available before Hibernate has persisted the data and the id ist therefore null which too will cause this exception. You have to keep in mind that the pesist methods of the session persist the objects aysnchronously.
Avatar of TheFACTORY_Max

ASKER

ChristoferDutz, thanks for your reply.

I am aware of what the error is saying, however I am retrieving a course from the database using an ID (so the course is persistent even for hibernate since it is just retrieved by hibernate), then in the same call I use that course object to retrieve the golfertypes of the course also using hibernate and thus the (I supposed) still persistent object. All goes well with a couple of calls, but with more it gives errors.

I am only retrieving, not adding courses so I'm afraid that that scenario is not the case.
You are not eagerly fetching youre relates items, are you?
No, I am not using hibernate lazy fetching since I want to determine when to retrieve related items in the code depending on the situation.
So you ARE using lazy fetching.

Could you please post your code-part that is dealing with this?
Sorry that was not clear, what I was trying to say was that I am not using the hibernate lazy fetching SETTING in hibernate (hibernate config is posted in the code snippet above). Which thus defaults to lazy=true (so indeed I am using lazy fetching, not eagerly fetching).

The courseDAO uses this code to get the course:

        //return (Golfer)getHibernateTemplate().get(Golfer.class, id);
        ArrayList arraylist = (ArrayList)getHibernateTemplate().find(   "FROM Course as c " +
                                                                        "WHERE c.id = ?", id);
        if (arraylist.size()==0)   return null;
        Course course = (Course) arraylist.get(0);


And the golfertypeDAO uses this code to retrieve the golfertypes by course:

        ArrayList results = (ArrayList)getHibernateTemplate().find("from GolferType where course = ? and title='Guest'", course);
        if (results.size()==0) return null;
        return (GolferType)results.get(0);
SOLUTION
Avatar of ChristoferDutz
ChristoferDutz
Flag of Germany image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Again thanks for your suggestion, it is true what you say, it uses the course object from the first DAO to retrieve one GolferType in the second DAO.
So with the retrieved course object I am only retrieving one GolferType not multiple (I was meaning to correct that in my previous comment but there is no edit button after posting it).

Yes I am using the HibernateTemplate, but I don't think there are different sessions, since when the application is not being stressed that much, it does not have a problem with it, which would mean it is in one session at that moment and not detachted.

I have already tried the solution you gave before I asked the question and I know it works but what I am trying to solve is the problem of the object detaching for some reason. This solution is a workaround while I suspect it is a problem that will occur on more places in the application, so I need to know the solution and why it solves the problem not just a work around.
Then you are propably having caching problems. As long as the Bean is still in Hibernates Cache, the thing might work great, but as soon as is is flushed from the Cache, you get the Errors.

If you turn off caching entirely and you get problems all the time, then this was propably the cause and for sake of stability you should adjust your query.

Well I'd suggest you try the query modification. As this is propably what you were looking for .. it should work.
I turned of the cache entirely using:
                <prop key="hibernate.cache.use_second_level_cache">false</prop>
                <prop key="hibernate.cache.enabled">false</prop>
the startup also confirms with:
       INFO: Second-level cache: disabled
But unfortunatly  the problem still exists, it does not give the error at low stress, but it does on higher stress.

The query modification does work, I knew this already but it is still a work-around and that is not what I am looking for :)

Do you have any other ideas?
Since you are using the course only for selecting and the object itself has no impact on the result of the query, there is no difference and I wouldn't call it a work-around.
If you turn on SQL logging, you might even see that the Query executed on the DB will actually use the id of the course for selecting. So you are just doing what hibernate does for you otherwise.
It shows the SQL and I know it has the same effect.
The problem is that something does not work like it is supposed to, and getting the same thing done using a solution that you are forced to because it is not working like it is supposed is, I think, called a work-around.

Using the ID would be sufficient to solve this situation but does not solve future occurences of problems caused by the same thing as the problem in my question, that is why I need a solution to the problem not another way to reach the same goal.
Well the template technique opens a session performs the action and closes the session after this. (As far as I know). So if you are having problems, I'd suggest not closing the session immediately. I usually use a design-pattern called OpenSessionInViewFilter.

If you use the approach you are using, then you have to make sure the Object is not detached, which I guess happens as soon as you get high load.
As far as I know the HibernateTemplate (http://static.springframework.org/spring/docs/2.0.x/api/org/springframework/orm/hibernate/HibernateTemplate.html) uses .getSession (which comes from HibernateDaoSupport) with which it creates a session if there it is not one or uses the existing session if there is one.

So this must already be the case, how else can it, when it is less stressed, still use the retrieved course object from the first DAO in the second DAO while it opens and closes the first session (in the first DAO)? That should not be possible if what you are saying is true.

With this, I think I am actually using the 'session-per-request' or 'Open Session in View' pattern already (don't know the exact difference between the two), both bind a session to thread which is used during the entire request and closed when the response is done, right?
It would be really dangerous to have only one session working for the entire Application, so I doubt that this is the case. If it were, any error - no matter how small - the session is closed and it would be for the entire application. After having a look at the link I think the following is happening.

As you seem to be using JPA and Spring the spring context usually takes care of the context and the sessions assigned to it. I have to admit that I was having similar problems too a few months ago. The problem was definitively related to the Sessions. Don't ask me why you get the problems under heavy load, but that's the price we semm to have to pay when abstracting from the basics too much. If I had my hands on the real code, I would propably run the application in my Memory-Analyzer-Logging-Tool and simply record what is happening in my VM, cross check with the Hibernate sources and narrow down the problem ... but in this case I simply reloaded the Object first and hereby made sure it's not detached. In your simple case I would stick to the workaround, since it relieves us from one select-statement to the DB.
Thanks for the reply. I don't think it holds one session for the entire application, just per request I think, else I already would have noticed that while testing (and what you are saying on small errors).

I thought I was using Hibernate transactions not JPA. I am using the org.springframework.orm.hibernate3.HibernateTransactionManager instead of the JpaTransactionManager.

I ran the application with a memory-analyzer-logging-tool, YourKit Java Profiler, but this only narrows it down to what is also visible in the stack-trace, which pretty much gives me the same information as the exceptions outputted. What was the problem eventually when you narrowed it down using your tool?

A possible solution is indeed reloading the object (using .lock) but that again does not explain why it is detached in the first place, which is what I am trying to get a solution to. I agree it is something session related, but you would have no idea how?
Well while profiling, I could actually see the content of the Hibernate wrapper-objects. I had quite some problems looking at most of the Hibernate objects in Eclipse. For example if you use lazy collections it was quite problematic to see the Hibernate control-structures ... I guess this is an Eclipse bug, but doing a memory trace (not a threadstack-trace) helped quite a lot.

I used the JProfiler evaluation licence and it helped me quite a lot.
I tried Jprofiler with it's 10 day license, also a nice tool btw, but it leads to some internals of Hibernate which are hocus pocus to me.

This is actually not the first time for this question but EE advised me to repost it, since EE is going to delete that question in a couple of days I just want to quickly refer to it here:
https://www.experts-exchange.com/questions/24270970/On-simultaneous-request-object-references-an-unsaved-transient-instance-error-problem.html
(which has gives the 'ID in the query' workaround in my initial question)

Hopefully I will still get a solution in this one, or maybe ask hibernate themselves.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Mick Barry
> With this, I think I am actually using the 'session-per-request' or 'Open Session in View' pattern already (don't know the exact difference between the two), both bind a session to thread which is used during the entire request and closed when the response is done, right?

are you sure you are using open session in view?

ChristoferDutz, I think I understand what you are saying but would have no idea how to realize such a thing. Anyway I expected Hibernate to take care of these things. If it is needed to implement all of that and give it that much follow up care I might as wel use a different decent persistence layer alternative or even write one myself. Thank you for the suggestion still !

Objects, I don't think I am consciencly using that pattern since I have not implemented it as I have seen it in some samples using the spring framework. Do you think that would solve it?

(it's quite annoying that the Hibernate site has been down for maintenance, I thought I had read a good article there about the different patterns to use in what situation)
Well I had quite similar Problems in a project recently.

Hibernate is intended to hide the complexity of Object to Relational-Data persistence from us. This is a pretty complex thing. Complex things are mostly rather cost-intensive. In order to provide a decent performance. Hibernate uses lazy-loading, lazy-saving, caching etc. It's a rather general purpose thing. The problem with general purpose things is, that sometimes you have to invest quite some time and effort to making it work in special situations (such as High Load with high Concurrency). I would have really liked to make Hibernate do some sort of "synchronousUpdate" which executes the operation fully and returnes when finished. Unfortunately I couln't find anything like this.

If your data is not that complicated and you really want the high concurrency and high load, then I would recomend using plain old JDBC and thereby skip all the abstractions of Hibernate.
>  Do you think that would solve it?

sounds like it could as it would mean the same session is used for the entire request

> it's quite annoying that the Hibernate site has been down for maintenance

is it still down :(  been down for ages, someone must have broken something big

ChristoferDutz, yes I suppose there is a limit to every technique used in case of high concurrency and high load. I just thought/hoped I was doing something wrong with hibernate causing this, guess the work-around would be the easiest way to go, if I am indeed doing things right and it would not be because of the pattern implemented( or rather lack of a pattern being implemented).

Objects, but as far as I understand it, the 'open session in view' is implemented so the session does not get lost inbetween requests while going from a control to a view routine in a MVC architecture. However the error I am getting occures within the same request, even right after retrieving the 'course' object..
have you verified that the same session *is* being used?

As far as I understood the "open session in view" pattern, It adresses the fact that for dosplaying one request sometimes multiple hibernate operations are needed. Therfore in this pattern at first a check is done to see if a session is saved in the current request-session-scope. If not a new one is generated and attached. This way all operations needed for one request are handled in the same session.

A Servlet Filter is added to the configuration which automatically closes the current session after the rendering of the response is finished.

It's a quite handy pattern, but I doubt that it helps with concurrency problems of multiple requests. since every request has his own session.
Objects, I have tried but don't know which property to check on wether it is the same session, I can retrieve the Session object (using HibernateDaoSupport.getSession). Any suggestion which property to use to verify?

ChristoferDutz, I found the following about 'open session in view' as spring understands it, which is basically "keeping the hibernate session alive until the view is rendered" because else when item(s) related to the initial item are loaded, these would be set to null while rendering the view.
http://springtips.blogspot.com/2007/07/open-session-in-view.html

Is it possible that the setting hibernate.current_session_context_class which is set to thread has anything to do with it?
Well your definition is exactly the one I used, just with different words ;-) "View" is the HTML Response. You could check the Session Objects by simply adding a

System.out.println(sessionObject);

Just check if the ID's are the same. Just add the System out wherever you use the session. It's no guarantee, that two objects with different Ids might not wrap the same session, but if the id is the same you are certain that it's the same session.
Yes I tried that but all I get back is:

SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[]])

Currently using:
System.out.println(this.getSession());

Where 'this.' refers to the HibernateDaoSupport object which my DAO object extends. As you see no ID's much to my surprise as well.
Oh well .. guess they implemented the "toString()" method ... how unfortunate :-(
How about an Eclipse breakpoint ... you should see the object id in the variable view.
I'm using Netbeans, just managed to set the breakpoint and take a look at the variables.
I could not find any id on the Session object however there was a property called 'timestamp', which changes with every request and has the same value on different DAO calls within the same request.

According to this 'timestamp' value in the session object it is the same session each request.
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
... And I got a system speedup of about 400% ... ok ... and a Memory-Increase of about 500MB, but who cares ... memory is cheap and certainly worth my sanity ;-)
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Haha, also a nice way of solving it.
Unfortunatly the deadline does not allow such a rebuild, guess I might reconsider when the concurrency actually becomes a problem and use the work-around for now (I even doubt if it will reach the concurrency level which gives these problems). Although that speedup of 400% sounds very good :)

Also an interesting link, kind of confirms it.

I will accept some as solution now, thanks for all the help