俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!涉及的知识点总结如下:

  • Hibernate的内部执行过程(CRUD)
  • 对象的状态及其转换图和例子
  • 使用JUnit测试
  • 使用getCurrentSession代替openSession
  • ThreadLoacl对象
  • 享元模式
  • session.update(obj),为保证执行更新,推荐使用session.flush()刷新缓存;

  1和2大概总结了Hibernate运行的技术原理,那么现在总结一下它自身的编写过程:如下:

  其中本质上主要就是使用了dom4j解析配置文件+反射技术来支撑了整个框架的运行。当然如果是注解的话,还有注解技术。而其中世界级的设计思想和编程技巧,又是另一个方面的技术内容了。

  Hibernate是如何识别持久化类的?

  在Hibernate的hibernate.cfg.xml配置文件中引入了实体关系的映射文件Xxx.hbm.xml,而在映射文件中指明了持久化类是哪些,所以Hibernate通过它识别持久化类,Hibernate容器——》hibernate.cfg.xml——》 *.hbm.xml——》class元素的name属性加在持久化类,通过这种方式识别持久化类。

  内部执行过程

  Hibernate的CRUD方法代码

  1. /**
  2. * UserDao
  3. *
  4. * @author Wang Yishuai.
  5. * @date 2016/2/2 0002.
  6. * @Copyright(c) 2016 Wang Yishuai,USTC,SSE.
  7. */
  8. public class UserDao {
  9. final Logger LOG = LoggerFactory.getLogger(UserDao.class);
  10.  
  11. private SessionFactory sessionFactory;
  12.  
  13. private UserDao() {
  14. // 通过new一个Configuration实例,然后用该实例去调用configure返回一个配置实例
  15. Configuration configuration = new Configuration().configure();
  16. // 通过 配置实例的buildSessionFactory方法 生成一个 sessionFactory 对象
  17. // buildSessionFactory方法会默认的去寻找配置文件hibernate.cfg.xml并解析xml文件
  18. // 解析完毕生成sessionFactory,负责连接数据库
  19. this.sessionFactory = configuration.buildSessionFactory();
  20. }
  21.  
  22. public static UserDao newInstance() {
  23. return new UserDao();
  24. }
  25.  
  26. public void save(User user) {
  27. // 通过 sessionFactory 获得一个数据库连接 session,可以操作数据库
  28. Session session = sessionFactory.openSession();
  29. // 把操作封装到数据库的事务,则需要开启一个事务
  30. Transaction transaction = session.beginTransaction();
  31.  
  32. // 一般把对实体类和数据库的操作,放到try-catch-finally块
  33. try {
  34. // 把user对象插入到数据库
  35. session.save(user);
  36. // 提交操作事务
  37. transaction.commit();
  38. LOG.info("transaction.commit(); ok");
  39. } catch (Exception e) {
  40. // 提交事务失败,必须要回滚
  41. transaction.rollback();
  42. // 打印日志
  43. LOG.error("save user error......", e);
  44. } finally {
  45. // 不能丢这一步,要释放资源
  46. session.close();
  47. LOG.info("session.close(); ok");
  48. }
  49. }
  50.  
  51. public void retriveAll() {
  52. Session session = sessionFactory.openSession();
  53. List<User> userList = session.createQuery("from User").list();
  54. session.close();
  55.  
  56. for (User user : userList) {
  57. LOG.info("username = {}", user.getUsername());
  58. }
  59. }
  60.  
  61. public void delete(User user) {
  62. Session session = sessionFactory.openSession();
  63. Transaction transaction = session.beginTransaction();
  64.  
  65. user = (User) session.get(user.getClass(), user.getUserId());
  66. session.delete(user);
  67. transaction.commit();
  68. session.close();
  69. }
  70.  
  71. public void update(User user) {
  72. Session session = sessionFactory.openSession();
  73. Transaction transaction = session.beginTransaction();
  74. user = (User) session.get(user.getClass(), user.getUserId());
  75. user.setUsername("dadad");
  76. session.update(user);
  77. transaction.commit();
  78. session.close();
  79. }
  80. }

  2 中我们知道hibernate通过读取配置文件和依靠反射去拼接对应的SQL语句,比如当hibernate执行session.get(xxx.class, xL)这个代码的时候,在hibernate内部会拼接成一个SQL语句:

select
user0_.userId as userId0_0_,
user0_.username as username0_0_,
user0_.password as password0_0_
from
user user0_
where
user0_.userId=?

