Spring 的事务抽象
核心接口
PlatformTransactionManager
- DataSourceTransactionManager
- HibernateTransactionManager
- JtaTransactionManager
TransactionDefinition
- Propagation
定义传播特性 - Isolation
定义隔离性 - Timeout
定义事务超时的设置 - Read-only status
定义事务是不是只读的
事务的传播特性
Spring中通过Propagation来设置事务的传播属性。
事务隔离特性
脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。
不可重复读是指在对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,被另一个事务修改并提交了。
幻读是事务非独立执行时发生的一种现象。
幻读和不可重复读都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。
编程式事务
TransactionTemplate
- TransactionCallback
有返回值的,使用这个方法 - TransactionCallbackWithoutResult
没有返回值的,使用这个方法
此外,可以定义传播特性、隔离性、事务超时时间和事务是不是只读的
PlatformTransactionManager
- 可以传入TransactionDefinition进行定义
下面,我们用一个例子来看一下。
package com.example.demo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import javax.sql.rowset.spi.TransactionalWriter;
/**
* @Author: 13679
* @CreateTime: 2019-10-13 20:23
*/
@Slf4j
@SpringBootApplication
public class test implements CommandLineRunner {
@Autowired
private TransactionTemplate transactionTemplate;
@Autowired
private JdbcTemplate jdbcTemplate;
public static void main(String[] args) {
SpringApplication.run(test.class,args);
}
@Override
public void run(String... args) throws Exception {
log.info("count before transaction:{}",getCount());//事务开始前打印count
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
jdbcTemplate.execute("INSERT INTO FOO (ID, BAR) VALUES (1, 'aaa')");
log.info("COUNT IN TRANSACTION: {}", getCount());//事务开始中打印count
transactionStatus.setRollbackOnly();
}
});
log.info("COUNT AFTER TRANSACTION: {}", getCount());//事务开始后打印count
}
private long getCount() {
return (long)jdbcTemplate.queryForList("SELECT COUNT(*) AS CNT FROM FOO").get(0).get("CNT");
}
}
程序运行结果如下:
因为,我们使用了transactionStatus.setRollbackOnly()方法,意思是事务回滚。所以,在没有执行事务时,个数为0,执行事务中,插入了一条语句。故为1,然后事务回滚,个数又变成了0.
声明式事务
大致流程如下:
基于注解的配置方式
开启事务注解的方式
- @EnableTransactionManagement
在代码中加这个注解 - <tx:annotation-driven/>
加在xml文件中
它的一些配置
- proxyTargetClass
我当前做的AOP是基于接口,还是基于类的(参数:true or false) - mode
一般默认使用java - order
指定事务aop拦截的顺序
@Transactional
- transactionManager
支持各种数据访问框架的事务管理 - propagation
设置传播性 - isolation
设置隔离等级 - timeout
设置超时时间 - readOnly
设置是否只读 - 怎么判断回滚
设置在碰到特定的异常类的时候,才去回滚
我们用一个详细的例子说明:
package geektime.spring.data.simplejdbcdemo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* @Author: 13679
* @CreateTime: 2019-10-15 21:32
*/
@SpringBootApplication
@EnableTransactionManagement(mode = AdviceMode.PROXY)
@Slf4j
public class DeclarativeTransactionDemoApplication implements CommandLineRunner {
@Autowired
private FooService fooService;
@Autowired
private JdbcTemplate jdbcTemplate;
public static void main(String[] args) {
SpringApplication.run(DeclarativeTransactionDemoApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
fooService.insertRecord();
log.info("AAA {}",
jdbcTemplate
.queryForObject("SELECT COUNT(*) FROM FOO WHERE BAR='AAA'", Long.class));
try {
fooService.insertThenRollback();
} catch (Exception e) {
log.info("BBB {}",
jdbcTemplate
.queryForObject("SELECT COUNT(*) FROM FOO WHERE BAR='BBB'", Long.class));
}
try {
fooService.invokeInsertThenRollback();
} catch (Exception e) {
log.info("BBB {}",
jdbcTemplate
.queryForObject("SELECT COUNT(*) FROM FOO WHERE BAR='BBB'", Long.class));
}
}
}
package geektime.spring.data.simplejdbcdemo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.UnexpectedRollbackException;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.RollbackRuleAttribute;
/**
* @Author: 13679
* @CreateTime: 2019-10-15 21:25
*/
@Component
public class FooServiceImpl implements FooService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
@Transactional
public void insertRecord() {
jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('AAA')");
}
@Override
@Transactional(rollbackFor = RollbackException.class)
public void insertThenRollback() throws RollbackException {
jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('BBB')");
throw new RollbackException();
}
@Override
public void invokeInsertThenRollback() throws RollbackException {
insertThenRollback();
}
}
package geektime.spring.data.simplejdbcdemo;
/**
* @Author: 13679
* @CreateTime: 2019-10-15 21:23
*/
public interface FooService {
void insertRecord();
void insertThenRollback() throws RollbackException;
void invokeInsertThenRollback() throws RollbackException;
}
package geektime.spring.data.simplejdbcdemo;
/**
* @Author: 13679
* @CreateTime: 2019-10-15 21:31
*/
public class RollbackException extends Exception {
}
运行结果如下:
因为调用invokeInsertThenRollback()方法时,没有事务的产生,即没有@Transactional注解。所以,事务的回滚是无效的。它只执行了 jdbcTemplate.execute(“INSERT INTO FOO (BAR) VALUES (‘BBB’)”);