一、事务简介
1、概述
1、事务在逻辑上一组操作,要么都执行(成功),要么都不执行(失败),主要是针对数据库而言的,比如MySQL、Oracle等。
2、事务是数据库提供的特性,因此可以直接通过操作数据库来操作事务,但是对Java开发来说,更多的是使用现成的Java事务API来管理事务,这些API都非常的底层。Java的API都是通过数据库连接(Connection)来操作数据库的,Connection本质上就是一个项目到数据库的Socket连接,建立连接之后就能向数据库发送各种操作指令;事务的管理也是基于连接,并且一个事务对应一个数据库连接
。
2、事务的四个重要特性
1、原子性(Atomicity)
:一个事务中的所有操作,要么全部成功,要么全部失败,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
2、一致性(Consistency)
:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。
3、隔离性(Isolation)
:数据库允许多个并发事务同时对其数据进行读写和修改,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。
4、持久性(Durability)
:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
3、Spring事务的好处
1、Spring提供了一种更加简单并且强大的的方式对事务管理进行了支持,是基于AOP机制实现的。
2、好处:
-
能与Spring数据访问抽象完美集成
,Spring数据访问抽象定义了访问数据库的标准,通过此可以引入其他的数据访问框架。
-
为不同的访问框架的事务管理提供了一致的抽象编程模型
,比如JDBC、Hibernate、MyBatis等,它们都有自己的控制事务的方式,但是Spring数据访问抽象能够与这些框架无缝集成,Spring事务管理又与数据访问抽象完美集成,那么当Spring中引入集成这些框架后,可以统一使用Spring提供的API或者声明方式来管理。
-
提供了声明式事务管理和编程式事务管理两种方式
,大多数使用声明式事务管理。
-
支持全局事务
,(Global Transactions,也就是分布式事务,单个操作涉及多个不同的数据源/数据库)和本地事务(Local Transactions,单个操作设计一个数据源/数据库)的所有功能。
二、Spring事务管理的核心API
1、概述
1、Spring将事务管理的核心抽象为一个事务管理器TransactionManager
,该接口只有一个简单的接口定义,属于一个标记接口,没有任何方法可以实现,如果实现了该接口,那么就标志着该类属于一个事务管理器。
2、该接口存在两个重要的子接口:
-
org.springframework.transaction.PlatformTransactionManager
:命令式事务管理的核心接口,
-
org.springframework.transaction.ReactiveTransactionManager
:响应事务管理的核心接口,这是Spring 5.2新增的功能,用于配合Spring Data MongoDB 2.2和Spring Data R2DBC 1.0依赖。
2、PlatformTransactionManager事务管理器
1、PlatformTransactionManager是命令式事务管理器接口。是一个服务提供方接口,采用的是Service Provider Interface
服务注册发现机制,即SPI,这是一种JDK自带的服务发现机制,在这里用于实现事务管理器服务的扩展发现。
2、它虽然是一个服务提供方接口,但是spring-tx仍然提供了一些默认实现,常见的实现有:
-
org.springframework.transaction.jta.JtaTransactionManager
:该事务管理器适用于处理分布式事务,即跨多个资源的事务,以及一般控制应用程序服务器资源(例如JNDI中可用的JDBC数据源)上的事务。
-
org.springframework.jdbc.datasource.DataSourceTransactionManager
:该事务管理器能够在任何环境中使用任何JDBC驱动程序工作。该事务管理器操作的数据源需要返回独立的连接。 连接可能来自池(数据库连接池),但数据源不得返回线程范围/请求范围的连接等。此事务管理器将根据指定的传播行为将连接与线程绑定事务本身相关联。它假定即使在正在进行的事务期间也可以获得单独的独立连接。使用Spring JDBC或者MyBatis进行数据库访问和操作时使用。
3、PlatformTransactionManager类中主要包括事务的提交的方法commit,回滚的方法rollback,以及根据传递的事务定义TransactionDefinition获取一个事务状态对象TransactionStatus的方法getTransaction,实际上获取的事务状态对象就可以看作事务对象本身
。
public interface PlatformTransactionManager extends TransactionManager {
TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
3、TransactionStatus事务状态
1、TransactionStatus是一个事务的状态接口,提供了一些控制事务执行和查询事务状态的方法。实际上TransactionStatus对象就代表一个事务对象。
2、常用方法:
方法名 | 说明 |
---|
boolean hasSavepoint() | 返回此事务是否在内部带有保存点,这是实现嵌套事务回滚特性的关键 |
boolean isNewTransaction() | 返回当前事务是否为新事务 |
void setRollbackOnly() | 设置仅事务回滚,这指示事务管理器,事务的唯一可能的结果可能是回滚。该方法可以用在某些不能抛出异常的地方,作为引发异常的替代方法,因为异常会触发回滚。 |
boolean isRollbackOnly() | 返回事务是否标记为仅回滚 |
boolean isCompleted() | 返回事务是否已完成,即是否已提交或回滚 |
Object createSavepoint() | 创建新的保存点,可以通过rollbackToSavepoint回滚到特定的保存点 |
void rollbackToSavepoint(Object savepoint) | 回滚到给定的保存点 |
void releaseSavepoint(Object savepoint) | 显式释放给定的保存点,大多数事务管理器将在事务完成时自动释放保存点 |
public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {
boolean hasSavepoint();
void flush();
boolean isNewTransaction();
void setRollbackOnly();
boolean isRollbackOnly();
boolean isCompleted();
Object createSavepoint() throws TransactionException;
void rollbackToSavepoint(Object savepoint) throws TransactionException;
void releaseSavepoint(Object savepoint) throws TransactionException;
}
4、TransactionDefinition事务定义
1、TransactionDefinition是一个定义了符合Spring标准的事务属性的接口,内部具有获取各种Spring事务属性的方法,并且定义了以下几种属性常量:
-
PROPAGATION
:事务的传播行为。 通常,事务范围内的所有代码都在该事务中运行。但是,如果事务上下文已存在,则可以指定传播行为。例如,代码可以在现有事务(常见情况下)中继续运行,也可以挂起现有事务并创建新的事务。
-
ISOLATION
:事务的隔离级别。 用来表示事务与事务之间的工作隔离的程度。例如:某事务能否查到来自其他事务的未提交的写入。
-
TIMEOUT
:超时时间。 事务在超时之前运行多长时间,超时将自动回滚;Spring事务超时时间 = 事务开始时到最后一个Statement创建时时间 + 最后一个Statement的执行完的时间
。
-
Read-only status
:只读状态。 当代码中只有读取没有修改数据时,可以使用只读事务。
public interface TransactionDefinition {
int PROPAGATION_REQUIRED = 0;
int PROPAGATION_SUPPORTS = 1;
int PROPAGATION_MANDATORY = 2;
int PROPAGATION_REQUIRES_NEW = 3;
int PROPAGATION_NOT_SUPPORTED = 4;
int PROPAGATION_NEVER = 5;
int PROPAGATION_NESTED = 6;
int ISOLATION_DEFAULT = -1;
int ISOLATION_READ_UNCOMMITTED = 1;
int ISOLATION_READ_COMMITTED = 2;
int ISOLATION_REPEATABLE_READ = 4;
int ISOLATION_SERIALIZABLE = 8;
int TIMEOUT_DEFAULT = -1;
default int getPropagationBehavior() {
return PROPAGATION_REQUIRED;
}
default int getIsolationLevel() {
return ISOLATION_DEFAULT;
}
default int getTimeout() {
return TIMEOUT_DEFAULT;
}
default boolean isReadOnly() {
return false;
}
@Nullable
default String getName() {
return null;
}
static TransactionDefinition withDefaults() {
return StaticTransactionDefinition.INSTANCE;
}
}
三、编程式事务
1、概述
1、编程式事务就是在业务代码中手动调用事务管理相关的方法进行事务管理,这样的好处是事务控制的粒度非常细,可以精确到代码行级别;但是代码耦合度高。
2、Spring框架提供了两种编程式事务管理方式:
-
使用上层的TransactionTemplate或者TransactionalOperator。
-
直接使用TransactionManager的实现。
3、对上述两种方式的:
-
Spring项目组推荐编程式事务管理使用TransactionTemplate
,对于响应式代码则推荐使用TransactionalOperator
,这两个类是一个模板类,提供了统一的API。
-
第二种方式较为复杂,需要使用PlatformTransactionManager、TransactionDefinition和TransactionStatus这三个对象的各自的实现类来进行事务管理。
2、TransactionTemplate方式配置
1、TransactionTemplate相当于事务模版,它使用回调方法机制,简化、封装了原始事务方法的调用,不需要显示地进行开始事务、提交事务等方法的手动调用,业务代码只关注业务逻辑即可。
2、要想使用事务模板类,必须要传递一个事务管理器类,TransactionTemplate底层仍然是依靠事务管理器来实现事务管理的。
3、可以在TransactionTemplate上指定事务属性,如传播行为,隔离级别,超时等。默认情况下,TransactionTemplate实例具有默认的事务属性,来自于它的父类DefaultTransactionDefinition
。
3、核心方法execute(TransactionCallback<T> action:
-
该方法传递一个TransactionCallback对象作为参数,TransactionCallback是一个函数式接口,可以使用匿名对象,也可以使用lambda表达式,方法doInTransaction中包含需要在事务上下文中运行的业务代码,并且支持返回值
。
-
该方法会自动开启、提交或回滚事务
。
-
默认情况下,如果抛出非受检异常(RuntimeException)或Error异常则会回滚事务,抛出其他受检异常或方法执行成功则会提交事务
。
-
使用该方法时不允许直接抛出受检异常,而是必须处理!对于受检异常,可以使用doInTransaction方法传递的参数TransactionStatus(当前事务对象),可以调用它的setRollbackOnly方法手动设置该事务的唯一结果就回滚,用来代替抛出异常
。
4、配置依赖说明:
-
引入spring-jdbc依赖,直接使用Spring提供的JdbcTemplate来访问数据库,不再引入其他数据库框架。
-
spring-jdbc中包含了spring-tx的依赖是用于支持Spring事务管理,当然也可以单独引入。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
1、新建配置文件(druid.properties)用于存储数据库连接信息
url=jdbc:mysql://localhost:3306/learning
className=com.mysql.cj.jdbc.Driver
userName=root
password=98072428
2、配置类
@Configuration
@ComponentScan("com.itan.transactional.*")
@PropertySource(value = "classpath:druid.properties", encoding = "UTF-8")
public class DataSourceConfig {
@Value("${url}")
private String url;
@Value("${className}")
private String className;
@Value("${userName}")
private String userName;
@Value("${password}")
private String password;
@Bean
public DruidDataSource druidDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(url);
dataSource.setDriverClassName(className);
dataSource.setUsername(userName);
dataSource.setPassword(password);
return dataSource;
}
@Bean
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(druidDataSource());
}
@Bean
public DataSourceTransactionManager transactionManager() {
return new DataSourceTransactionManager(druidDataSource());
}
@Bean
public TransactionTemplate transactionTemplate() {
return new TransactionTemplate(transactionManager());
}
}
3、正常测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = DataSourceConfig.class)
public class Test1 {
@Resource
private JdbcTemplate jdbcTemplate;
@Resource
private TransactionTemplate transactionTemplate;
@Test
public void test01() {
Integer res1 = transactionTemplate.execute(status -> {
return insert("张三", 20);
});
System.out.println("使用lambda表达式返回结果:" + res1);
Integer res2 = transactionTemplate.execute(new TransactionCallback<Integer>() {
@Override
public Integer doInTransaction(TransactionStatus status) {
return insert("李四", 25);
}
});
System.out.println("使用匿名内部类方式返回结果:" + res2);
}
private int insert(String name, int age) {
String sql = "insert into users (name, age) values (?,?)";
return jdbcTemplate.update(sql, name, age);
}
}
4、异常测试
-
对于非受检异常(RuntimeException)或Error异常则会回滚事务。
-
对于受检异常可以通过setRollbackOnly设置事务唯一结果为回滚进行处理。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = DataSourceConfig.class)
public class Test1 {
@Resource
private JdbcTemplate jdbcTemplate;
@Resource
private TransactionTemplate transactionTemplate;
@Test
public void test02() {
Integer res1 = transactionTemplate.execute(status -> {
return insert2("王五", 20);
});
System.out.println("使用lambda表达式返回结果:" + res1);
}
private int insert2(String name, int age) {
String sql = "insert into users (name, age) values (?,?)";
int result = jdbcTemplate.update(sql, name, age);
int i = 1 / 0;
return result;
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = DataSourceConfig.class)
public class Test1 {
@Resource
private JdbcTemplate jdbcTemplate;
@Resource
private TransactionTemplate transactionTemplate;
@Test
public void test03() {
Integer res1 = transactionTemplate.execute(status -> {
try {
return insert3("王五", 20);
} catch (IOException e) {
status.setRollbackOnly();
e.printStackTrace();
return null;
}
});
System.out.println("使用lambda表达式返回结果:" + res1);
}
private int insert3(String name, int age) throws IOException {
String sql = "insert into users (name, age) values (?,?)";
int result = jdbcTemplate.update(sql, name, age);
throw new IOException();
}
}
3、TransactionManager方式配置
1、使用TransactionManager进行事务管理,需要PlatformTransactionManager、TransactionDefinition和TransactionStatus这三个对象搭配使用。
2、通过PlatformTransactionManager的getTransaction方法根据传入的事务定义TransactionDefinition对象获取一个事务状态对象TransactionStatus,并且事务的开启、提交、回滚都需要手动控制,Spring不会自动控制
。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = DataSourceConfig.class)
public class Test2 {
@Resource
private JdbcTemplate jdbcTemplate;
@Autowired
private DataSourceTransactionManager transactionManager;
@Test
public void test01() {
DefaultTransactionDefinition definition = null;
TransactionStatus status = null;
try {
definition = new DefaultTransactionDefinition();
definition.setName("userAdd");
definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
status = transactionManager.getTransaction(definition);
int res = insert1("王五", 30);
System.out.println("受影响行数:" + res);
transactionManager.commit(status);
} catch (Exception e) {
e.printStackTrace();
transactionManager.rollback(status);
}
}
private int insert1(String name, int age) {
String sql = "insert into users (name, age) values (?,?)";
int result = jdbcTemplate.update(sql, name, age);
return result;
}
}
四、基于XML的声明式事务
1、声明式事务概述
1、声明式事务就是通过配置的方式(XML或注解)告诉Spring,哪些方法需要Spring来管理事务。
2、声明式事务是基于Spring AOP实现的,对于使用AOP声明式配置的Bean,将会生成一个AopProxy代理对象,当调用事务方法时,实际上是通过代理对象去调用的,方法前后进行拦截(TransactionInterceptor),然后通过TransactionManager在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务
。
![在这里插入图片描述](https://img-blog.csdnimg.cn/7ab59424899e412c9938921d8952d775.png#pic_center)
3、Spring声明式事务优点:不需要通过编程的方式管理事务,业务逻辑代码中不掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。
4、Spring声明式事务缺点:
-
AOP的机制导致了声明式事务只能进行方法级别的事务管理,而编程式事务则可以精确到代码行级别
。
-
AOP的机制导致了同一个AOP类中的事务方法相互调用时,被调用方法的事务配置不会生效,原因是Spring AOP的代理机制最终还是通过原始目标对象本身去调用目标方法的,这样被调用的方法就会因为是原始对象调用的,而不被拦截
。
-
无论是基于JDK动态代理还是CGLIB代理,由于本身的缺陷,它们代理的方法增强都具有限制。对于JDK的代理,目标类必须实现符合规则的接口,并且只能代理实现的接口的方法;对于CGLIB的代理,目标类不能是final
修饰的,并且被代理的方法也不能是private/final/static
的。
2、配置依赖
1、依赖配置和上面保持一致,由于需要使用到切入点表达式,因此还需要引入aspectjweaver的依赖。
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
3、业务类
@Data
@NoArgsConstructor
public class User {
private Integer id;
private String name;
private Integer age;
private Date createTime;
private Date updateTime;
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
}
package com.itan.transactional.xml.service;
import com.itan.transactional.entity.User;
import java.util.List;
public interface UserService {
int insertUser(User user);
List<User> getAllUsers();
}
package com.itan.transactional.xml.service.impl;
import com.itan.transactional.entity.User;
import com.itan.transactional.xml.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.transaction.support.DefaultTransactionStatus;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public int insertUser(User user) {
DefaultTransactionStatus transactionStatus = (DefaultTransactionStatus) TransactionAspectSupport.currentTransactionStatus();
System.out.println("insertUser method Transaction isReadOnly: " + transactionStatus.isReadOnly());
String sql = "insert into users (name, age) values (?,?)";
return jdbcTemplate.update(sql, user.getName(), user.getAge());
}
@Override
public List<User> getAllUsers() {
DefaultTransactionStatus transactionStatus = (DefaultTransactionStatus) TransactionAspectSupport.currentTransactionStatus();
System.out.println("getAllUsers method Transaction isReadOnly: " + transactionStatus.isReadOnly());
String sql = "select * from users";
List<User> list = jdbcTemplate.query(sql, BeanPropertyRowMapper.newInstance(User.class), null);
return list;
}
}
4、事务配置文件及事务标签
1、基于XML的声明式事务是依靠tx命名空间标签和aop命名空间标签完成的,因此需要引入两个命名空间;还需要使用到与事务相关的标签<tx:advice>、<tx:attributes>
等组合。
2、<tx:advice>
标签:
-
事务通知标签,可以定义许多方法的事务语义(包括:传播行为、隔离级别、回滚规则等);该标签可以配置多个,可以使用不同的事务管理器。
-
==id属性:==为advice bean的标识。
-
==transaction-manager属性:==关联一个事务管理器,默认查找id/name为“transactionManager”的事务管理器,因此如果配置的事务管理器bean的id/name就是transactionManager,那么该属性可以省略。
3、<tx:attributes>
标签:是一个标签集,内部可以配置多个<tx:method>
标签。
4、<tx:method>
标签:
-
声明式事务的核心配置标签,该标签用于配置某个或某些方法的事务属性,这些方法是从切入点表达式aop:pointcut匹配到的,具有以下可配置属性:
名称 | 必填 | 默认值 | 说明 |
---|
name | Y | | 表示与该的事务属性关联的方法名称,可以使用通配符*表示所有 |
propagation | N | REQUIRED | 指定事务的传播行为,默认值对应PROPAGATION_REQUIRED |
isolation | N | DEFAULT | 指定事务的隔离级别,默认值对应ISOLATION_DEFAULT |
timeout | N | -1 | 事务超时时间,单位秒,默认值为-1(永不超时) |
read-only | N | false | 事务只读状态 |
rollback-for | N | | 指定将触发回滚的异常,多个使用","分隔;不指定则使用默认策略 |
no-rollback-for | N | | 指定不会触发回滚的异常,多个使用","分隔;不指定则使用默认策略 |
5、默认回滚策略:执行时如果抛出RuntimeException(运行时异常,非受检异常)和Error及其它们的子类异常时,将会回滚。如果抛出其他类型的异常,比如受检异常,那么不会回滚而是提交事务。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:property-placeholder location="classpath:druid.properties" file-encoding="UTF-8" />
<context:component-scan base-package="com.itan.transactional.xml" />
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="${url}" />
<property name="driverClassName" value="${className}" />
<property name="username" value="${userName}" />
<property name="password" value="${password}" />
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg name="dataSource" ref="druidDataSource" />
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg name="dataSource" ref="druidDataSource" />
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="get*" read-only="true" timeout="30" />
<tx:method name="*" timeout="30" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="userServicePointCut" expression="execution(* com.itan.transactional.xml.service.impl.UserServiceImpl.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="userServicePointCut" />
</aop:config>
</beans>
5、测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:druid-config.xml")
public class Test3 {
@Autowired
private UserService userService;
@Test
public void test01() {
int res = userService.insertUser(new User("王五", 20));
System.out.println("成功修改数据行数:" + res);
List<User> users = userService.getAllUsers();
System.out.println("获取所有用户信息:" + users);
}
}
五、基于注解的声明式事务
1、概述
1、声明式事务还支持注解方式配置。并且更加简单。在XML中,使用<tx:annotation-driven>
标签开启事务注解的支持,可以不必配置<tx:advice>、<aop:config>
标签。
2、<tx:annotation-driven>常用属性:
-
transaction-manager属性:指定驱动事务的事务管理器的beanName,默认值为transactionManager;如果需要的事务管理器的beanName不是transactionManager才需要显式指定。
-
proxy-target-class属性:用于指定使用@Transactional注解标志的类创建的事务代理类型,默认为false,使用JDK代理;如果设置为true,使用CGLIB代理。
-
order属性:使用@Transactional注解的bean,当多个通知在特定连接点执行时,控制事务通知器的执行顺序。
-
mode属性:该属性用于指示应该采用Spring AOP来对异步方法进行动态代理,还是采用AspectJ来进行静态织入;默认为proxy,即Spring AOP代理,如果设置为aspectj,那么还需要spring-aspects.jar(其内部包含了aspectjweaver依赖)
2、@Transactional注解
1、它是Spring提供的事务注解,事务的语义(传播行为、隔离级别、回滚规则)都可以在注解属性中定义。
2、@Transactional注解可以标注在类、接口、方法上;标注在类上时,类中的所有方法都尝试应用同一个@Transactional注解;如果同时标注在类上和方法上,方法上的注解配置优先级高于类上的注解配置。
3、由于基于Spring AOP,因此同一个代理对象的事务方法相互调用,则被调用的方法的事务注解配置不会生效。
4、如果将@Transactional注解添加在protected、private修饰的方法上,虽然代码不会有任何的报错,但是实际上注解是不会生效的(XML配置中可以生效)。
5、属性说明:
属性名 | 说明 |
---|
value | 指定使用的事务管理器的beanName,默认为空 |
transactionManager | 指定使用的事务管理器的beanName,默认为空,同value属性 |
propagation | 事务的传播行为,默认Propagation.REQUIRED,即PROPAGATION_REQUIRED |
isolation | 事务的隔离级别,默认Isolation.DEFAULT,即ISOLATION_DEFAULT |
timeout | 事务的超时时间,默认TransactionDefinition.TIMEOUT_DEFAULT,即-1 |
readOnly | 只读事务标识,默认false,即普通读写事务 |
rollbackFor | 引发回滚的可选异常类Class数组,必须是Throwable下面的异常类型 |
rollbackForClassName | 引发回滚的可选异常类名称数组,必须是Throwable下面的异常类型 |
noRollbackFor | 不得导致回滚的异常类Class数组,必须是Throwable下面的异常类型 |
noRollbackForClassName | 不得导致回滚的异常类名称数组,必须是Throwable下面的异常类型 |
3、事务配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:druid.properties" file-encoding="UTF-8" />
<context:component-scan base-package="com.itan.transactional.anno" />
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="${url}" />
<property name="driverClassName" value="${className}" />
<property name="username" value="${userName}" />
<property name="password" value="${password}" />
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg name="dataSource" ref="druidDataSource" />
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg name="dataSource" ref="druidDataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
</beans>
4、业务配置类
package com.itan.transactional.anno.service;
import com.itan.transactional.entity.User;
import java.util.List;
public interface UserService {
int insertUser(User user);
List<User> getAllUsers();
}
package com.itan.transactional.anno.service.impl;
import com.itan.transactional.entity.User;
import com.itan.transactional.xml.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.transaction.support.DefaultTransactionStatus;
import java.util.List;
@Service
@Transactional(timeout = 30)
public class UserServiceImpl implements UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public int insertUser(User user) {
DefaultTransactionStatus transactionStatus = (DefaultTransactionStatus) TransactionAspectSupport.currentTransactionStatus();
System.out.println("insertUser method Transaction isReadOnly: " + transactionStatus.isReadOnly());
String sql = "insert into users (name, age) values (?,?)";
return jdbcTemplate.update(sql, user.getName(), user.getAge());
}
@Override
@Transactional(readOnly = true, timeout = 30)
public List<User> getAllUsers() {
DefaultTransactionStatus transactionStatus = (DefaultTransactionStatus) TransactionAspectSupport.currentTransactionStatus();
System.out.println("getAllUsers method Transaction isReadOnly: " + transactionStatus.isReadOnly());
String sql = "select id,name,age from users";
List<User> list = jdbcTemplate.query(sql, BeanPropertyRowMapper.newInstance(User.class), null);
return list;
}
}
5、测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:druid-anno-config.xml")
public class Test4 {
@Autowired
private UserService userService;
@Test
public void test01() {
int res = userService.insertUser(new User("赵六", 29));
System.out.println("成功修改数据行数:" + res);
List<User> users = userService.getAllUsers();
System.out.println("获取所有用户信息:" + users);
}
}
6、纯注解方式的声明式事务
1、上面通过@Transactional注解+XML搭配实现声明式事务,配置还是比较复杂,可以完全使用纯注解和Java Config配置。
2、使用@EnableTransactionManagement
注解替代<tx:annotation-driven>
标签来开启事务注解驱动。
package com.itan.transactional.anno.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@ComponentScan("com.itan.transactional.anno.*")
@PropertySource(value = "classpath:druid.properties", encoding = "UTF-8")
@EnableTransactionManagement
public class DataSourceAnnoConfig {
@Value("${url}")
private String url;
@Value("${className}")
private String className;
@Value("${userName}")
private String userName;
@Value("${password}")
private String password;
@Bean
public DruidDataSource druidDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(url);
dataSource.setDriverClassName(className);
dataSource.setUsername(userName);
dataSource.setPassword(password);
return dataSource;
}
@Bean
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(druidDataSource());
}
@Bean
public DataSourceTransactionManager transactionManager() {
return new DataSourceTransactionManager(druidDataSource());
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = DataSourceAnnoConfig.class)
public class Test5 {
@Autowired
private UserService userService;
@Test
public void test01() {
int res = userService.insertUser(new User("陈七", 29));
System.out.println("成功修改数据行数:" + res);
List<User> users = userService.getAllUsers();
System.out.println("获取所有用户信息:" + users);
}
}