在spring事务管理中,可以通过xml配置的方式去设置,也可以通过@Transactional注解去设置,那么这两种方式可以共存吗,如果可以共存,哪一种方式的优先级高呢?
创建一个maven项目,导入maven依赖:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.20</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
</dependencies>
创建一个User实体类:
public class UserEntity {
private long id;
private String userName;
private String userSex;
// 省略getter/setter
}
创建一个UserDao:
public class UserDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
/**
* 保存用户
*
* @param user
*/
public void saveUser(UserEntity user) throws Exception {
StringBuilder sql = new StringBuilder();
sql.append("INSERT INTO t_user ( `id`, `user_name`, `user_sex` ) VALUES (").append(user.getId()).append(",'")
.append(user.getUserName()).append("',").append("'").append(user.getUserSex()).append("')");
System.out.println(sql.toString());
jdbcTemplate.execute(sql.toString());
}
/**
* 通过id删除用户
*
* @param id
*/
public void removeUserById(int id) throws Exception {
String sql = "delete from t_user where id=" + id;
System.out.println(sql.toString());
jdbcTemplate.execute(sql.toString());
}
}
创建UserService和UserServiceImpl:
public interface UserService {
/**
* 保存用户
*
* @param user
*/
void saveUser(UserEntity user) throws Exception;
/**
* 通过id删除用户
*
* @param id
*/
void removeUserById(int id) throws Exception;
}
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void saveUser(UserEntity user) throws Exception {
userDao.saveUser(user);
}
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED, readOnly = false)
public void removeUserById(int id) throws Exception {
userDao.removeUserById(id);
}
}
创建数据库配置文件:
jdbc.mysql.driverClass=com.mysql.cj.jdbc.Driver
jdbc.mysql.url=jdbc:mysql://127.0.0.1:3306/learn_work?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
jdbc.mysql.username=root
jdbc.mysql.password=root
创建spring的xml配置文件:
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 读取数据库配置文件 -->
<context:property-placeholder location="classpath:jdbc.properties" />
<!-- 配置jdbcTemplate对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 配置dataSource -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.mysql.driverClass}" />
<property name="url" value="${jdbc.mysql.url}" />
<property name="username" value="${jdbc.mysql.username}" />
<property name="password" value="${jdbc.mysql.password}" />
</bean>
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 开启注解配置事务支持 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- xml声明式注解配置 -->
<!--配置AOP-->
<aop:config>
<!--通用切入点表达式-->
<aop:pointcut id="pCut" expression="execution(* com.learn.service.impl.*.*(..))"/>
<!--建立切入点和通知的对应关系-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pCut"/>
</aop:config>
<!--配置事务的通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED" read-only="false" isolation="DEFAULT" rollback-for="java.lang.Exception" />
</tx:attributes>
</tx:advice>
<!-- 配置userDao对象 -->
<bean id="userDao" class="com.learn.dao.UserDao">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<!-- 配置userService对象 -->
<bean id="userService" class="com.learn.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
</beans>
配置文件里面同时配置了注解方式的事务声明和xml配置方式的事务声明,最终项目结构:
测试一下数据能否正常插入:
public class Application {
private UserService userService;
@Before
public void init() {
@SuppressWarnings("resource")
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
userService = (UserService) ctx.getBean("userService");
}
@Test
public void testSave() throws Exception {
UserEntity user = new UserEntity();
user.setId(100);
user.setUserName("张三");
user.setUserSex("男");
userService.saveUser(user);
}
}
数据库:
程序可以正常运行,下面进行正式测试。
1.测试注解式事务声明和xml配置式事务声明能否共存:
修改UserServiceImpl类中的实现方法:
public void saveUser(UserEntity user) throws Exception {
userDao.saveUser(user);
int i = 1 / 0;// 发生异常事务是否回滚
}
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED, readOnly = false)
public void removeUserById(int id) throws Exception {
userDao.removeUserById(id);
int i = 1 / 0;// 发生异常事务是否回滚
}
测试类:
/**
* 测试再次插入user
*
* @throws Exception
*/
@Test
public void testSave2() throws Exception {
UserEntity user = new UserEntity();
user.setId(101);
user.setUserName("王麻子");
user.setUserSex("男");
userService.saveUser(user);
}
/**
* 测试删除之前插入的id 100
*
* @throws Exception
*/
@Test
public void testRemove() throws Exception {
userService.removeUserById(100);
}
数据库结果:
可以看到,插入和删除操作都因为发生异常进行了事务回滚,说明注解式事务声明和xml配置式事务声明是可以共存的。
2.测试注解方式和xml配置方式的优先级:
修改spring的xml配置文件,指定remove方法的事务传播机制:
<!--配置事务的通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED" read-only="false" isolation="DEFAULT" rollback-for="java.lang.Exception" />
<!-- remove开头的方法以非事务的方法运行 -->
<tx:method name="remove*" propagation="NOT_SUPPORTED" />
</tx:attributes>
</tx:advice>
再次运行删除的测试方法:
/**
* 测试删除之前插入的id 100
*
* @throws Exception
*/
@Test
public void testRemove() throws Exception {
userService.removeUserById(100);
}
可以在数据库看到,数据被删除了,也就是说xml里面配置了remove开头的方法不使用事务,虽然removeUserById使用@Transactional注解开启了事务管理,但是发生了异常以后事务并没有回滚,处于无事务管理状态,那么是不是说明xml配置方式优先级高于注解方式呢?
修改一下配置文件,交换一下注解方式和xml配置方式配置代码的位置:
<!-- xml声明式注解配置 -->
<!--配置AOP-->
<aop:config>
<!--通用切入点表达式-->
<aop:pointcut id="pCut" expression="execution(* com.learn.service.impl.*.*(..))"/>
<!--建立切入点和通知的对应关系-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pCut"/>
</aop:config>
<!--配置事务的通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED" read-only="false" isolation="DEFAULT" rollback-for="java.lang.Exception" />
<!-- remove开头的方法以非事务的方法运行 -->
<tx:method name="remove*" propagation="NOT_SUPPORTED" />
</tx:attributes>
</tx:advice>
<!-- 开启注解配置事务支持 -->
<tx:annotation-driven transaction-manager="transactionManager" />
重新插入一条id为100的数据,再次运行测试方法:
这次数据并没有删除,说明事务管理是生效的,发生异常以后事务回滚了,这时xml配置的事务管理失效了,到这里你应该明白了,注解方式和xml配置方式优先级谁高,取决于谁最后配置,最后配置会覆盖前面配置的属性。
那有没有可以指定谁优先的方法呢,当然是可以的,可以借助order属性指定,order属性越大加载顺序越靠后,可以覆盖之前的属性,也就是优先级越高,修改配置文件,依旧把注解方式放在前面,但使用order指定顺序:
<!-- 开启注解配置事务支持 -->
<tx:annotation-driven transaction-manager="transactionManager" order="2"/>
<!-- xml声明式注解配置 -->
<!--配置AOP-->
<aop:config>
<!--通用切入点表达式-->
<aop:pointcut id="pCut" expression="execution(* com.learn.service.impl.*.*(..))"/>
<!--建立切入点和通知的对应关系-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pCut" order="1"/>
</aop:config>
<!--配置事务的通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED" read-only="false" isolation="DEFAULT" rollback-for="java.lang.Exception" />
<!-- remove开头的方法以非事务的方法运行 -->
<tx:method name="remove*" propagation="NOT_SUPPORTED" />
</tx:attributes>
</tx:advice>
再次运行测试方法,可以发现数据依旧没有删除,说明removeUserById方法依然有事务。
结论:
1.注解方式和xml配置方式的事务管理可以共存;
2.注解方式和xml配置方式优先级谁高,取决于谁最后加载,最后加载的设置会覆盖之前的设置。