什么是事务?
数据库事务(简称:事务)是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。
事务应该具有4个属性:原子性、一致性、隔离性、持久性,这四个属性通常称为ACID特性。
* 原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。
* 一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束。
* 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
* 持久性(Durability):已被提交的事务对数据库的修改应该永久保存在数据库中。
事务的隔离级别
在数据库操作中,为了有效保证并发读取数据的正确性,提出的事务隔离级别。我们的数据库锁,也是为了构建这些隔离级别存在的。
隔离级别 | 脏读(Dirty Read) | 不可重复读(NonRepeatable Read) | 幻读(Phantom Read) |
---|---|---|---|
读未提交(Read uncommitted) | 可能 | 可能 | 可能 |
读已提交(Read committed) | 不可能 | 可能 | 可能 |
可重复读(Repeatable read) | 不可能 | 不可能 | 可能 |
可串行化(Serializable ) | 不可能 | 不可能 | 不可能 |
说明:
* 读未提交(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据
* 读已提交(Read Committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读)
* 可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读
* 串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞
Read Uncommitted这种级别,数据库一般都不会用,而且任何操作都不会加锁,这里就不讨论了。MySQL默认的事务隔离级别为可重复读(Repeated Read)。
一、编程式事务
在 Spring 出现以前,编程式事务管理对开发者来说是唯一选择。熟悉Java JDBC 的人都知道,我们需要在代码中显式调用Connection 的setAutoCommit()、commit()、rollback()等事务管理相关的方法,这就是编程式事务管理。
1、JDBC 的编程式事务管理
public void save(User user) throws SQLException{
Connection conn = datasource.getConnection();
conn.setAutoCommit(false);
try {
PreparedStatement ps = conn.prepareStatement("insert into user(name,age) value(?,?)");
ps.setString(1,user.getName());
ps.setInt(2,user.getAge());
ps.execute();
conn.commit();
} catch (Exception e) {
e.printStackTrace();
conn.rollback();
}finally{
conn.close();
}
}
2、Hibernate 的编程式事务管理
Hibernate 中需要显式调用beginTransaction()、commit()、rollback()等事务管理相关的方法,如下:
public void save(User user){
Session session = hibernateDao.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
session.save(user);
tx.commit();
} catch (Exception e) {
if(tx!=null){
tx.rollback();
}
}finally{
session.close();
}
}
3、MyBatis 的编程式事务管理
MyBatis中,我们可以通过org.apache.ibatis.session.SqlSession
的commit()、rollback() 等事务管理相关的方法,代码如下:
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream in = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = sqlSessionFactory.openSession(false); // 打开会话,事务开始
try {
IUserMapper mapper = session.getMapper(IUserMapper.class);
User user = new User(9, "Test transaction");
int affectedCount = mapper.updateUser(user); // 因后面的异常而未执行commit语句
User user = new User(10, "Test transaction continuously");
int affectedCount2 = mapper.updateUser(user2); // 因后面的异常而未执行commit语句
int i = 2 / 0; // 触发运行时异常
session.commit(); // 提交会话,即事务提交
} catch (Exception e) {
session.rollback(); //回滚
} finally {
session.close(); // 关闭会话,释放资源
}
参考资料
分布式事务系列(1.2)Spring的事务体系:https://yq.aliyun.com/articles/39046?spm=5176.100239.blogcont39044.27.mmXvhw