Hibernate中的不同主键生成策略下flush()方法的妙用

依旧让代码站出来说话。。这是一个Java Project。。

首先是位于src下的Hibernate核心配置文件hibernate.cfg.xml

  1. <?xml version='1.0' encoding='UTF-8'?>  
  2. <!DOCTYPE hibernate-configuration PUBLIC  
  3.           "-//Hibernate/Hibernate Configuration DTD 3.0//EN"  
  4.           "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">  
  5. <hibernate-configuration>  
  6.     <session-factory>  
  7.         <property name="dialect">org.hibernate.dialect.MySQLDialect</property>  
  8.         <property name="connection.url">jdbc:mysql://localhost:3306/jadyer?characterEncoding=UTF-8</property>  
  9.         <property name="connection.username">root</property>  
  10.         <property name="connection.password">jadyer</property>  
  11.         <property name="connection.driver_class">com.mysql.jdbc.Driver</property>  
  12.           
  13.         <property name="hibernate.show_sql">true</property>  
  14.         <property name="hibernate.format_sql">true</property>  
  15.           
  16.         <!-- 批量读取数据。建议值50。需要JDBC和底层数据库的支持 -->  
  17.         <property name="hibernate.jdbc.fetch_size">50</property>           
  18.         <!-- 批量更新数据。建议值30 -->  
  19.         <property name="hibernate.jdbc.batch_size">30</property>  
  20.         <!-- 配置完这两个属性后,当我们向数据库提交SQL时,就不会一次性把全部数据读入内存 -->  
  21.         <!-- 而是按照一定的数量来批量读取相应的数据,但最终是否会生效还取决于底层数据库的支持 -->  
  22.         <!-- 有些数据库就不支持这些参数。其中Oracle和SQLServer都支持,而MySQL貌似就不支持 -->  
  23.           
  24.          <!-- 也可以通过以下方式编写映射文件 -->  
  25.         <mapping resource="com/jadyer/hibernate/all.hbm.xml"/>  
  26.         <!--   
  27.         <mapping resource="com/jadyer/hibernate/User11.hbm.xml"/>  
  28.         <mapping resource="com/jadyer/hibernate/User22.hbm.xml"/>  
  29.         <mapping resource="com/jadyer/hibernate/User33.hbm.xml"/>  
  30.          -->  
  31.     </session-factory>  
  32. </hibernate-configuration>  

接下来是我们用到的三个实体类

  1. package com.jadyer.hibernate;  
  2. import java.util.Date;  
  3. public class User11 {  
  4.     private String id;  
  5.     private String name;  
  6.     private String password;  
  7.     private Date createTime;  
  8.     /*--三个属性对应的setter和getter略--*/  
  9. }  
  10.   
  11.   
  12. package com.jadyer.hibernate;  
  13. import java.util.Date;  
  14. public class User22 {  
  15.     private int id;  
  16.     private String name;  
  17.     private String password;  
  18.     private Date createTime;  
  19.     /*--三个属性对应的setter和getter略--*/  
  20. }  
  21.   
  22.   
  23. package com.jadyer.hibernate;  
  24. import java.util.Date;  
  25. public class User33 {  
  26.     private String id;  
  27.     private String name;  
  28.     private String password;  
  29.     private Date createTime;  
  30.     /*--三个属性对应的setter和getter略--*/  
  31. }  

下面是这三个实体类所对应的Hibernate映射文件all.hbm.xml

  1. <?xml version="1.0"?>  
  2. <!DOCTYPE hibernate-mapping PUBLIC   
  3.     "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
  4.     "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">  
  5. <hibernate-mapping package="com.jadyer.hibernate">  
  6.     <class name="User11" table="t_user11">  
  7.         <id name="id" column="user_id" length="32">  
  8.             <generator class="uuid"/>  
  9.         </id>  
  10.         <property name="name" unique="true" not-null="true" length="20"/>  
  11.         <property name="password" not-null="true" length="10"/>  
  12.         <property name="createTime" column="create_time"/>  
  13.     </class>  
  14.       
  15.     <class name="User22" table="t_user22">  
  16.         <id name="id" column="user_id">  
  17.             <generator class="native"/>  
  18.         </id>  
  19.         <property name="name" unique="true" not-null="true" length="20"/>  
  20.         <property name="password"/>  
  21.         <property name="createTime" column="createtime"/>  
  22.     </class>  
  23.       
  24.     <class name="User33" table="t_user33">  
  25.         <id name="id" column="user_id" length="32">  
  26.             <generator class="assigned"/>  
  27.         </id>  
  28.         <property name="name"/>  
  29.         <property name="password"/>  
  30.         <property name="createTime" column="create_time"/>  
  31.     </class>  
  32. </hibernate-mapping>  

