spring事务管理中,注解方式和xml配置方式优先级谁高?

在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配置方式优先级谁高,取决于谁最后加载,最后加载的设置会覆盖之前的设置。

posted @ 2020-05-26 11:56  三分魔系  阅读(177)  评论(0编辑  收藏  举报