Hibernate::
|
Compare performance of bag and idbag mappings.Last update: Oct - 10 -2005 Hibernate is well known for its ability to help developers to persist collections of dependent elements. As we might expect that comes at a price. Let's try to determine the cost and see if we are willing to pay it. Hibernate provides us with numerous mapping options, but lets focus on two: bag, and idbag. On the application code level the usage of bag and idbag tags is exactly the same. The differences are hidden in the Hibernate mapping files and in the database schema. Let's look at the DB schema: 1 2 CREATE TABLE bag_main_objects ( 3 id varchar(40) PRIMARY KEY, 4 name varchar(100) 5 ); 6 7 CREATE TABLE idbag_dependent_objects ( 8 id varchar(40) PRIMARY KEY, 9 main_id varchar(40), 10 val char(255) 11 ); 12 13 CREATE TABLE bag_dependent_objects ( 14 main_id varchar(40), 15 val char(255) 16 ); 17
Here we create do tables for dependent objects: table bag_dependent_object does not have a surrogate key, and table idbag_dependent_objects that has surrogate key. For simplicity we will just have values in the dependent objects table, but we as well could store keys to another table that will allow us to implements many-to-many relationships. Then we will populate those tables with 100 main object and with 20 dependent objects per main object. 27 28 public void generateTestData( Connection c ) throws SQLException{ 29 for( int main_id = 0; main_id < MAIN_OBJ_ID_LIMIT; main_id++){ 30 exec( c, "INSERT INTO bag_main_objects (id, name ) VALUES ( '"+ main_id +"','main-obj-" + main_id + "')"); 31 for( int j = 0; j < DEPENDENT_OBJ_LIMIT; j++ ){ 32 exec( c, "INSERT INTO idbag_dependent_objects(id, main_id, val) VALUES " + 33 "( 'm"+ main_id + "d"+ j +"','" + main_id + "','value of " +main_id + "d"+ j +"')"); 34 exec( c, "INSERT INTO bag_dependent_objects(main_id, val) VALUES " + 35 "('" + main_id + "','value of " +main_id + "d"+ j +"kjhdsafjhsjhdfkhgskjhdfkjhsgkdjfhgkjshdfgk asjfhdg akksddg aslkdkhfg alsidf')"); 36 } 37 } 38 } BAG mapping is the example of un-optimized collection handling, it sure looks nice in the code: we simply add, update, or delete objects from the collection and Hibernate stores our changes. However for bag Hibernate uses a rather brutal force strategy: it deletes all the relevant rows from decent objects table and then inserts all the current collection content back into the database. For example, lets assume that we have 20 dependent objects in a collection of dependent objects. If we delete just one object from the collection then Hibernate will execute 1 delete statement to delete all 20 rows, and then 19 insert statements to insert remaining 19 objects back into the database. Here is how the mapping file looks: 5 6 <hibernate-mapping package="com.sourcelabs.hibernate.bhw.bags" > 7 8 <class table="bag_main_objects" name="BagContainerObj" lazy="false" > 9 <id name="id" > 10 <generator class="uuid"/> 11 </id> 12 <property name="name" /> 13 <bag name="dependentObjects" table="bag_dependent_objects" lazy="false"> 14 <key column="main_id" /> 15 <element type="java.lang.String" column="val"/> 16 </bag> 17 </class> 18 </hibernate-mapping>
IDBAG mapping is much smarter than BAG but requires ID column in the table of dependent objects. When we change collection content or update its members Hibernate will issue only necessary SQL update statements. If we delete one object from the collection, then Hibernate will execute only one SQL delete statement. IDBAG mapping looks as the following: <hibernate-mapping package="com.sourcelabs.hibernate.bhw.bags" > 6 7 <class table="bag_main_objects" name="BagContainerObj" lazy="false" > 8 <id name="id" > 9 <generator class="uuid"/> 10 </id> 11 <property name="name" /> 12 <idbag name="dependentObjects" table="idbag_dependent_objects" lazy="false"> 13 <collection-id column="id" type="java.lang.String"> 14 <generator class="uuid"/> 15 </collection-id> 16 <key column="main_id" /> 17 <element type="java.lang.String" column="val"/> 18 </idbag> 19 </class> 20 </hibernate-mapping> Both our mappings map collection of Strings to a list field for the same object, that is right, the same object have two different mappings, they of course cannot be used simultaneously in the same session, therefore we will run our tests sequentially: public class BagContainerObj { 11 String id; 12 String name; 13 List dependentObjects; 14 15 public String getId(){ 16 return id; 17 } 18 19 public void setId( String id ){ 20 this.id = id; 21 } 22 23 public String getName(){ 24 return name; 25 } 26 27 public void setName( String name ){ 28 this.name = name; 29 } 30 31 public List getDependentObjects(){ 32 return dependentObjects; 33 } 34 35 public void setDependentObjects( List dependentObjects ){ 36 this.dependentObjects = dependentObjects; 37 } 38 } 39 Running our tests produces the expected results: IDBAG collection is approximately 5 times faster than BAG mapping. And the advantage of IDBAG becomes even more noticeable with increase in the number of objects in the collection.
run-class:
[java] 0 [main] WARN net.sf.ehcache.config.Configurator
- No configuration found. Configuring ehcache
from ehcache -failsafe.xml found in the classpath: jar:file:
/home/kosta/.m2/repository/net/sf/ehcache/ehcache
/1.1-1/ehcache-1.1-1.jar!/ehcache-failsafe.xml
[java] USING BAG
[java] 613 [main] INFO org.springframework.aop.framework.DefaultAopProxyFactory
- CGLIB2 available: pro
xyTargetClass feature enabled
[java] 616 [main] INFO org.springframework.core.CollectionFactory
- JDK 1.4+ collections available
[java] 627 [main] INFO org.springframework.core.CollectionFactory
- Commons Collections 3.x available
[java] Pass #1
[java] hibernateSessionFactory = org.hibernate.impl.SessionFactoryImpl@1d95da8
[java] execAddElement 1716 ms
[java] Pass #2
[java] hibernateSessionFactory = org.hibernate.impl.SessionFactoryImpl@1d95da8
[java] execAddElement 1372 ms
[java] Pass #3
[java] hibernateSessionFactory = org.hibernate.impl.SessionFactoryImpl@1d95da8
[java] execAddElement 1178 ms
[java] Average::1422.3333333333333 ms
[java] USING IDBAG
[java] Pass #1
[java] hibernateSessionFactory = org.hibernate.impl.SessionFactoryImpl@1a3b359
[java] execAddElement 485 ms
[java] Pass #2
[java] hibernateSessionFactory = org.hibernate.impl.SessionFactoryImpl@1a3b359
[java] execAddElement 438 ms
[java] Pass #3
[java] hibernateSessionFactory = org.hibernate.impl.SessionFactoryImpl@1a3b359
[java] execAddElement 526 ms
[java] Average::483.6666666666667 ms
[java] USING BAG
[java] Pass #1
[java] hibernateSessionFactory = org.hibernate.impl.SessionFactoryImpl@1d95da8
[java] execRemoveElement 1367 ms
[java] Pass #2
[java] hibernateSessionFactory = org.hibernate.impl.SessionFactoryImpl@1d95da8
[java] execRemoveElement 1292 ms
[java] Pass #3
[java] hibernateSessionFactory = org.hibernate.impl.SessionFactoryImpl@1d95da8
[java] execRemoveElement 1353 ms
[java] Average::1337.3333333333333 ms
[java] USING IDBAG
[java] Pass #1
[java] hibernateSessionFactory = org.hibernate.impl.SessionFactoryImpl@1a3b359
[java] execRemoveElement 413 ms
[java] Pass #2
[java] hibernateSessionFactory = org.hibernate.impl.SessionFactoryImpl@1a3b359
[java] execRemoveElement 419 ms
[java] Pass #3
[java] hibernateSessionFactory = org.hibernate.impl.SessionFactoryImpl@1a3b359
[java] execRemoveElement 402 ms
[java] Average::411.6666666666667 ms
The testing environment: kosta@swan ~/dev/hb-beyond-hw $ java -version java version "1.5.0_06" Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_06-b05) Java HotSpot(TM) Server VM (build 1.5.0_06-b05, mixed mode) kosta@swan ~/dev/hb-beyond-hw $ uname -a Linux swan 2.6.14-gentoo-r2 #1 SMP Wed Dec 21 09:41:13 PST 2005 x86_64 Intel(R) Xeon(TM) CPU 2.80GHz GenuineIntel GNU/Linux Note: BAGs brute force strategy is surely necessary for update and delete operations, but Hibernate could do a better job optimizing add (insert) operations. There is a little need for deleteing and reinserting all the members of the collection just to add one more record.
|
© Copyright 2003-2006 SourceLabs, Inc. All Rights Reserved.
411 First Ave S. Ste 403, Seattle, WA 98104 | 206.322.0099 | info@sourcelabs.com