要生成该SQL语句,必须找到数据库对应的表,以及表中的字段,表中的主键。又因为session.get方法的第一个参数为持久化类的class形式,去sessionFactory中查找该class对应的映射文件,找到该映射文件以后,映射文件中的class元素的name

属性的值就是对应的持久化类,class元素的table属性就是对应的表。这样找到的。

  Hibernate对象的三种状态

  • Transient 瞬时状态:数据库中没有数据与之一一对应,id没有纳入session的管理,没有持久化标识(相当于主键),随时都有可能被垃圾回收。
  • Persist 持久化状态:数据库中有数据与之一一对应,id纳入了session的管理。特点:属性与数据的改变,与数据库中保持一致
  • Detached 托管状态/游离状态:没有纳入session的管理,但数据在数据库中存在。

下面是测试代码:使用的JUnit4做单元测试

  1. package test.java;
  2.  
  3. import dashuai.dao.UserDao;
  4. import dashuai.vo.User;
  5. import org.junit.After;
  6. import org.junit.Before;
  7. import org.slf4j.Logger;
  8. import org.slf4j.LoggerFactory;
  9.  
  10. /**
  11. * Test
  12. *
  13. * @author Wang Yishuai.
  14. * @date 2016/3/10 0010.
  15. * @Copyright(c) 2016 Wang Yishuai,USTC,SSE.
  16. */
  17. public class Test {
  18. private static final Logger LOG = LoggerFactory.getLogger(Test.class);
  19.  
  20. private UserDao userDao;
  21.  
  22. @Before
  23. public void init() {
  24. LOG.info("init");
  25. this.userDao = UserDao.newInstance();
  26. }
  27.  
  28. @After
  29. public void clear() {
  30. LOG.info("clear");
  31. }
  32.  
  33. @org.junit.Test
  34. public void testHibernate1() {
  35. User user = new User();
  36. user.setUsername("niubi");
  37. user.setPassword("1");
  38. // 此时的user对象是瞬时态的
  39.  
  40. userDao.save(user);
  41. // save到数据库之后,user变为持久态
  42.  
  43. // 最后在save方法里,最终执行 session.close();使得user变为游离态
  44. user.setUsername("liuxiang");// 数据库中的数据不会变
  45. }
  46. }

  小结:

  1. 瞬时状态的对象没有和hibernate发生交互,转换为持久态对象和hibernate容器发生交互,之后一直到事务提交,当session关闭之后

    ,对象脱离hibernate管理了,变为游离态。

  2. 瞬时状态:

    1.new出来的对象,但没有进行session.save();

    2.持久化对象调用delete()方法,持久态对象也会变成瞬时对象;

  3. 持久化对象:

    1.在数据库中通过get(),load(),find()查询出来的对象数据;

    2.瞬时的对象调用save();方法

    3.游离态的对象调用update()方法;

  4. 托管/游离态对象:

    1.手动构建游离态对象;

    2.持久化对象调用evict()(evict方法可以把一个对象从hibernate容器中去除掉),clear()(session.clear方法清空hibernate内部的所有对象),close()方法,可变为游离对象;

  如图:

  使用getCurrentSession创建session

  为什么不推荐使用openSession方法,看一个例子:

  银行的转账操作:一个数据库的表account,如下:

现在把100块从一个账户转到另外一个账户,代码如下:

  1. public void delete(String account) {
  2. Account acc = (Account) session.createQuery("from Account where account = '" + account + "'").uniqueResult();
  3. Transaction transaction = session.beginTransaction();
  4. acc.setMoney(acc.getMoney() - 100);
  5. transaction.commit();
  6. session.close();
  7. }
  8.  
  9. public void addMoney(String account) {
  10. Account acc = (Account) session.createQuery("from Account where account = '" + account + "'").uniqueResult();
  11. Transaction transaction = session.beginTransaction();
  12. acc.setMoney(acc.getMoney() + 100);
  13. transaction.commit();
  14. session.close();
  15. }

测试

  1. @org.junit.Test
  2. public void testAccount() {
  3. // 账户1
  4. Account account1 = new Account();
  5. account1.setAccount("1");
  6. account1.setAid(1);
  7. account1.setMoney(100.0);
  8.  
  9. // 账户2
  10. Account account2 = new Account();
  11. account2.setAccount("2");
  12. account2.setAid(2);
  13. account2.setMoney(200.0);
  14.  
  15. // accountDao.save(account1);
  16. // accountDao.save(account2);
  17. //
  18. // 把2的账户的钱转100到1账户
  19. // 先增加1账户100元
  20. accountDao.addMoney("1");
  21. // 删除2账户100元
  22. accountDao.delete("2");
  23. }

