持久化对象的状态
Hibernate的持久化类
Java类与数据库的某个表建立了映射关系.这个类就称为是持久化类.
持久化类 = Java类 + hbm的配置文件
Hibernate的持久化类的状态
Hibernate为了管理持久化类:将持久化类分成了三个状态:
-
瞬时态:Transient Object
没有持久化标识OID, 没有被纳入到Session对象的管理. -
持久态:Persistent Object
有持久化标识OID,已经被纳入到Session对象的管理. -
脱管态:Detached Object
有持久化标识OID,没有被纳入到Session对象的管理.
Hibernate持久化对象的状态的转换
-
瞬时态 – 没有持久化标识OID, 没有被纳入到Session对象的管理
获得瞬时态的对象
User user = new User()
瞬时态对象转换持久态
save()/saveOrUpdate();
瞬时态对象转换成脱管态
user.setId(1);
//此时虽然手动添加了oid,但是却没有纳入到Session对象的管理 -
持久态 – 有持久化标识OID,已经被纳入到Session对象的管理
获得持久态的对象
get()/load();
持久态转换成瞬时态对象
delete();
— 比较有争议的,进入特殊的状态(删除态:Hibernate中不建议使用的)持久态对象转成脱管态对象
session的close()/evict()/clear();
注意:持久态对象有自动更新数据库的能力!!!
-
脱管态 – 有持久化标识OID,没有被纳入到Session对象的管理
获得托管态对象:不建议直接获得脱管态的对象.
User user = new User(); user.setId(1);
脱管态对象转换成持久态对象
update();/saveOrUpdate()/lock();
脱管态对象转换成瞬时态对象
user.setId(null);
Hibernate的一级缓存
-
什么是缓存?
其实就是一块内存空间,将数据源(数据库或者文件)中的数据存放到缓存中.再次获取的时候 ,直接从缓存中获取.可以提升程序的性能! -
Hibernate框架提供了两种缓存
一级缓存 : 自带的不可卸载的.一级缓存的生命周期与session一致.一级缓存称为session级别的缓存.
二级缓存 : 默认没有开启,需要手动配置才可以使用的.二级缓存可以在多个session中共享数据,二级缓存称为是sessionFactory级别的缓存. -
Session对象的缓存概述
Session接口中,有一系列的java的集合,这些java集合构成了Session级别的缓存(一级缓存).将对象存入到一级缓存中,session没有结束生命周期,那么对象在session中存放着
内存中包含Session实例 --> Session的缓存(一些集合) --> 集合中包含的是缓存对象! -
证明一级缓存的存在,编写查询的代码即可证明
在同一个Session对象中两次查询,可以证明使用了缓存,因为第二次查询的时候控制台没有输出sql语句(前提是要在核心配置文件中启用控制台打印sql语句的功能,如何开启看这里) -
Hibernate框架是如何做到数据发生变化时进行同步操作的呢?
使用get方法查询User对象
然后设置User对象的一个属性,注意:没有做update操作。发现,数据库中的记录也改变了。
利用快照机制来完成的(SnapShot),hibernate在commit事务的时候会将缓存中的数据与快照的数据做对比,如果不一致,则说明需要更新数据库。
事务相关的概念
-
什么是事务
事务就是逻辑上的一组操作,组成事务的各个执行单元,操作要么全都成功,要么全都失败.
转账的例子:冠希给美美转钱,扣钱,加钱。两个操作组成了一个事情! -
事务的特性
原子性 : 事务不可分割.
一致性 : 事务执行的前后数据的完整性保持一致.
隔离性 : 一个事务执行的过程中,不应该受到其他的事务的干扰.
持久性 : 事务一旦提交,数据就永久保持到数据库中. -
如果不考虑隔离性:引发一些读的问题
脏读 : 一个事务读到了另一个事务未提交的数据.
不可重复读 : 一个事务读到了另一个事务已经提交的update数据,导致多次查询结果不一致.
虚读 : 一个事务读到了另一个事务已经提交的insert数据,导致多次查询结构不一致. -
通过设置数据库的隔离级别来解决上述读的问题
未提交读:以上的读的问题都有可能发生.
已提交读:避免脏读,但是不可重复读,虚读都有可能发生.
可重复读:避免脏读,不可重复读.但是虚读是有可能发生.
串行化:以上读的情况都可以避免. -
如果想在Hibernate的框架中来设置隔离级别,需要在hibernate.cfg.xml的配置文件中通过标签来配置
例如:<property name="hibernate.connection.isolation">4</property>
取值:
1—Read uncommitted isolation
2—Read committed isolation(Oracle默认)
4—Repeatable read isolation(Mysql默认)
8—Serializable isolation
丢失更新的问题
如果不考虑隔离性,也会产生写入数据的问题,这一类的问题叫丢失更新的问题。
两个事务同时对某一条记录做修改,就会引发丢失更新的问题。例如:
- A事务和B事务同时获取到一条数据,同时再做修改
- 如果A事务修改完成后,提交了事务
- B事务修改完成后,不管是提交还是回滚,如果不做处理,都会对A操作后的数据产生影响
解决方案有两种
- 悲观锁
采用的是数据库提供的一种锁机制,如果采用做了这种机制,在SQL语句的后面添加 for update 子句
当A事务在操作该条记录时,会把该条记录锁起来,其他事务是不能操作这条记录的。
只有当A事务提交后,锁释放了,其他事务才能操作该条记录
- 乐观锁
采用版本号的机制来解决的。会给表结构添加一个字段version,默认值是0
当A事务在操作完该条记录,提交事务时,会先检查版本号,如果发生版本号的值相同时,才可以提交事务。同时会更新版本号version=1.
当B事务操作完该条记录时,提交事务时,会先检查版本号,如果发现版本不同时,程序会出现错误。
使用Hibernate框架解决丢失更新的问题
-
悲观锁(不建议使用,效率慢)
使用session.get(Customer.class, 1,LockMode.UPGRADE);
-
乐观锁
1.在对应的JavaBean中添加一个属性,名称可以是任意的。
例如:private Integer version;
并 提供get和set方法;2.在映射的配置文件中,提供
<version name="version"/>
标签即可
绑定本地的Session
以前在用jdbc处理事务的时候,需要在业务层使用Connection来开启事务,一种是通过参数的方式传递下去,另一种是把Connection绑定到ThreadLocal对象中
现在的Hibernate框架中,使用session对象开启事务,框架也提供了ThreadLocal的方式,使用步骤有如下:
-
需要在hibernate.cfg.xml的配置文件中提供配置
<property name="hibernate.current_session_context_class">thread</property>
-
使用SessionFactory的
getCurrentSession()
方法来获取当前的Session对象。并且该Session对象不用手动关闭,线程结束了,会自动关闭。
注意:想使用getCurrentSession()方法,步骤1不能少。
下面给个处理事务的service和dao层的代码片段示例:
UserService
public class UserService {
public void save(User u1,User u2){
UserDao dao = new UserDao();
// 获取当前线程session
Session session = HibernateUtils.getCurrentSession();
// 开启事务
Transaction tr = session.beginTransaction();
try {
dao.save1(u1); //添加第一条记录
int a = 10/0; //模拟异常操作
dao.save2(u2); //添加第二条记录
// 提交事务
tr.commit();
} catch (Exception e) {
e.printStackTrace();
// 出现问题:回滚事务
tr.rollback();
}finally{
// Hibernate自己管理释放资源,不用手动关闭session了,线程结束后会自动关闭的!!
}
}
}
UserDao
public class UserDao {
public void save1(User u1){
Session session = HibernateUtils.getCurrentSession();
session.save(u1);
}
public void save2(User u2){
Session session = HibernateUtils.getCurrentSession();
session.save(u2);
}
}
HibernateUtils
public class HibernateUtils {
private static final Configuration CONFIG;
private static final SessionFactory FACTORY;
// 编写静态代码块
static{
// 加载XML的配置文件
CONFIG = new Configuration().configure();
// 构造工厂
FACTORY = CONFIG.buildSessionFactory();
}
/**
* 从工厂中新获取Session对象
* @return
*/
public static Session getSession(){
return FACTORY.openSession();
}
/**
* 从ThreadLocal类中获取到当前线程的session对象
* @return
*/
public static Session getCurrentSession(){
return FACTORY.getCurrentSession();
}
}