Hibernate(3)——实例总结Hibernate对象的状态和ThreadLoacl封闭的session

时间:2021-09-20 21:20:31

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

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

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

Hibernate(3)——实例总结Hibernate对象的状态和ThreadLoacl封闭的session

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

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

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

  内部执行过程

  Hibernate的CRUD方法代码

 /**
* UserDao
*
* @author Wang Yishuai.
* @date 2016/2/2 0002.
* @Copyright(c) 2016 Wang Yishuai,USTC,SSE.
*/
public class UserDao {
final Logger LOG = LoggerFactory.getLogger(UserDao.class); private SessionFactory sessionFactory; private UserDao() {
// 通过new一个Configuration实例,然后用该实例去调用configure返回一个配置实例
Configuration configuration = new Configuration().configure();
// 通过 配置实例的buildSessionFactory方法 生成一个 sessionFactory 对象
// buildSessionFactory方法会默认的去寻找配置文件hibernate.cfg.xml并解析xml文件
// 解析完毕生成sessionFactory,负责连接数据库
this.sessionFactory = configuration.buildSessionFactory();
} public static UserDao newInstance() {
return new UserDao();
} public void save(User user) {
// 通过 sessionFactory 获得一个数据库连接 session,可以操作数据库
Session session = sessionFactory.openSession();
// 把操作封装到数据库的事务,则需要开启一个事务
Transaction transaction = session.beginTransaction(); // 一般把对实体类和数据库的操作,放到try-catch-finally块
try {
// 把user对象插入到数据库
session.save(user);
// 提交操作事务
transaction.commit();
LOG.info("transaction.commit(); ok");
} catch (Exception e) {
// 提交事务失败,必须要回滚
transaction.rollback();
// 打印日志
LOG.error("save user error......", e);
} finally {
// 不能丢这一步,要释放资源
session.close();
LOG.info("session.close(); ok");
}
} public void retriveAll() {
Session session = sessionFactory.openSession();
List<User> userList = session.createQuery("from User").list();
session.close(); for (User user : userList) {
LOG.info("username = {}", user.getUsername());
}
} public void delete(User user) {
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction(); user = (User) session.get(user.getClass(), user.getUserId());
session.delete(user);
transaction.commit();
session.close();
} public void update(User user) {
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
user = (User) session.get(user.getClass(), user.getUserId());
user.setUsername("dadad");
session.update(user);
transaction.commit();
session.close();
}
}

  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做单元测试

 package test.java;

 import dashuai.dao.UserDao;
import dashuai.vo.User;
import org.junit.After;
import org.junit.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; /**
* Test
*
* @author Wang Yishuai.
* @date 2016/3/10 0010.
* @Copyright(c) 2016 Wang Yishuai,USTC,SSE.
*/
public class Test {
private static final Logger LOG = LoggerFactory.getLogger(Test.class); private UserDao userDao; @Before
public void init() {
LOG.info("init");
this.userDao = UserDao.newInstance();
} @After
public void clear() {
LOG.info("clear");
} @org.junit.Test
public void testHibernate1() {
User user = new User();
user.setUsername("niubi");
user.setPassword("1");
// 此时的user对象是瞬时态的 userDao.save(user);
// save到数据库之后,user变为持久态 // 最后在save方法里,最终执行 session.close();使得user变为游离态
user.setUsername("liuxiang");// 数据库中的数据不会变
}
}

  小结:

  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()方法,可变为游离对象;

  如图:

Hibernate(3)——实例总结Hibernate对象的状态和ThreadLoacl封闭的session

  使用getCurrentSession创建session

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

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

Hibernate(3)——实例总结Hibernate对象的状态和ThreadLoacl封闭的session

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

    public void delete(String account) {
Account acc = (Account) session.createQuery("from Account where account = '" + account + "'").uniqueResult();
Transaction transaction = session.beginTransaction();
acc.setMoney(acc.getMoney() - 100);
transaction.commit();
session.close();
} public void addMoney(String account) {
Account acc = (Account) session.createQuery("from Account where account = '" + account + "'").uniqueResult();
Transaction transaction = session.beginTransaction();
acc.setMoney(acc.getMoney() + 100);
transaction.commit();
session.close();
}

测试

    @org.junit.Test