这是转账之前的表,转账之后如下:

账户2的钱没有少100,这肯定不对。而且JUnit还报错了:org.hibernate.SessionException: Session is closed!,说明当执行sessionFactory.openSession的时候,会创建一个新的session,再执行事务提交的时候,肯定是一个新的事务提交了,说明上面的代码中addMoney中的事务和delete的事务不是同一个事务,但是根据需求分析,这两个方法必须在同一个事务中,如果在同一个事务中则必须在同一个session中!也就是说openSession每次都会创建一个新的session,这样非常耗费内存,且有安全隐患。

  

  使用getCurrentSession

  先检查当前线程中是否有session,如果当前线程中有session,则把session提取出来,直接使用,如果当前线程中没有session,才用openSession方法创建session,然后把新创建的session放入到threadlocal中,当再次得到session的时候就是从当前线程中获取了。其实这非常类似享元设计模式的思想:

减小内存的占用问题——享元模式和单例模式的对比分析

因为数据库的crud操作必须在事务的条件下运行,而使用getCurrentSession创建session,当事务提交的时候,session自动关闭, 这种做法相当于把session和事务绑定在一起了。

  再补充一下ThreadLoacl类的作用

  《Java并发编程实践》上面说到:ThreadLoacl是一种线程封闭的实现策略。把变量封闭在线程中,使之变得安全。ThreadLocal并不是一个Thread,而是Thread的局部变量,也许把它命名为ThreadLocalVariable更容易让人理解一些。使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。ThreadLocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。

  getCurrentSession用法

  在hibernate.cfg.xml文件中增加配置:

  说明session从当前线程中获取。代码中使用无需关闭session事务,非常方便,推荐使用。

  1. this.session = sessionFactory.getCurrentSession();

  下面比较两者的区别:

  1 getCurrentSession创建的session会绑定到当前线程,而openSession不会。

  2 getCurrentSession创建的线程会在事务回滚或事物提交后自动关闭,而openSession必须手动关闭

   这里getCurrentSession使用本地事务(本地事务:jdbc)时 要在配置文件里进行如下设置:

<property name="hibernate.current_session_context_class">thread</property>
  如果使用的是全局事务(jta事务),如下配置:
<property name="hibernate.current_session_context_class">jta</property>

  3.getCurrentSession () 使用当前的session,openSession() 重新建立一个新的session 

  使用getCurrentSession的优点

  1)非常适合web程序,并发管理十分容易,session由线程产生,且能够保证一个线程总是只有一个session。
  2)资源回收变得轻松。session将在commit()或rollback()后自动释放,无需再手动关闭事务。
  3)事务管理十分直观,一般来说在业务层或service层的方法前后用三个语句包住:
  1. this.session = sessionFactory.getCurrentSession();
  2. Transaction transaction = session.beginTransaction();
  3. transaction.commit();

就可以让该方法的原子性得到保证。

  4)由于3)的方式应用十分普遍,用spring AOP 对 service 层进行事务控制就更简单了,上面三行代码甚至都不必写。
 
注意:永远不在DAO的方法内做开启session、打开事务、提交事务、释放session这些事,一般来说这不是什么好习惯。一般交给Spring AOP 容器去做事务的管理。

  能否不使用事务保存对象

  Hibernate3.3为了提倡大家使用事务,把默认的setAutoCommit设为false,所以,不使用事务也可以实现对象保存,只是Hibenate并不推荐这么做。

欢迎关注

dashuai的博客是终身学习践行者,大厂程序员,且专注于工作经验、学习笔记的分享和日常吐槽,包括但不限于互联网行业,附带分享一些PDF电子书,资料,帮忙内推,欢迎拍砖!

