1 2 Previous Next 20 Replies Latest reply on Oct 20, 2005 6:27 PM by Emmanuel Bernard

    Query performane & CascadeType.PERSIST

    J-C jc Apprentice

      Execution time may vary for the same query from something close to 0 to a few seconds accroding to the context (ie persistent object retrieves previously). Is it a bug ?

      Following a sample of code to reprodroduce that problem:

      @Entity()
      public class Protocol {
      ...
      @OneToMany(cascade = CascadeType.PERSIST)
      @JoinTable(table = @Table(name = "Protocol_Param"), joinColumns = { @JoinColumn(name = "relation_id") }, inverseJoinColumns = @JoinColumn(name = "position_id"))
       public List<Parameter> getParameters() {
       return parameters;
       }
      ...
      }
      @Entity()
      public class Parameter {
      ...
      }
      

      I create a Protocol containg 500 parameters. Its id is 1. Then I execute following code using a stateless session bean:
       @PersistenceContext(unitName = "myDBTest")
       private EntityManager manager;
       @TransactionAttribute(TransactionAttributeType.REQUIRED)
       public void sample() {
       long l0 = System.currentTimeMillis();
       Protocol p = manager.find(Protocol.class, 1);
       // ((InjectedEntityManager)manager).getHibernateSession().evict(relation);
       long l11 = System.currentTimeMillis();
       Query query = manager.createQuery("SELECT count(t) FROM Taxon t");
       System.out.println(query.getSingleResult());
       long l2 = System.currentTimeMillis();
       System.out.println("Find = " + (l11 - l0) / 1000.0);
       System.out.println("Query = " + (l2 - l11) / 1000.0);
       System.out.println("Total = " + (l2 - l0) / 1000.0);
       }
      

      -I invoke the session bean several time. Find is very quick (around 0.0xs), query s quite long (around 0.5s).
      -If I evict from the session the object return by the find, query becomes very fast (around 0.0xs).
      -If I change the mapping in the Protocol for the OneToMay relationship (I remove the cascade = CascadeType.PERSIST part of the annotation), query becomes very fast (around 0.0xs).
      - If I comment the find method, query becomes very fast (around 0.0xs).

      I have reproduce that with several kind of classes with a List containing a lot of objects and with CascadeType.PERSIST. In that case, no objects are persisted, they are just retrieved form DB. Moreover the fetch for the list is LAZY, consequently the size of the list should have no impact of the performance. But in that case it has a great impact (not on the find execution but on the query ???).
      It looks like a bug in some cache.
      This sample may look stupid, it is just something that I isolate from a real application.
      The same impact on the execution time of the query may be seen, if a lot of object are persisted in the same JTA transaction.

      I really suspect a bug in hibernate cache. I use JBoss4.0.3rc2, EJB3rc1 and mysql.

      I can use workaround such as not use CascadeType.PERSISTENT in my collection, but it makes thinks harder to implement.
      I can also evict objects from the session, but it is just a hook specific to hibernate (it is ,ot ejb3). Moreover it will make the source code harder to implmenent and not very reusable.

      Has someone already see that problem ? Is it a bug ?
      Due to that kind of bug my application has very poor performance even if each query execute separatly are fast.

      Regards





        • 1. Re: Query performane & CascadeType.PERSIST
          Emmanuel Bernard Master

          It's not a bug in Hibernate cache but I fixed this in CVS.

          • 2. Re: Query performane & CascadeType.PERSIST
            J-C jc Apprentice

            Do you have an idea when this patch will be included in an hibernate version ?

            I'm currently trying to evaluate/compare two kinds of architecture for an application (EJB2/JDO vs EJB3). None and the workaround I mentionned in the first post are acceptable for a large application. Consequently, for the moment, I have very poor performance in EJB3 compare to JDO. I suspect that with that patch, EJB3 solution will be much more effcient. Moreover, from a developper point of view, EJB3 solution is much easier to implement.

            In order that I can go further in my study, is it possible/easy to include the correction you have done in a jar file ? Which class and what should I modify ?

            Regards

            • 3. Re: Query performane & CascadeType.PERSIST
              Emmanuel Bernard Master

              You need to take Hibernate3 CVS HEAD
              Hibernate Annotations CVS HEAD
              Hibernate Entity Manager CVS HEAD
              and, if you use the JBoss EJB3 plug them in to JBoss EJB3 CVS HEAD

              It should work.

              • 4. Re: Query performane & CascadeType.PERSIST
                J-C jc Apprentice

                I try to get sources form CVS, I'm not pretty sure where to get it.

                I try:

                - hibernate-entitymanager.jar
                cvs.sourceforge.net:/cvsroot/hibernate HibernateExt

                - hibernate-annotations.jar
                cvs.sourceforge.net:/cvsroot/hibernate HibernateExt/metadata

                - hibernate3.jar
                cvs.sourceforge.net:/cvsroot/hibernate Hibernate3

                - jboss-ejb3.jar (It seems that classes org.jboss.ejb.* are missing)
                cvs.sourceforge.net:/cvsroot/jboss jboss-ejb3


                Could you please confirm if cvs adresses are correct and if something is missing ?

                Regards

                • 6. Re: Query performane & CascadeType.PERSIST
                  J-C jc Apprentice

                  In CVS, I have downloaded those jar files (class compile 09/09/2005). It fixed the problem with the List.
                  Unfortunatelly, there is exactly the same kind of problem with single reference. Following some code to reproduce it:

                  @Entity()
                  public class Test {
                   ...
                   private Ref ref;
                   @ManyToOne(cascade = CascadeType.PERSIST)
                   public Ref getRef() {
                   return ref;
                   }
                  }
                  
                  @Entity()
                  public class Ref {
                  ...
                  }
                  
                  


                  I create 5000 Test with for each one a reference to Ref. In a stateless bean I execute the following code :
                  long l0 = System.currentTimeMillis();
                   int cpt = 0;
                   for (int i = 1; i < 5000; i++) {
                   Test t = manager.find(Test.class, i);
                   }
                   long l11 = System.currentTimeMillis();
                   Query query = manager.createQuery("SELECT count(t) FROM TaxonBO t");
                   query.getSingleResult();
                   long l2 = System.currentTimeMillis();
                   System.out.println("Find = " + (l11 - l0) / 1000.0);
                   System.out.println("Query = " + (l2 - l11) / 1000.0);
                   System.out.println("Total = " + (l2 - l0) / 1000.0);
                  


                  Query takes about 1s.

                  If I just modify the previous code to evict Test instances (using hibernate API), query takes around 0.0x s.
                  If I remove the cascade type for Test->Ref query takes around 0.0x s.


                  It looks like the same problem that I had with the previous RC with List&CascadeType.PERSISTENT. For List problem has been fixed but not for simple reference.

                  In fact in my application, objects are more complex and I reproduce the problem with much fewer instance.


                  Is it a known problem ?



                  • 7. Re: Query performane & CascadeType.PERSIST
                    Emmanuel Bernard Master

                    I did the same test as you and find no significative difference between evicting the objects or not after a decent warming up time.

                    Are you sure that your conditions are fine.

                    • 8. Re: Query performane & CascadeType.PERSIST
                      J-C jc Apprentice

                      My config is:
                      - JBoss 4.0.3RC2, i use the config All + EJB3 RC1
                      - MySql 3.x
                      -Hibernate3.1beta3 (dowload from CVS: jboss\repository.jboss.com\hibernate\3.1beta3\lib
                      -Hibernate-entitymanager 3.1beta3 (download from jboss\repository.jboss.com\hibernate-entitymanager\3.1beta3)
                      -Hibernate-annotations (dowload from CVS:jboss\repository.jboss.com\hibernate-annotations\3.1beta5)

                      The query without evicting objects from the Hibernate session takes in average around 0.8 s. I change the database to use postgress, it takes 0.180 s. If I evict object or if I change the cascadeType, with the 2 DB, it takes around 0 s.

                      On wich DB did you try to reproduce that problem ?

                      • 9. Re: Query performane & CascadeType.PERSIST
                        J-C jc Apprentice

                        Just a little correction I use mySql 4.0.20

                        • 10. Re: Query performane & CascadeType.PERSIST
                          Emmanuel Bernard Master

                          MySQL, PostgreSQL, HSQLDB, but the DB has nothing to do with evict().
                          If you see some extra interaction between your DB and hibernate (show_sql) when you don't eveit, there is something wrong elsewhere in your code.

                          If there is not extra interaction, then, your test has some warm up issues

                          Here is my test, I also switch the last two tests to be fair => no changes.

                          The first test (warm up one) is of course much much longer.

                          public void testPerfCascadeAndFetchEntity() throws Exception {
                           EntityManager em = factory.createEntityManager();
                           //init data
                           em.getTransaction().begin();
                           for (int i = 0 ; i < 5000 ; i++) {
                           Troop disney = new Troop();
                           disney.setName( "Disney" );
                           Soldier mickey = new Soldier();
                           mickey.setName( "Mickey" );
                           disney.addSoldier( mickey );
                           em.persist( disney );
                           }
                           em.getTransaction().commit();
                           em.close();
                          
                           //Warm up loop
                           em = factory.createEntityManager();
                           em.getTransaction().begin();
                           for (int i = 0 ; i < 5000 ; i++) {
                           Soldier soldier = em.find( Soldier.class, new Integer(i) );
                           //( ( HibernateEntityManager ) em ).getSession().evict(soldier);
                           }
                           long l11 = System.currentTimeMillis();
                           Query query = em.createQuery("SELECT count(t) FROM Soldier t");
                           query.getSingleResult();
                           long l2 = System.currentTimeMillis();
                           System.out.println("Query1 " + (l2 - l11));
                           em.getTransaction().commit();
                           em.close();
                          
                           //do not evict
                           em = factory.createEntityManager();
                           em.getTransaction().begin();
                           for (int i = 0 ; i < 5000 ; i++) {
                           Soldier soldier = em.find( Soldier.class, new Integer(i) );
                           //( ( HibernateEntityManager ) em ).getSession().evict(soldier);
                           }
                           l11 = System.currentTimeMillis();
                           query = em.createQuery("SELECT count(t) FROM Soldier t");
                           query.getSingleResult();
                           l2 = System.currentTimeMillis();
                           System.out.println("Query " + (l2 - l11));
                           em.getTransaction().commit();
                           em.close();
                          
                           //evict
                           em = factory.createEntityManager();
                           em.getTransaction().begin();
                           for (int i = 0 ; i < 5000 ; i++) {
                           Soldier soldier = em.find( Soldier.class, new Integer(i) );
                           ( ( HibernateEntityManager ) em ).getSession().evict(soldier);
                           }
                           l11 = System.currentTimeMillis();
                           query = em.createQuery("SELECT count(t) FROM Soldier t");
                           query.getSingleResult();
                           l2 = System.currentTimeMillis();
                           System.out.println("Query " + (l2 - l11));
                           em.getTransaction().commit();
                           em.close();
                           }


                          • 11. Re: Query performane & CascadeType.PERSIST
                            J-C jc Apprentice

                            My test case is quite different. If I execute your test case there is no problem. Just change

                            for (int i = 0 ; i < 5000 ; i++) {
                             Soldier soldier = em.find( Soldier.class, new Integer(i) );
                             //( ( HibernateEntityManager ) em ).getSession().evict(soldier);
                             }
                            

                            with
                            for (int i = 0 ; i < 5000 ; i++) {
                             Troop troop = em.find( Troop.class, new Integer(i) );
                             //( ( HibernateEntityManager ) em ).getSession().evict(troop);
                            }
                            


                            Problem is that Troop->Solidier is a @OneToMany(cascade = CascadeType.PERSIST). If I remove the cascadeType or if I evict Troop instance, it works quick and fine.

                            • 12. Re: Query performane & CascadeType.PERSIST
                              Emmanuel Bernard Master

                               

                              public void testPerfCascadeAndFetchEntity() throws Exception {
                               EntityManager em = factory.createEntityManager();
                               //init data
                               em.getTransaction().begin();
                               for (int i = 0 ; i < 5000 ; i++) {
                               Troop disney = new Troop();
                               disney.setName( "Disney" );
                               Soldier mickey = new Soldier();
                               mickey.setName( "Mickey" );
                               disney.addSoldier( mickey );
                               em.persist( disney );
                               }
                               em.getTransaction().commit();
                               em.close();
                              
                               //Warm up loop
                               em = factory.createEntityManager();
                               em.getTransaction().begin();
                               for (int i = 0 ; i < 5000 ; i++) {
                               //Soldier soldier = em.find( Soldier.class, new Integer(i) );
                               Troop troop = em.find( Troop.class, new Integer(i) );
                               //( ( HibernateEntityManager ) em ).getSession().evict(soldier);
                               }
                               long l11 = System.currentTimeMillis();
                               Query query = em.createQuery("SELECT count(t) FROM Soldier t");
                               query.getSingleResult();
                               long l2 = System.currentTimeMillis();
                               System.out.println("Query1 " + (l2 - l11));
                               em.getTransaction().commit();
                               em.close();
                              
                               //do not evict
                               em = factory.createEntityManager();
                               em.getTransaction().begin();
                               for (int i = 0 ; i < 5000 ; i++) {
                               Troop troop = em.find( Troop.class, new Integer(i) );
                               ( ( HibernateEntityManager ) em ).getSession().evict(troop);
                               }
                               l11 = System.currentTimeMillis();
                               query = em.createQuery("SELECT count(t) FROM Soldier t");
                               query.getSingleResult();
                               l2 = System.currentTimeMillis();
                               System.out.println("Query " + (l2 - l11));
                               em.getTransaction().commit();
                               em.close();
                              
                               //evict
                               em = factory.createEntityManager();
                               em.getTransaction().begin();
                               for (int i = 0 ; i < 5000 ; i++) {
                               //Soldier soldier = em.find( Soldier.class, new Integer(i) );
                               Troop troop = em.find( Troop.class, new Integer(i) );
                              
                               //( ( HibernateEntityManager ) em ).getSession().evict(troop);
                               }
                               l11 = System.currentTimeMillis();
                               query = em.createQuery("SELECT count(t) FROM Soldier t");
                               query.getSingleResult();
                               l2 = System.currentTimeMillis();
                               System.out.println("Query " + (l2 - l11));
                               em.getTransaction().commit();
                               em.close();
                               }


                              On MySQL
                              0 ms
                              32 ms

                              On PostreSQL
                              15ms
                              16ms

                              All those values are quite not significant regarding the VM precision.


                              • 13. Re: Query performane & CascadeType.PERSIST
                                Emmanuel Bernard Master

                                Of course I've eliminated the first printed result (warm up)

                                • 14. Re: Query performane & CascadeType.PERSIST
                                  J-C jc Apprentice

                                  OK. I'll try to investigate that a little more.

                                  I have that kind of performance only if CascadeType for the relationship between Troop->(*)Soldier is different from PERSISTENCE.

                                  With CascadeType to persist, I get:
                                  - MySQl without evict 0.8s
                                  - MySQl with evict 0.016s

                                  - Postgres without evict 0.178s
                                  - Postgreswith evict 0.016s

                                  Another difference with you test case is that I am in a SessionBean with an EntityManager get by injection.

                                  I'll try to investigate that a little more on monday.

                                  1 2 Previous Next