4 Replies Latest reply on Jun 18, 2009 3:19 AM by kragoth

    Hibernate first level cache is destroing my life

    jean.baldessar
      Hi.

      I'Will try to show my problem in a pratical way becouse its very hard to explain.

      I have this code:


      em = getEntityManager(); //assume this work
      List<UserEntity> a = em.createQuery("from UserEntity as user where user.id=1").getResultList();
      List<UserEntity> b = em.createQuery("from UserEntity as user left outer join fecth user.stuff where user.id=1").getResultList();


      stuff is a manyToOne assotiation with lazy fetch.

      in the first query the stuff is not fetched, but in the socond it might be. But de Hibernate cache gets the lazy object that was been "fetched" in the first query.

      if I put a em.clear(); between the queryes it works fine.


      em = getEntityManager(); //assume this work
      List<UserEntity> a = em.createQuery("from UserEntity as user where user.id=1").getResultList();
      em.clear();
      List<UserEntity> b = em.createQuery("from UserEntity as user left outer join fecth user.stuff where user.id=1").getResultList();


      Can I tell to hibernate do the fetch with a real object, and not a lazy proxy from hell??





        • 1. Re: Hibernate first level cache is destroing my life
          qwertywin

          set the manyToOne fetch type to eager


          @


          ManyToOne(fetch = FetchType.EAGER)





          if you are working with it in the same transaction then it doesnt matter, unless you are trying to do some sort of object equivalence..


          e.g.


          also you might want to do this instead




          UserEntity user = em.createQuery("from UserEntity as user where user.id=1").getSingleResult();
          
          Stuff stuff = user.getStuff();
          


          at which point hibernate will go and snatch the lazy proxy anyway.






          • 2. Re: Hibernate first level cache is destroing my life
            amitev

            Try marking the first query as read-only.

            • 3. Re: Hibernate first level cache is destroing my life
              swd847

              I have been in this situation before. The trick is to use bytecode enhancement instead of proxies. First you need to instrument your code:


              <target name="instrument" depends="compile">
                        <taskdef name="instrument" classname="org.hibernate.tool.instrument.javassist.InstrumentTask">
                             <classpath>
                                  <fileset dir="lib">
                                       <include name="*.jar" />
                                  </fileset>
                             </classpath>
                        </taskdef>
              
                        <instrument verbose="false" >
                             <fileset dir="${jar.dir}/com/myproject/entities">
                                  <include name="*.class" />
                             </fileset>
                             
                        </instrument>
                   </target>
              
              



              Then you need to annotatate your association with @LazyToOne(NO_PROXY), and hibernate should stop giving you proxies.

              • 4. Re: Hibernate first level cache is destroing my life
                kragoth

                Hi Jean,


                We've come across this same issue in our application I'll go through what I think we did to solve it. Was a while ago :P


                Just a note on the other posts:


                First of all, making the relationship EAGER is not a good option, and never is in any larger application. It's also not really a good practice to get into. You don't want to be flooding your DB with queries and loading data that you don't need.


                Second, I don't think making the query read only will solve your problem. You can try but I'm pretty sure it will have no effect. Could be wrong about this, but I think we tried this and it didn't change anything.


                Third, bytecode enhancement seems like a rather large change to fix a relatively small problem.


                Ok, the way we solved the problem (at least as far as I can remember) is this.


                Using your example.



                1. Query out UserEntity without loading stuff

                2. Now when you need stuff, Query out Stuff (NOT UserEntity fetch stuff) just query Stuff where UserEntity='value' //assumes you have a back pointer

                3. Next, do a



                Hibernate.initialize(UserEntity.stuff) //This should not cause any queries to happen as your previous query loaded stuff
                



                This solution is based on the assumption that you are in the same session when both queries occur and when the call to Hibernate.initialize happens.


                If this doesn't work let me know and I'll try take another look at our codebase to see if I missed something.


                Of course alternatively you could just make sure that your two queries don't happen in the same session and then it would just work for you. But if you are using SEAM's extended persistence context then I can't help you there.