Hibernate(3)——实例总结Hibernate对象的状态和ThreadLoacl封闭的session
俗话说,自己写的代码,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方法代码
/**
* 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");// 数据库中的数据不会变
}
}
小结:
- 瞬时状态的对象没有和hibernate发生交互,转换为持久态对象和hibernate容器发生交互,之后一直到事务提交,当session关闭之后
,对象脱离hibernate管理了,变为游离态。
- 瞬时状态:
1.new出来的对象,但没有进行session.save();
2.持久化对象调用delete()方法,持久态对象也会变成瞬时对象;
- 持久化对象:
1.在数据库中通过get(),load(),find()查询出来的对象数据;
2.瞬时的对象调用save();方法
3.游离态的对象调用update()方法;
- 托管/游离态对象:
1.手动构建游离态对象;
2.持久化对象调用evict()(evict方法可以把一个对象从hibernate容器中去除掉),clear()(session.clear方法清空hibernate内部的所有对象),close()方法,可变为游离对象;
如图:
使用getCurrentSession创建session
为什么不推荐使用openSession方法,看一个例子:
银行的转账操作:一个数据库的表account,如下:
现在把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");
}
这是转账之前的表,转账之后如下:
账户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事务,非常方便,推荐使用。
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>
使用getCurrentSession的优点
2)资源回收变得轻松。session将在commit()或rollback()后自动释放,无需再手动关闭事务。
3)事务管理十分直观,一般来说在业务层或service层的方法前后用三个语句包住:
this.session = sessionFactory.getCurrentSession();
Transaction transaction = session.beginTransaction();
transaction.commit();
就可以让该方法的原子性得到保证。
能否不使用事务保存对象
欢迎关注
dashuai的博客是终身学习践行者,大厂程序员,且专注于工作经验、学习笔记的分享和日常吐槽,包括但不限于互联网行业,附带分享一些PDF电子书,资料,帮忙内推,欢迎拍砖!
Hibernate(3)——实例总结Hibernate对象的状态和ThreadLoacl封闭的session的更多相关文章
- 一口一口吃掉Hibernate(八)——Hibernate中inverse的用法
一.Inverse是hibernate双向关系中的基本概念.inverse的真正作用就是指定由哪一方来维护之间的关联关系.当一方中指定了“inverse=false”(默认),那么那一方就有责任负责之 ...
- Hibernate对象的状态
站在持久化的角度, Hibernate 把对象分为 4 种状态: 1. 持久化状态 2. 临时状态 3. 游离状态 4. 删除状态 Session 的特定方法能使对象从一个状态转换到另一个状态. 下面 ...
- [原创]java WEB学习笔记79:Hibernate学习之路--- 四种对象的状态,session核心方法:save()方法,persist()方法,get() 和 load() 方法,update()方法,saveOrUpdate() 方法,merge() 方法,delete() 方法,evict(),hibernate 调用存储过程,hibernate 与 触发器协同工作
本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...
- Hibernate之Session对象的相关方法以及持久化对象的状态
一.持久化对象的状态 站在持久化的角度, Hibernate 把对象分为 4种状态: 持久化状态,临时状态,游离状态,删除状态.Session 的特定方法能使对象从一个状态转换到另一个状 ...
- Hibernate对象的状态和映射
一. Hibernate对象的状态 实体对象的三种状态: 1) 暂态(瞬时态)(Transient)---实体在内存中的自由存在,它与数据库的记录无关. po在DB中无记录(无副本),po和sessi ...
- Hibernate PO对象的状态
Hibernate的PO对象有三种状态:临时状态(又称临时态).持久状态(又称为持久态)和脱管状态(又称为脱管态.游离态).处理持久态的对象也称为PO,临时对象和脱管对象也称为VO. 1.临时态: 简 ...
- Hibernate中的对象有三种状态
Hibernate中的对象有三种状态: 瞬时状态 (Transient),持久状态 (Persistent), 1. 脱管状态 (Detached) 1. 1. 瞬时状态 (Transient) 由 ...
- hibernate 增改查后对象的三种状态转换
this.getSession().update(obj); this.getSession().merge(obj); this.getSession().saveOrUpdate(obj);1. ...
- Hibernate对象的状态转换
Hibernate中的实体对象可以分为三种状态:Transient(临时).Persistent(持久).Detached(游离) Transient 用new创建出对象,这些对象还没有与数据库发生任 ...
随机推荐
- 从0开始搭建SQL Server AlwaysOn 第二篇(配置故障转移集群)
从0开始搭建SQL Server AlwaysOn 第二篇(配置故障转移集群) 第一篇http://www.cnblogs.com/lyhabc/p/4678330.html第二篇http://www ...
- ABP文档 - 导航
文档目录 本节内容: 创建菜单 注册导航供应器 显示菜单 每个web应用都有一些菜单用来在页面/屏幕之间导航,ABP提供了一个通用的基础框架创建并显示菜单给用户. 创建菜单 一个应用可能由不同模块组成 ...
- nginx的使用
1.nginx的下载 解压后文件目录: 2.nginx的常用命令 nginx -s stop 强制关闭 nginx -s quit 安全关闭 nginx -s reload 改变配置文件的时候,重 ...
- Node.js:理解stream
Stream在node.js中是一个抽象的接口,基于EventEmitter,也是一种Buffer的高级封装,用来处理流数据.流模块便是提供各种API让我们可以很简单的使用Stream. 流分为四种类 ...
- Hbase的伪分布式安装
Hbase安装模式介绍 单机模式 1> Hbase不使用HDFS,仅使用本地文件系统 2> ZooKeeper与Hbase运行在同一个JVM中 分布式模式– 伪分布式模式1> 所有进 ...
- Kooboo CMS技术文档之一:Kooboo CMS技术背景
语言平台 依赖注入方案 存储模型 1. 语言平台 Kooboo CMS基于.NET Framework 4.x,.NET Framework 4.x的一些技术特性成为站点开发人员使用Kooboo CM ...
- 拼图小游戏之计算后样式与CSS动画的冲突
先说结论: 前几天写了几个非常简单的移动端小游戏,其中一个拼图游戏让我郁闷了一段时间.因为要获取每张图片的位置,用`<style>`标签写的样式,直接获取计算后样式再用来交换位置,结果就悲 ...
- 推荐一个ASP.NET网站内容管理系统源码
许多人都有各自的兴趣,如打球.踢毽子.看书.看电视.玩游戏等等....我近来迷上了猜灯谜,于是业余做了一个在线猜灯谜的网站:何问起谜语. 先出个谜语让你猜猜:不可缺一点(打一字).可以在线猜:http ...
- 自建git node pm2 (不赘述,就说遇见的问题)
//======================[git]部分 主题部分还是按照网上的办法进行安装. 安装的话 分为两个办法(一个是yum (contos办法) 或者sudo(ubuntu办法) ...
- asp.net core 实战之 redis 负载均衡和"高可用"实现
1.概述 分布式系统缓存已经变得不可或缺,本文主要阐述如何实现redis主从复制集群的负载均衡,以及 redis的"高可用"实现, 呵呵双引号的"高可用"并不是 ...