本章学习目标
- 事务的相关概念
- Hibernate 的隔离级别配置(了解)
- 使用 ThreadLocal 管理 Session(重点)
1. 事务相关概念
典型事务例子:转账
张三 转账 给 李四 1000 元
账户表: t_account
id name money
1 张三 5000
2 李四 5000
转账业务的底层是两条 SQL 语句:
update t_accuont set money=money-1000 where id=1;
发生异常
update t_accuont set money=money+1000 where id=2;
这时,转账业务必须加上事务!!!
1.1 事务的四个特征
原子性:事务要么一起成功,要么一起失败!
一致性:事务操作应该保证数据库的数据操作前后是一致的
隔离性:并发的事务需要相互隔离
持久性:事务一旦提交,数据应该永久保存。
1.2 并发事务会存在的问题
-
脏读: 一个事务读到另一个并发事务的未提交的数据。
-
不可重复读:一个事务读到了另一个并发事务的 update 数据。
-
幻读:一个事务读到了另一个并发事务的 insert 数据。
2. Hibernate 的隔离级别配置(了解)
数据库的隔离级别
数据库的隔离级别是为了防止(脏读、不可重复读、幻读)三种现象:
read uncommited: 不能防止脏读,不可重复读,幻读。
read committed: 防止脏读,不能防止不可重复读,幻读。(Oracle 级别)
repeatable read: 防止脏读,不可重复读,不能防止幻读。(MySQL)
serializeable: 防止脏读,不可重复读,幻读。
Hibernate 通过配置修改数据库的隔离级别(了解)
<!-- 修改hibernate的隔离级别 -->
<!--
read uncommited:1
read committed:2
repeatable read:4
serializeable:8
-->
<property name="hibernate.connection.isolation">4</property>
使用 ThreadLocal 管理 Session(重点)
ThreadLocal 管理 Session 作用
疑问:为什么要使用 ThreadLocal 管理 Session?
在业务层(service)发生错误时,无法回滚事务!!!
dao 层
public class CustomerDao {
public void save(Customer cust){
Session session = HibernateUtil.getSession();
session.save(cust);
session.close();
}
}
service 层
public class CustomerService {
private CustomerDao dao = new CustomerDao();
public void save(Customer c1,Customer c2){
dao.save(c1);
//这时发生错误,想要回滚事务
int i = 100/0;
dao.save(c2);
}
}
测试
@Test
public void test1(){
Customer c1 = new Customer();
c1.setName("张全蛋");
Customer c2 = new Customer();
c2.setName("李四");
CustomerService service = new CustomerService();
service.save(c1, c2);
}
方式一
配置 HIberante 的 ThreadLocal
修改 hibernate.cfg.xm
<!-- 让session被TheadLocal管理 -->
<property name="hibernate.current_session_context_class">thread</property>
修改 Session 获取方法
修改工具类中 SessionFactory 获取 session 的方法
session = sessionFactory.getCurrentSession();
方式二
工具类重新编写
/**
* Description:
*
* @author 郭新
*/
public class HibernateUtil {
private static SessionFactory sessionFactory;
static
{
try{
Configuration configuration=new Configuration().configure();
sessionFactory = configuration.buildSessionFactory();
}catch (Throwable ex){
throw new ExceptionInInitializerError(ex);
}
}
private static final ThreadLocal<Session> threadLocal = new ThreadLocal<Session>();
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
public static Session getSession() throws HibernateException
{
Session session = (Session) threadLocal.get();
if (session == null){
session = sessionFactory.openSession();
threadLocal.set(session);
}
return session;
}
public static void closeSession() throws HibernateException {
Session session = (Session) threadLocal.get();
if (session != null)
session.close();
threadLocal.set(null);
}
public static void shutdown(){
getSessionFactory().close();
}
}
修改 dao 和 service 代码
dao 层
public class CustomerDao {
public void save(Customer cust){
Session session = HibernateUtil.getSession();
session.save(cust);
// 不能关闭session
// HibernateUtil.closeSession();
}
}
service 层
public class CustomerService { private CustomerDao dao = new CustomerDao();; public void save(Customer c1,Customer c2){ // 在业务层开启 session Session session = HibernateUtil.getSession(); //开启事务 Transaction tx = session.beginTransaction(); try { dao.save(c1); // 这时发生错误 // int i = 100/0; dao.save(c2); // 切记不可写错位置 tx.commit(); } catch (Exception e) { // 回滚事务 tx.rollback(); e.printStackTrace(); } }}
测试代码
@Test public void test1(){ Customer c1 = new Customer(); c1.setName("张全蛋"); Customer c2 = new Customer(); c2.setName("李四"); CustomerService service = new CustomerService(); service.save(c1, c2); }