`
bcyy
  • 浏览: 1827789 次
文章分类
社区版块
存档分类
最新评论

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

 
阅读更多

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

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

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

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

  1. packagecom.jadyer.hibernate;
  2. importjava.util.Date;
  3. publicclassUser11{
  4. privateStringid;
  5. privateStringname;
  6. privateStringpassword;
  7. privateDatecreateTime;
  8. /*--三个属性对应的setter和getter略--*/
  9. }
  10. packagecom.jadyer.hibernate;
  11. importjava.util.Date;
  12. publicclassUser22{
  13. privateintid;
  14. privateStringname;
  15. privateStringpassword;
  16. privateDatecreateTime;
  17. /*--三个属性对应的setter和getter略--*/
  18. }
  19. packagecom.jadyer.hibernate;
  20. importjava.util.Date;
  21. publicclassUser33{
  22. privateStringid;
  23. privateStringname;
  24. privateStringpassword;
  25. privateDatecreateTime;
  26. /*--三个属性对应的setter和getter略--*/
  27. }

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

  1. <?xmlversion="1.0"?>
  2. <!DOCTYPEhibernate-mappingPUBLIC
  3. "-//Hibernate/HibernateMappingDTD3.0//EN"
  4. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  5. <hibernate-mappingpackage="com.jadyer.hibernate">
  6. <classname="User11"table="t_user11">
  7. <idname="id"column="user_id"length="32">
  8. <generatorclass="uuid"/>
  9. </id>
  10. <propertyname="name"unique="true"not-null="true"length="20"/>
  11. <propertyname="password"not-null="true"length="10"/>
  12. <propertyname="createTime"column="create_time"/>
  13. </class>
  14. <classname="User22"table="t_user22">
  15. <idname="id"column="user_id">
  16. <generatorclass="native"/>
  17. </id>
  18. <propertyname="name"unique="true"not-null="true"length="20"/>
  19. <propertyname="password"/>
  20. <propertyname="createTime"column="createtime"/>
  21. </class>
  22. <classname="User33"table="t_user33">
  23. <idname="id"column="user_id"length="32">
  24. <generatorclass="assigned"/>
  25. </id>
  26. <propertyname="name"/>
  27. <propertyname="password"/>
  28. <propertyname="createTime"column="create_time"/>
  29. </class>
  30. </hibernate-mapping>

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

  1. packagecom.jadyer.hibernate;
  2. importorg.hibernate.cfg.Configuration;
  3. importorg.hibernate.tool.hbm2ddl.SchemaExport;
  4. /**
  5. *利用Hibernate映射文件生成数据库表
  6. */
  7. publicclassExportDB{
  8. publicstaticvoidmain(String[]args){
  9. //读取hibernate.cfg.xml文件
  10. Configurationcfg=newConfiguration().configure();
  11. //创建SchemaExport对象
  12. SchemaExportexport=newSchemaExport(cfg);
  13. //创建数据库表
  14. export.create(true,true);
  15. }
  16. }


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

  1. packagecom.jadyer.hibernate;
  2. importorg.hibernate.Session;
  3. importorg.hibernate.SessionFactory;
  4. importorg.hibernate.cfg.Configuration;
  5. publicclassHibernateSessionUtils{
  6. privatestaticSessionFactoryfactory;
  7. static{
  8. try{
  9. Configurationcfg=newConfiguration().configure();
  10. factory=cfg.buildSessionFactory();
  11. }catch(Exceptione){
  12. e.printStackTrace();
  13. }
  14. }
  15. publicstaticSessiongetSession(){
  16. returnfactory.openSession();
  17. }
  18. publicstaticvoidcloseSession(Sessionsession){
  19. if(null!=session&&session.isOpen()){
  20. session.close();
  21. }
  22. }
  23. }


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

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

分享到:
评论

相关推荐

    Hibernate 中文 html 帮助文档

    触发器实现的主键生成器(Primary keys assigned by triggers) 5.1.5. composite-id 5.1.6. 鉴别器(discriminator) 5.1.7. 版本(version)(可选) 5.1.8. timestamp (可选) 5.1.9. property 5.1.10. 多对一...

    hibernate学习笔记

    主键生成器Generator 6 多对一, 一对一, 一对多, 多对多 7 hibernate多对一关联映射(Hibernate_Many2One) 7 hibernate一对一主键关联映射(单向关联Person----&gt;IdCard) 8 hibernate一对一主键关联映射(双向关联...

    最全Hibernate 参考文档

    触发器实现的主键生成器(Primary keys assigned by triggers) 5.1.5. composite-id 5.1.6. 鉴别器(discriminator) 5.1.7. 版本(version)(可选) 5.1.8. timestamp (optional) 5.1.9. property 5.1.10. 多对一...

    hibernate 体系结构与配置 参考文档(html)

    触发器实现的主键生成器(Primary keys assigned by triggers) 5.1.5. composite-id 5.1.6. 鉴别器(discriminator) 5.1.7. 版本(version)(可选) 5.1.8. timestamp (可选) 5.1.9. property 5.1.10. 多对...

    hibernate3.04中文文档.chm

    触发器实现的主键生成器(Primary keys assigned by triggers) 6.1.5. composite-id 6.1.6. 鉴别器(discriminator) 6.1.7. 版本(version)(可选) 6.1.8. timestamp (optional) 6.1.9. property 6.1.10. ...

    Hibernate教程

    触发器实现的主键生成器(Primary keys assigned by triggers) 6.1.5. composite-id 6.1.6. 鉴别器(discriminator) 6.1.7. 版本(version)(可选) 6.1.8. timestamp (optional) 6.1.9. property 6.1.10. 多...

    Hibernate3+中文参考文档

    触发器实现的主键生成器(Primary keys assigned by triggers) 5.1.5. composite-id 5.1.6. 鉴别器(discriminator) 5.1.7. 版本(version)(可选) 5.1.8. timestamp (optional) 5.1.9. property 5.1.10. 多对一...

    hibernate 框架详解

    触发器实现的主键生成器(Primary keys assigned by triggers) 6.1.5. composite-id 6.1.6. 鉴别器(discriminator) 6.1.7. 版本(version)(可选) 6.1.8. timestamp (optional) 6.1.9. property 6.1.10. ...

    Hibernate参考文档

    触发器实现的主键生成器(Primary keys assigned by triggers) 5.1.5. composite-id 5.1.6. 鉴别器(discriminator) 5.1.7. 版本(version)(可选) 5.1.8. timestamp (可选) 5.1.9. property 5.1.10. 多对一...

    jdbc基础和参考

    hibernate不在自动生成主键值,需要你在插入时自己指明 3.hilo:高低值算法,由数据库表提供高值,程序提供低值 value = hi*(max_lo+1)+lo 4.sequences Cat: cid name color weight age ------------------------...

Global site tag (gtag.js) - Google Analytics