Hibernate(3)——实例总结Hibernate对象的状态和ThreadLoacl封闭的session的更多相关文章

  1. 一口一口吃掉Hibernate(八)——Hibernate中inverse的用法

    一.Inverse是hibernate双向关系中的基本概念.inverse的真正作用就是指定由哪一方来维护之间的关联关系.当一方中指定了“inverse=false”(默认),那么那一方就有责任负责之 ...

  2. Hibernate对象的状态

    站在持久化的角度, Hibernate 把对象分为 4 种状态: 1. 持久化状态 2. 临时状态 3. 游离状态 4. 删除状态 Session 的特定方法能使对象从一个状态转换到另一个状态. 下面 ...

  3. [原创]java WEB学习笔记79:Hibernate学习之路--- 四种对象的状态,session核心方法:save()方法,persist()方法,get() 和 load() 方法,update()方法,saveOrUpdate() 方法,merge() 方法,delete() 方法,evict(),hibernate 调用存储过程,hibernate 与 触发器协同工作

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  4. Hibernate之Session对象的相关方法以及持久化对象的状态

    一.持久化对象的状态        站在持久化的角度, Hibernate 把对象分为 4种状态: 持久化状态,临时状态,游离状态,删除状态.Session 的特定方法能使对象从一个状态转换到另一个状 ...

  5. Hibernate对象的状态和映射

    一. Hibernate对象的状态 实体对象的三种状态: 1) 暂态(瞬时态)(Transient)---实体在内存中的自由存在,它与数据库的记录无关. po在DB中无记录(无副本),po和sessi ...

  6. Hibernate PO对象的状态

    Hibernate的PO对象有三种状态:临时状态(又称临时态).持久状态(又称为持久态)和脱管状态(又称为脱管态.游离态).处理持久态的对象也称为PO,临时对象和脱管对象也称为VO. 1.临时态: 简 ...

  7. Hibernate中的对象有三种状态

    Hibernate中的对象有三种状态: 瞬时状态 (Transient),持久状态 (Persistent), 1. 脱管状态 (Detached) 1. 1. 瞬时状态 (Transient) 由  ...

  8. hibernate 增改查后对象的三种状态转换

    this.getSession().update(obj); this.getSession().merge(obj); this.getSession().saveOrUpdate(obj);1. ...

  9. Hibernate对象的状态转换

    Hibernate中的实体对象可以分为三种状态:Transient(临时).Persistent(持久).Detached(游离) Transient 用new创建出对象,这些对象还没有与数据库发生任 ...

随机推荐

  1. 十分钟介绍mobx与react

    原文地址:https://mobxjs.github.io/mobx/getting-started.html 写在前面:本人英语水平有限,主要是写给自己看的,若有哪位同学看到了有问题的地方,请为我指 ...

  2. 写出易调试的SQL(修订版)

    h4 { background: #698B22 !important; color: #FFFFFF; font-family: "微软雅黑", "宋体", ...

  3. 带你实现开发者头条APP(四)---首页优化(加入design包)

    title: 带你实现开发者头条APP(四)---首页优化(加入design包) tags: design,Toolbar,TabLayout,RecyclerView grammar_cjkRuby ...

  4. 通过三次优化,我将gif加载优化了16.9%

    WeTest 导读 现在app越来越炫,动不动就搞点动画,复杂的动画用原生实现起来挺复杂,如是就搞起gif播放动画的形式,节省开发成本.   背 景 设计同学准备给一个png序列,开发读取png序列, ...

  5. Linux学习之文件操作

    Linux,一起学习进步-    mkdir The mkdir command is used to create directories.It works like this: mkdir命令是用 ...

  6. ES6的一些常用特性

    由于公司的前端业务全部基于ES6开发,于是给自己开个小灶补补ES6的一些常用特性.原来打算花两天学习ES6的,结果花了3天才勉强过了一遍阮老师的ES6标准入门(水好深,ES6没学好ES7又来了...) ...

  7. C++内联函数

    在C语言中,我们使用宏定义函数这种借助编译器的优化技术来减少程序的执行时间,那么在C++中有没有相同的技术或者更好的实现方法呢?答案是有的,那就是内联函数.内联函数作为编译器优化手段的一种技术,在降低 ...

  8. VSCode调试go语言出现:exec: "gcc": executable file not found in %PATH%

    1.问题描述 由于安装VS15 Preview 5,搞的系统由重新安装一次:在用vscdoe编译go语言时,出现以下问题: # odbcexec: "gcc": executabl ...

  9. [译]处理文本数据(scikit-learn 教程3)

    原文网址:http://scikit-learn.org/stable/tutorial/text_analytics/working_with_text_data.html 翻译:Tacey Won ...

  10. 免费公开课,讲解强大的文档集成组件Aspose,现在可报名

    课程①:Aspose.Total公开课内容:讲解全能型文档管理工具Aspose.Total主要功能及应用领域时间:2016-11-24 14:30 (暂定)报名地址:http://training.e ...