1. 事务相关的概念
1、什么是事务
- 事务就是逻辑上的一组操作,组成事务的各个执行单元,操作要么全都成功,要么全都失败.
- 转账的例子:用户1给用户2转钱,包含了扣钱,加钱两个操作,这两个操作组成了一个事情!
2、事务的特性
- 原子性 – 事务不可分割.
- 一致性 – 事务执行的前后数据的完整性保持一致.
即:用户1给用户2转钱,用户1必须扣钱,用户2必须加钱,是对等的。
- 隔离性 – 一个事务执行的过程中,不应该受到其他的事务的干扰.
- 持久性 – 事务一旦提交,数据就永久保持到数据库中.
3、如果不考虑隔离性,会引发一些读的问题
- 脏读 – 一个事务读到了另一个事务未提交的数据
即:用户1给用户2转钱,用户1扣钱动作修改了(update)但还未提交(commit),而用户2尝试读取(get)该数据。
- 不可重复读 – 一个事务读到了另一个事务已经提交的 update 数据,导致多次查询结果不一致.
即:用户1给用户2转钱,用户1扣钱动作已经提交了,用户2也读取(get)该数据一次,接着用户1又执行第二次转钱,第二次扣钱动作已经提交了,用户2第二次读取(get)该数据,从而两次读取的数据不一样。
- 虚读 – 一个事务读到了另一个事务已经提交的 insert 数据,导致多次查询结构不一致.
即:假设一张数据表中原有14条记录,用户查询第10条记录的过程中,数据表又插入了一条数据,导致用户查询的第10条记录与预想的不匹配。
4、通过设置数据库的隔离级别来解决上述读的问题
- 未提交读:以上的读的问题都有可能发生
- 已提交读:避免脏读,但是不可重复读,虚读都有可能发生
- 可重复读:避免脏读,不可重复读.但是虚读是有可能发生
- 串行化:以上读的情况都可以避免(在读取表的时候,锁定表数据,不允许插入,但是就会变成单线程操作,最耗资源)
5、如果想在 Hibernate 的框架中来设置隔离级别,需要在hibernate.cfg.xml
的配置文件中通过标签来配置
通过:hibernate.connection.isolation = 4 来配置
1 — Read uncommitted isolation
2 — Read committed isolation
4 — Repeatable read isolation
8 — Serializable isolation
2. 绑定本地的Session
1、 JavaWEB 事务中,需要在业务层使用 Connection 来开启事务
- 第一种是通过参数的方式传递下去
- 第二种是把 Connection 绑定到 ThreadLocal 对象中
2、现在的 Hibernate 框架中,使用 session 对象开启事务,所以需要来传递session 对象,框架提供了 ThreadLocal 的方式
- 需要在 hibernate.cfg.xml 的配置文件中提供配置
<property name="hibernate.current_session_context_class">thread</property>
ThreadLocal方法:创建一个线程本地变量,是一个键值对结构类型的数据:<k,v>
set(v)放入一个值进去
k:当前线程名称
v:session
- 修改 HibernateUtil 的工具类, 使用 SessionFactory 的getCurrentSession()方法,获取当前的 Session 对象。并且该 Session 对象不用手动关闭, 线程结束了,会自动关闭。
public static Session getCurrentSession(){
return factory.getCurrentSession();
}
//注意:想使用 getCurrentSession()方法,必须要先配置才能使用
![事务图解](https://i-blog.csdnimg.cn/blog_migrate/64906feb454755e482fb249f9cb7c0e3.jpeg)
3. 绑定本地 session 示例
新建项目(Hibernate5_d02_c04)
基于Hibernate5_d02_c03项目选取必要文件
1、修改配置文件
/Hibernate5_d02_c04/src/hibernate.cfg.xml
程序代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
...
<!-- 把session绑定到当前线程中,使用getCurrentSession()获取当前session -->
<property name="hibernate.current_session_context_class">thread</property>
<!-- 映射文件 -->
<mapping resource="/hibernate/domain/User.hbm.xml"/>
</session-factory>
</hibernate-configuration>
2、修改 HibernateUtil 的工具类,使用 SessionFactory 的 getCurrentSession()方法,获取当前的 Session 对象。
/Hibernate5_d02_c04/src/hibernate/util/HibernateUtils.java
程序代码如下:
...
public class HibernateUtils {
...
//session绑定到线程
//这种方式获取的session不用手动关闭,线程结束了会自动关闭
public static Session getCurrentSession(){
return FACTORY.getCurrentSession();
}
}
3、新建UserDao类
/Hibernate5_d02_c04/src/hibernate/dao/UserDao.java
程序代码如下:
package hibernate.dao;
import org.hibernate.Session;
import hibernate.domain.User;
import hibernate.util.HibernateUtils;
public class UserDao {
public void save1(User user1){
//获取 session 对象
Session session = HibernateUtils.getCurrentSession();
//不需要开启事务,事务在sevice层开启
session.save(user1);
}
public void save2(User user2){
//获取 session 对象
Session session = HibernateUtils.getCurrentSession();
//不需要开启事务,事务在sevice层开启
session.save(user2);
}
}
4、新建UserServicer类
/Hibernate5_d02_c04/src/hibernate/service/UserService.java
程序代码如下:
package hibernate.service;
import org.hibernate.Session;
import org.hibernate.Transaction;
import hibernate.dao.UserDao;
import hibernate.domain.User;
import hibernate.util.HibernateUtils;
public class UserService {
/*
* 使用事务的方式
*/
public void save(User user1,User user2){
//先获取到 session 对象,session 会创建,会把 session 绑定到当前线程上
Session session = HibernateUtils.getCurrentSession();
//开启事务
Transaction tr = session.beginTransaction();
try {
//调用 dao 层
UserDao dao = new UserDao();
dao.save1(user1);
dao.save2(user2);
//提交事务
tr.commit();
} catch (Exception e) {
//回滚事务
tr.rollback();
}finally {
//释放资源,当前代码完成后,当前线程结束,session会自动关闭,不要手动close
}
}
}
5、新建TestUser类
/Hibernate5_d02_c04/src/hibernate/test/UserTest.java
程序代码如下:
package hibernate.test;
import org.junit.Test;
import hibernate.domain.User;
import hibernate.service.UserService;
public class UserTest {
@Test
public void testUser(){
User u1 = new User();
User u2 = new User();
u1.setName("熊大");
u2.setName("熊二");
UserService us = new UserService();
us.save(u1, u2);
}
}
6、测试查看效果
![运行效果](https://i-blog.csdnimg.cn/blog_migrate/4ddacaebbde574e2bb133527bd1cd31b.jpeg)
运行效果