大家好呀,我是小笙,本节我分享一些事务有关的知识点
编程式事务
Connection connection = JdbcUtils.getConnection();
try {
//1. 先设置事务不要自动提交
connection.setAutoCommint(false);
//2. 进行各种多个表的操作
// ...
//3. 提交
connection.commit();
} catch (Exception e) {
//4. 回滚
conection.rollback();
}
优点:简单,好理解
缺点::代码冗余,效率低,不利于扩展
声明式事务
需要配置声明式注解
我们如果需要 service 中某个方法执行事务的语句加上注解即可
@Transactional
public void serviceMethod(){}
底层依旧是走了编程式事务的那一套,只是用 AOP 的方式嵌入事务开启、事务提交以及事务回滚
// dataSourceTransactionManager 底层源码
protected void doBegin(Object transaction, TransactionDefinition definition) {
// ......
// 核心代码 不开启自动提交事务
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
con.setAutoCommit(false);
}
// ......
}
// 提交事务
protected void doCommit(DefaultTransactionStatus status) {
DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
this.logger.debug("Committing JDBC transaction on Connection [" + con + "]");
}
try {
con.commit();
} catch (SQLException var5) {
throw this.translateException("JDBC commit", var5);
}
}
// 回滚事务
protected void doRollback(DefaultTransactionStatus status) {
DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
this.logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
}
try {
con.rollback();
} catch (SQLException var5) {
throw this.translateException("JDBC rollback", var5);
}
}
优点:无代码冗余,效率高,扩展方便
缺点:理解较困难
最主要的是 REQUIRED 和 REQUIRES_NEW,主要区别就是在当前运行的事务包裹的情况下是在该事务内部运行还是创建独立的事务运行
传播属性 | 描述 |
---|---|
*REQUIRED(默认) | 如果有事务在运行,当前的方法就在这个事务内运行;否则,就启动一个新的事务,并在自己的事务内运行 |
*REQUIRES_NEW | 当前的方法必须启动新事务,并在它自己的事务内运行;如果有事务正在运行,应该将它挂起 |
SUPPORTS | 如果有事务在运行,当前的方法就在这个事务内运行;否则它可以不运行在事务中 |
NOT_SUPPORTEI | 当前的方法不应该运行在事务中,如果有运行的事务,将它挂起 |
MANDATORY | 当前的方法必须运行在事务内部,如果没有正在运行的事务,就抛出异常 |
NEVER | 当前的方法不应该运行在事务中,如果有运行的事务,就抛出异常 |
NESTED | 如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运 行;否则,就启动一个新的事务,并在它自己的事务内运行 |
操作
// 注解分析
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
@AliasFor("transactionManager")
String value() default "";
@AliasFor("value")
String transactionManager() default "";
String[] label() default {};
Propagation propagation() default Propagation.REQUIRED; // 来设置哪种传播属性
Isolation isolation() default Isolation.DEFAULT;
int timeout() default -1;
String timeoutString() default "";
boolean readOnly() default false;
Class<? extends Throwable>[] rollbackFor() default {};
String[] rollbackForClassName() default {};
Class<? extends Throwable>[] noRollbackFor() default {};
String[] noRollbackForClassName() default {};
}
// 实际代码
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void serviceMethod(){}
Java 默认的隔离级别, 就是 mysql 数据库默认的隔离级别 一般为 REPEATABLE_READ
隔离级别 | 脏读 | 不可重复读 | 幻读 | 加锁读 |
---|---|---|---|---|
读未提交 | 会发生 | 会发生 | 会发生 | 不加锁 |
读已提交 | 不会发生 | 会发生 | 会发生 | 不加锁 |
可重复读 | 不会发生 | 不会发生 | 不会发生 | 不加锁 |
可串行化 | 不会发生 | 不会发生 | 不会发生 | 加锁 |
不可重复读和幻读的区别
可以理解为:不可重复读重点在于update和delete (只用行锁),而幻读的重点在于insert (必须表级锁)
查看数据库默认的隔离级别
SELECT @@global.tx_isolation;
格式
@Transactional(isolation = Isolation.READ_COMMITTED)
public void serviceMethod(){}
如果一个事务执行的时间超过某个时间限制,就让该事务回滚(单位:秒)
格式
// 通过抛出异常的形式,超时发生事务回滚
@Transactional(timeout = 2)
public void serviceMethod(){}