第五章 Hibernate 事务管理

本章介绍了Hibernate事务管理的重要性和事务的四个特征:原子性、一致性、隔离性和持久性。讨论了并发事务可能引发的脏读、不可重复读和幻读问题,并概述了数据库的四种隔离级别。重点讲解了使用ThreadLocal来管理Session,以解决业务层错误导致的事务回滚问题,包括两种实现方式及其在DAO和Service层的代码修改。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本章学习目标

  1. 事务的相关概念
  2. Hibernate 的隔离级别配置(了解)
  3. 使用 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);	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值