public void testAccount() {
// 账户1
Account account1 = new Account();
account1.setAccount("1");
account1.setAid(1);
account1.setMoney(100.0); // 账户2
Account account2 = new Account();
account2.setAccount("2");
account2.setAid(2);
account2.setMoney(200.0); // accountDao.save(account1);
// accountDao.save(account2);
//
// 把2的账户的钱转100到1账户
// 先增加1账户100元
accountDao.addMoney("1");
// 删除2账户100元
accountDao.delete("2");
}

Hibernate(3)——实例总结Hibernate对象的状态和ThreadLoacl封闭的session

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

Hibernate(3)——实例总结Hibernate对象的状态和ThreadLoacl封闭的session

账户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文件中增加配置:

Hibernate(3)——实例总结Hibernate对象的状态和ThreadLoacl封闭的session

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

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层的方法前后用三个语句包住:
this.session = sessionFactory.getCurrentSession();
Transaction transaction = session.beginTransaction();
transaction.commit();

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

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

  能否不使用事务保存对象

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

欢迎关注

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

Hibernate(3)——实例总结Hibernate对象的状态和ThreadLoacl封闭的session

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

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

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

  2. Hibernate对象的状态

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

  3. &lbrack;原创&rsqb;java WEB学习笔记79:Hibernate学习之路--- 四种对象的状态,session核心方法:save&lpar;&rpar;方法,persist&lpar;&rpar;方法,get&lpar;&rpar; 和 load&lpar;&rpar; 方法,update&lpar;&rpar;方法,saveOrUpdate&lpar;&rpar; 方法,merge&lpar;&rpar; 方法,delete&lpar;&rpar; 方法,evict&lpar;&rpar;,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. Step01-题目申报

    毕业设计题目申报表 姓名:  王刚 学号:  201303041029 题目名称: 通用型云端物联网网关系统的设计与实现 毕业设计(论文)简介: 2017是物联网进入加速发展或成为最贴近普通民众的实用 ...

  2. eclipse中复制项目更名注意事项

    一.更改项目名称 web project Settings; 二.pom.xml中的项目名称更改

  3. windows github 搭建与使用

    git/github使用以下是全部在命令行使用(windows/github) 注册账户以及创建仓库先在github建立账号和创建仓库,此时为空的仓库 配置git下载并安装 git windows版本 ...

  4. Dreamweaver安装jQuery插件jQuery&lowbar;API&period;mxp

    要让Dreamweaver支持jQuery自动提示代码功能,方法很简单,下载一个插件—jQuery_API.mxp[点击下载]. 在Dreamweaver里依次选择“命令” -> “扩展管理” ...

  5. apicloud ios 打包流程

    1创建APP ID 等 2创建pruduction创建证书,用 这个证书导出P12格式的.. 3profile 证书,选 APP STORE  AD-HOC ok... http://docs.api ...

  6. Fzreo matlab

    fzero Root of nonlinear function collapse all in page Syntax x = fzero(fun,x0) example x = fzero(fun ...

  7. 虚拟机ubuntu新增挂载点进行磁盘扩展

    参考: http://m.blog.csdn.net/blog/pcsxk/38501579 一.vmware下扩展原来的磁盘空间 这个比较直观 1.关机状态下,选择磁盘->实用工具->扩 ...

  8. 【mysql】分区表

    分区表是什么? 分区表可以按照事先创建的规则,对mysql的记录进行分组,每一个组具有一个独立的逻辑单元来存储该组的数据.典型的如:按照创建时间的年份分组,按照id的顺序分组(每1000万条数据分一个 ...

  9. JavaSE——网络编程基础知识

    计算机网络的分类: 局域网(LAN) 指在一个较小地理范围内的各种计算机网络设备互联在一起的通信网络,可以包括一个或多个子网,通常局限在几千米的范围之内. 城域网(MAN) 主要由城域范围内的各个局域 ...

  10. python写一个密码生成器的类,要求有个类变量,统计一下一共生成过多少个密码。 要求有4个方法,1:构造方法 2 实例方法 3 类方法 4 静态方法

    生成指定长度的随机数字密码 生成指定长度的随机字母密码 生成指定长度的随机数字和字母的混合 #encoding=utf-8   import random import string class pa ...