spring boot实现声明式事物的三种方式以及spring aop事物不生效问题的解决

一、使用xml配置

  1. src/main/resources目录下新建transaction.xmltransaction.xml内容如下:

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">


	
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	bean>
	
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="del*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Throwable" />
			<tx:method name="remove*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Throwable" />
			<tx:method name="clear*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Throwable" />
			<tx:method name="batch*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Throwable" />
			<tx:method name="insert*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Throwable" />
			<tx:method name="save" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Throwable" />
			<tx:method name="update*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Throwable" />
			<tx:method name="add*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Throwable" />
			<tx:method name="approve*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Throwable" />
			<tx:method name="submit*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Throwable" />
			<tx:method name="*" propagation="SUPPORTS" read-only="true">tx:method>
		tx:attributes>
	tx:advice>
	<aop:config>
		<aop:pointcut id="pc" expression="execution(* com.xqxx.ttzcms.service..*(..))" />
		<aop:advisor pointcut-ref="pc" advice-ref="txAdvice" />
	aop:config>
beans>

  1. 在启动类(@SpringBootApplication修饰的类)上加上@ImportResource("classpath:transaction.xml")注解

二、使用java config配置

  1. 新建TransactionManagerConfig.java,内容如下
package com.xqxx.ttzcms.config;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource;
import org.springframework.transaction.interceptor.RollbackRuleAttribute;
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionAttribute;
import org.springframework.transaction.interceptor.TransactionInterceptor;

/**
 * 通过AOP切面设置全局事务,拦截service包下面所有方法  *
 * AOP术语:通知(Advice)、连接点(Joinpoint)、切入点(Pointcut)、切面(Aspect)、目标(Target)、代理(Proxy)、织入(Weaving)
 */
@Configuration
public class TransactionManagerConfig {
	// 拦截指定包下所有方法
	private static final String AOP_POINTCUT_EXPRESSION = "execution (* com.xqxx.ttzcms.service..*(..))";
	@Autowired
	private PlatformTransactionManager transactionManager;

	@Bean
	public TransactionInterceptor txAdvice() {
		/* 事务管理规则,声明具备事务管理的方法名 */
		NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();

		RuleBasedTransactionAttribute readOnlyRule = new RuleBasedTransactionAttribute();
		readOnlyRule.setReadOnly(true);
		// PROPAGATION_NOT_SUPPORTED事务传播级别5,以非事务运行,如果当前存在事务,则把当前事务挂起
		readOnlyRule.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED);

		RuleBasedTransactionAttribute requireRule = new RuleBasedTransactionAttribute();
		// 抛出异常后执行切点回滚
		requireRule.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class)));

		// PROPAGATION_REQUIRED:事务隔离性为1,若当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。
		requireRule.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

		// 设置事务失效时间,如果超过60秒,则回滚事务
		requireRule.setTimeout(60);
		Map<String, TransactionAttribute> txMap = new HashMap<>();

		txMap.put("add*", requireRule);
		txMap.put("save*", requireRule);
		txMap.put("insert*", requireRule);
		txMap.put("create*", requireRule);
		txMap.put("edit*", requireRule);
		txMap.put("update*", requireRule);
		txMap.put("modify*", requireRule);
		txMap.put("delete*", requireRule);
		txMap.put("remove*", requireRule);
		txMap.put("drop*", requireRule);
		txMap.put("clear*", requireRule);
		txMap.put("submit*", requireRule);
		txMap.put("approve*", requireRule);
		txMap.put("reject*", requireRule);
		txMap.put("forward*", requireRule);

		txMap.put("get*", readOnlyRule);
		txMap.put("query*", readOnlyRule);
		txMap.put("find*", readOnlyRule);
		txMap.put("select*", readOnlyRule);
		txMap.put("list*", readOnlyRule);
		txMap.put("show*", readOnlyRule);
		txMap.put("view*", readOnlyRule);
		txMap.put("page*", readOnlyRule);
		source.setNameMap(txMap);
		TransactionInterceptor txAdvice = new TransactionInterceptor(transactionManager, source);
		return txAdvice;
	}

	@Bean
	public Advisor txAdviceAdvisor() {
		/*
		 * 声明切点的面
		 * 切面(Aspect):切面就是通知和切入点的结合。通知和切入点共同定义了关于切面的全部内容——它的功能、在何时和何地完成其功能。
		 */
		AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
		/* 声明和设置需要拦截的方法,用切点语言描写 */
		pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
		/* 设置切面=切点pointcut+通知TxAdvice */
		return new DefaultPointcutAdvisor(pointcut, txAdvice());
	}
}

  1. 检查TransactionManagerConfig.java所在的位置(包,如示例中的com.xqxx.ttzcms.config)能否被启动类扫描,如果不能,则手动在启动类上添加@ComponentScan("com.xqxx.ttzcms.config")注解

三、使用@Transactional注解

  1. @Transactional注解使用在类上时,整个类的所有方法都会产生事务,使用在单个方法上时,单个方法会产生事务。

  2. @Transactional只能作用于类或抽象类,不能作用于接口。

  3. @Transactional一般放在service层的实现类上,示例:

package com.xqxx.ttzcms.service;

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.xqxx.ttzcms.db.service.AbstractBookcpService;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@Primary
@Service
//@Transactional
public class BookcpServiceImpl extends AbstractBookcpService {

	@Transactional
	public int deleteByPrimaryKey(Integer id) {
		return super.deleteByPrimaryKey(id);
	}
}
  1. @Transactional可以放在抽象类中(即service的具体实现类的父实现类),从而解决spring aop代理不了父类方法的问题。
  2. 使用@Transactional(rollbackFor = {Throwable.class})可以在指定类型的异常下回滚,如果是非常慎重和严谨的提交操作,一定要加上rollbackFor = {Throwable.class}

spring aop事物不生效问题

Service不能继承

spring aop只能代理目标类存在的方法,不能代理目标类父类的方法,从而导致有多级service实现类时,会出现事物失效。
解决办法:

  1. 不使用多级service实现类,只保留一级。
  2. 把需要使用事务的父类方法,在子类再次声明,子类的实现使用super.父类方法
  3. 在需要使用事务的父类方法上添加@Transactional注解。

Service方法内部不能互相调用

内部调用时无法触发事务,因为隐式的是this是源对象,而不是代理对象。
怎么在内部调用而且触发事务呢?
不要使用this,而是从Spring容器里取:

applicationContext.getBean(UserService.class);

其他原因

spring aop事务不生效还有其他原因,如事务的扫描包配错了数据库不支持事务service的方法不是publicservice方法中新起了线程(线程中的代码不参与事务),这里不具体说明了。

你可能感兴趣的:(JavaEE,spring,spring,boot,spring事务,aop)