然后是利用Hibernate映射文件生成数据库表的ExportDB.java

  1. package com.jadyer.hibernate;  
  2.   
  3. import org.hibernate.cfg.Configuration;  
  4. import org.hibernate.tool.hbm2ddl.SchemaExport;  
  5.   
  6. /** 
  7.  * 利用Hibernate映射文件生成数据库表 
  8.  */  
  9. public class ExportDB {  
  10.     public static void main(String[] args) {  
  11.           
  12.         //读取hibernate.cfg.xml文件   
  13.         Configuration cfg = new Configuration().configure();  
  14.           
  15.         // 创建SchemaExport对象   
  16.         SchemaExport export = new SchemaExport(cfg);  
  17.           
  18.         // 创建数据库表   
  19.         export.create(truetrue);  
  20.     }  
  21. }  


接下来是自定义的用于生成Session的工具类HibernateSessionUtils.java

  1. package com.jadyer.hibernate;  
  2.   
  3. import org.hibernate.Session;  
  4. import org.hibernate.SessionFactory;  
  5. import org.hibernate.cfg.Configuration;  
  6.   
  7. public class HibernateSessionUtils {  
  8.     private static SessionFactory factory;  
  9.       
  10.     static {  
  11.         try {  
  12.             Configuration cfg = new Configuration().configure();  
  13.             factory = cfg.buildSessionFactory();  
  14.         }catch(Exception e) {  
  15.             e.printStackTrace();  
  16.         }  
  17.     }  
  18.       
  19.     public static Session getSession() {  
  20.         return factory.openSession();  
  21.     }  
  22.       
  23.     public static void closeSession(Session session) {  
  24.         if (null != session && session.isOpen()) {  
  25.             session.close();  
  26.         }  
  27.     }  
  28. }  


最后是借助了JUnit3.8实现的单元测试类SessionFlushTest.java

  1. package com.jadyer.hibernate;  
  2.   
  3. import java.util.Date;  
  4.   
  5. import org.hibernate.Session;  
  6. import org.hibernate.Transaction;  
  7.   
  8. import com.jadyer.hibernate.HibernateSessionUtils;  
  9. import com.jadyer.hibernate.User11;  
  10. import com.jadyer.hibernate.User22;  
  11. import com.jadyer.hibernate.User33;  
  12.   
  13. import junit.framework.TestCase;  
  14.   
  15. public class SessionFlushTest extends TestCase {  
  16.     /** 
  17.      * 向数据库中批量录入1000条数据 
  18.      * @see 执行save()方法时,同样会向一级缓存中存放数据 
  19.      * @see 所以要考虑到一次性大量的实体数据入库导致内存溢出 
  20.      * @see 这时便可使用到session的flush()和clear()方法 
  21.      */  
  22.     public void testCachea() {  
  23.         Session session = null;  
  24.         try {  
  25.             session = HibernateSessionUtils.getSession();  
  26.             session.beginTransaction(); //开启事务   
  27.             for (int i=0; i<1000; i++) {  
  28.                 User22 user = new User22();  
  29.                 user.setName("u_" + i);  
  30.                 session.save(user);  
  31.                 if (0 == i%20) {  
  32.                     session.flush(); //每20条数据就强制session持久化数据   
  33.                     session.clear(); //同时清空缓存,避免大量数据造成内存溢出   
  34.                 }  
  35.             }  
  36.             //如果数据量特别大,最好不要再使用Hibernate录入数据了,可以考虑采用JDBC实现   
  37.             //如果JDBC也不能满足要求,那么还可以考虑采用数据库本身的特定导入工具   
  38.             //比如相对于Oracle来说,就可以使用它的特定工具【SQL*Loader】   
  39.             session.getTransaction().commit(); //提交事务   
  40.         }catch(Exception e) {  
  41.             e.printStackTrace();  
  42.             session.getTransaction().rollback(); //发生异常则将事务回滚   
  43.         }finally {  
  44.             HibernateSessionUtils.closeSession(session); //最终不要忘记关闭org.hibernate.Session   
  45.         }  
  46.     }  
  47.       
  48.     /** 
  49.      * 测试主键生成策略为uuid时 
  50.      * 监测:执行save()之后以及再显式执行flush()时的SQL语句发送情况 
  51.      * 结果:调用save()后,不会立即发出insert语句。调用flush()后,才会发送并执行SQL语句 
  52.      * @see 【以下是关于session中的existsInDatebase属性的说明】 
  53.      * @see 重点关注Debug透视图中的右上角的Variables视图中数据的变化 
  54.      * @see 首先以Debug As---JUnit Test方式执行单元测试方法,然后当执行完save()而未执行到flush()之前 
  55.      * @see 在Variables视图中展开到session---actionQueue---insertions---elementData---[0]里面 
  56.      * @see 我们可以把[0]里面的数据理解为形成insert的对象的集合,以后就遍历该集合,用以生成insert语句 
  57.      * @see 在Variables视图中session下面的actionQueue是一个临时的集合,用来形成insert或其它的语句 
  58.      * @see 在Variables视图中session下面的persistenceContext就可以理解成是它的缓存 
  59.      * @see 展开到persistenceContext---entityEntries---map---entries---table---[0]---value---value 
  60.      * @see 注意:展开到table中时,未必每次都是[0],而此时我们应该展开的是存在HashMap$Entry<K,V>值的[i]就对了 
  61.      * @see 接着在第二层value下面,发现这里面还有一份数据。我们可以粗略的理解成是它缓存里的数据 
  62.      * @see 这时我们就会发现existsInDatebase属性,该属性就是用来判断当前数据在数据库中是否存在,值为false或true 
  63.      * @see 在执行完save()而没有执行flush()之前,existsInDatebase属性的值是false 
  64.      * @see 在执行完flush()而没有执行commit()之前,existsInDatebase的值就变为true,并且会马上发出SQL语句 
  65.      * @see 当existsInDatebase的值就变为true时,就说明数据库里面已经存在这条数据了 
  66.      * @see 这时再回到elementData---[0]临时集合查找,发现临时集合里面的数据已经没有了 
  67.      * @see 因为它要遍历该临时集合,再把临时集合里的数据拿出来形成insert语句。在执行insert后,临时集合就被清掉了 
  68.      * @see 所以说该临时集合是用来做临时交换的。当清掉临时集合之后,就会更新缓存中existsInDatebase的状态为true 
  69.      * @see 这时是在执行完flush()之后而没有执行commit()之前,此时还没有提交事务,但是已经执行了SQL语句 
  70.      * @see 而此时在MySQL Command Line Client中执行select查询的话,是查看不到数据的,这涉及到数据库的隔离级别 
  71.      * @see 我们可以使用select @@tx_isolation;命令查看MySQL默认隔离级别,结果为REPEATABLE-READ:即可重复读 
  72.      * @see 如果我们执行set transaction isolation level read uncommitted;命令,此时的隔离级别就是未提交读 
  73.      */  
  74.     public void testSave11() {  
  75.         Session session = null;  
  76.         Transaction tx = null;  
  77.         try {  
  78.             session = HibernateSessionUtils.getSession();  
  79.             tx = session.beginTransaction();  
  80.             User11 user = new User11();  
  81.             user.setName("张三");  
  82.             user.setPassword("123");  
  83.             user.setCreateTime(new Date());  
  84.             //由于User11采用的是uuid的主键生成策略   
  85.             //所以调用save()后,不会发出insert语句,而只是将user纳入了session的管理中   
  86.             //但此时id已经生成。而且这个时候session中的existsInDatebase的状态为false   
  87.             session.save(user);  
  88.             //调用flush()后,Hibernate会清理缓存,并执行SQL语句,会将user对象保存到数据库中   
  89.             //如果数据库的隔离级别设置为READ-UNCOMMITTED的话,即未提交读,那么我们就可以看到flush()过的数据   
  90.             //并且将session中insertions临时集合里user对象清除,此时session中existsInDatebase也被设置为true   
  91.             //session.flush();   
  92.             //默认情况下commit()操作会先执行flush()清理缓存,所以不需要显式的调用flush()方法   
  93.             tx.commit();  
  94.         }catch(Exception e) {  
  95.             e.printStackTrace();  
  96.             tx.rollback();  
  97.         }finally {  
  98.             HibernateSessionUtils.closeSession(session);  
  99.         }  
  100.     }  
  101.       
  102.     /** 
  103.      * 测试主键生成策略为native时 
  104.      * @see 监测:执行save()之后的SQL语句发送情况 
  105.      * @see 结果:调用save()之后,将立即发送并执行insert语句。因为需要返回由数据库生成的id值 
  106.      */  
  107.     public void testSave22() {  
  108.         Session session = null;  
  109.         try {  
  110.             session = HibernateSessionUtils.getSession();  
  111.             session.beginTransaction();  
  112.             User22 user = new User22();  
  113.             user.setName("李四");  
  114.             user.setPassword("123");  
  115.             user.setCreateTime(new Date());  
  116.             //由于User22采用的是native的主键生成策略   
  117.             //所以调用save()方法之后,将发送并执行insert语句,然后返回由数据库生成的id值   
  118.             //并纳入了session的管理,也修改了session中的existsInDatebase的状态为true   
  119.             //如果数据库的隔离级别设置为未提交读,那么我们就可以看到save()过的数据   
  120.             session.save(user);  
  121.             session.getTransaction().commit();  
  122.         }catch(Exception e) {  
  123.             e.printStackTrace();  
  124.             session.getTransaction().rollback();  
  125.         }finally {  
  126.             HibernateSessionUtils.closeSession(session);  
  127.         }  
  128.     }  
  129.       
  130.       
  131.     /** 
  132.      * 测试主键生成策略为uuid时 
  133.      * @see 监测:执行save()和evict()之后,缓存中数据的变化 
  134.      * @see 结果:执行commit()后无法成功提交,并报告possible nonthreadsafe access to session 
  135.      */  
  136.     public void testSave33() {  
  137.         Session session = null;  
  138.         try {  
  139.             session = HibernateSessionUtils.getSession();  
  140.             session.beginTransaction();  
  141.             User11 user = new User11();  
  142.             user.setName("王五");  
  143.             user.setPassword("123");  
  144.             user.setCreateTime(new Date());  
  145.             session.save(user);  
  146.             //执行evict()后,会将user对象从session中逐出。即从session的entityEntries属性中逐出user对象   
  147.             session.evict(user);  
  148.             //执行commit()后无法成功提交   
  149.             //因为hibernate在清理缓存时,会在session的临时集合insertions中取出user对象进行insert操作   
  150.             //接下来就会去更新persistenceContext里面entityEntries中的existsInDatabase属性的值为true   
  151.             //而我们已经采用evict()将user对象从session的entityEntries中逐出了,故找不到相关数据,无法更新   
  152.             //抛出异常:org.hibernate.AssertionFailure: possible nonthreadsafe access to session   
  153.             //翻译结果:该线程不安全。也就是说当清除user对象,回来更新数据的时候,它认为是其它线程把这个数据删掉了   
  154.             //其实给我们的理解:应该说不会出现这个问题。而网上有的人说这可能是Hibernate的一个BUG,其实不然   
  155.             //通过这些可以看到:Hibernate在缓存方面确实下足了一番功夫   
  156.             session.getTransaction().commit();  
  157.         }catch(Exception e) {  
  158.             e.printStackTrace();  
  159.             session.getTransaction().rollback();  
  160.         }finally {  
  161.             HibernateSessionUtils.closeSession(session);  
  162.         }  
  163.     }  
  164.       
  165.     /** 
  166.      * 测试主键生成策略为uuid时 
  167.      * @see 监测:在evict()之前先执行flush()方法,用以解决调用evict()之后无法提交数据的问题 
  168.      * @see 结果:执行commit()后成功提交 
  169.      */  
  170.     public void testSave44() {  
  171.         Session session = null;  
  172.         try {  
  173.             session = HibernateSessionUtils.getSession();  
  174.             session.beginTransaction();  
  175.             User11 user = new User11();  
  176.             user.setName("赵六");  
  177.             user.setPassword("123");  
  178.             user.setCreateTime(new Date());  
  179.             session.save(user);  
  180.             session.flush(); //此时会发出insert语句   
  181.             session.evict(user);  
  182.             //执行commit()后可以成功提交   
  183.             //因为hibernate在清理缓存时,在session的insertions临时集合中无法找到user对象   
  184.             //所以就不会发出insert语句,也就不会更新session中的existsInDatabase属性的状态   
  185.             session.getTransaction().commit();  
  186.         }catch(Exception e) {  
  187.             e.printStackTrace();  
  188.             session.getTransaction().rollback();  
  189.         }finally {  
  190.             HibernateSessionUtils.closeSession(session);  
  191.         }  
  192.     }  
  193.       
  194.     /** 
  195.      * 测试主键生成策略为native时 
  196.      * @ses 监测:执行save()和evict()之后,缓存中数据的变化 
  197.      * @ses 结果:执行commit()后成功提交 
  198.      */  
  199.     public void testSave55() {  
  200.         Session session = null;  
  201.         try {  
  202.             session = HibernateSessionUtils.getSession();  
  203.             session.beginTransaction();  
  204.             User22 user = new User22();  
  205.             user.setName("马七");  
  206.             user.setPassword("123");  
  207.             user.setCreateTime(new Date());  
  208.             session.save(user); //此时会发送insert语句   
  209.             session.evict(user);  
  210.             //执行commit()后可以成功提交   
  211.             //因为hibernate在清理缓存时,在session的insertions临时集合中无法找到user对象   
  212.             //所以就不会发出insert语句,也就不会更新session中的existsInDatabase属性的状态   
  213.             session.getTransaction().commit();  
  214.         }catch(Exception e) {  
  215.             e.printStackTrace();  
  216.             session.getTransaction().rollback();  
  217.         }finally {  
  218.             HibernateSessionUtils.closeSession(session);  
  219.         }  
  220.     }  
  221.       
  222.     /** 
  223.      * 测试主键生成策略为assigned时 
  224.      * @see 监测:批量执行save、update、delete操作的顺序 
  225.      * @see 结果:Hibernate会按照save、update、delete顺序提交相关操作 
  226.      */  
  227.     public void testSave66() {  
  228.         Session session = null;  
  229.         try {  
  230.             session = HibernateSessionUtils.getSession();  
  231.             session.beginTransaction();  
  232.             User33 user = new User33();  
  233.             user.setId("001");  
  234.             user.setName("王八");  
  235.             session.save(user);  
  236.             user.setName("夙瑶");  
  237.             //session.update(user); //也可以不显式的调用update()。此时的user正处于持久态,它会自动更新的   
  238.             User33 user3 = new User33();  
  239.             user3.setId("002");  
  240.             user3.setName("玄宵");  
  241.             session.save(user3);  
  242.             //Hibernate: insert into t_user33 (name, password, create_time, user_id) values (?, ?, ?, ?)   
  243.             //Hibernate: insert into t_user33 (name, password, create_time, user_id) values (?, ?, ?, ?)   
  244.             //Hibernate: update t_user33 set name=?, password=?, create_time=? where user_id=?   
  245.             //Hibernate会按照save、update、delete顺序提交相关操作   
  246.             session.getTransaction().commit();  
  247.         }catch(Exception e) {  
  248.             e.printStackTrace();  
  249.             session.getTransaction().rollback();  
  250.         }finally {  
  251.             HibernateSessionUtils.closeSession(session);  
  252.         }  
  253.     }     
  254.       
  255.     /** 
  256.      * 测试主键生成策略为assigned时 
  257.      * @see 监测:利用flush()实现自定义的save、update、delete执行顺序 
  258.      * @see 结果:SQL会按照我们的意愿执行 
  259.      */  
  260.     public void testSave77() {  
  261.         Session session = null;  
  262.         try {  
  263.             session = HibernateSessionUtils.getSession();  
  264.             session.beginTransaction();  
  265.             User33 user = new User33();  
  266.             user.setId("003");  
  267.             user.setName("蔡依林");  
  268.             session.save(user);  
  269.             user.setName("菜10");  
  270.             //session.update(user); //也可以不显式的调用update()。此时的user正处于持久态,它会自动更新的   
  271.             session.flush();//利用flush()实现自定义的save、update、delete执行顺序  
  272.             User33 user33 = new User33();  
  273.             user33.setId("004");  
  274.             user33.setName("郑伊健");  
  275.             session.save(user33);  
  276.             //Hibernate: insert into t_user33 (name, password, create_time, user_id) values (?, ?, ?, ?)   
  277.             //Hibernate: update t_user33 set name=?, password=?, create_time=? where user_id=?   
  278.             //Hibernate: insert into t_user33 (name, password, create_time, user_id) values (?, ?, ?, ?)   
  279.             //由于在udpate()后面执行了flush()方法,所以在commit()清理缓存时,只会生成session.save(user33)的SQL语句   
  280.             session.getTransaction().commit();  
  281.         }catch(Exception e) {  
  282.             e.printStackTrace();  
  283.             session.getTransaction().rollback();  
  284.         }finally {  
  285.             HibernateSessionUtils.closeSession(session);  
  286.         }  
  287.     }  


原文链接: http://blog.csdn.net/hopezhangbo/article/details/7388201

你可能感兴趣的:(Hibernate中的不同主键生成策略下flush()方法的妙用)