maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
<dependency>
<groupId>com.jfinal</groupId>
<artifactId>jfinal</artifactId>
<version>2.2</version>
</dependency>
添加一个AOP切面类
package com.rc.openapi.datasource;
import com.jfinal.kit.LogKit;
import com.jfinal.plugin.activerecord.ActiveRecordException;
import com.jfinal.plugin.activerecord.Config;
import com.jfinal.plugin.activerecord.DbKit;
import com.jfinal.plugin.activerecord.NestedTransactionHelpException;
import com.jfinal.plugin.activerecord.tx.TxConfig;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.transaction.NoTransactionException;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
/**
* @ClassName JFinalTxAop
* @Author LWJ
* @Date 2021/1/22 21:46
* @方法说明 {事务控制}
*/
@Aspect
@Component
public class JFinalTxAop {
/**
* 是否可以手动提交事物,默认可以自动提交
*/
private static boolean canCommit = true;
/**
* 自定义JFinal 事物注解:类上面
*
* @within 表示注解在类下面所有的方法
*/
@Pointcut("@within(org.springframework.transaction.annotation.Transactional)")
private void methodWithin() {
}
/**
* 自定义JFinal 事物注解:方法上面
*
* @annotation 表示注解只能支持方法上
*/
@Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
private void methodAnno() {
}
/**
* 兼容@Transactional可以放在类上和方法上
* 当放类上时,类中所有方法都支持事物注解,
* 如果类上没有@Transactional,然而是放在方法上的,那么只有此方法支持事物注解
*
* @param pjp 切入点目标对象
* @return 返回切入方法的返回数据
* @throws Throwable
*/
@Around(value = "methodWithin() || methodAnno()")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
Object retVal = null;
Config config = getConfigWithTxConfig(pjp);
if (config == null)
config = DbKit.getConfig();
Connection conn = config.getThreadLocalConnection();
// Nested transaction support
if (conn != null) {
try {
if (conn.getTransactionIsolation() < getTransactionLevel(config))
conn.setTransactionIsolation(getTransactionLevel(config));
retVal = pjp.proceed();
return retVal;
} catch (SQLException e) {
throw new ActiveRecordException(e);
}
}
Boolean autoCommit = null;
try {
conn = config.getConnection();
autoCommit = conn.getAutoCommit();
config.setThreadLocalConnection(conn);
conn.setTransactionIsolation(getTransactionLevel(config));// conn.setTransactionIsolation(transactionLevel);
conn.setAutoCommit(false);
retVal = pjp.proceed();
if (canCommit) {
conn.commit();
} else {
try {
conn.rollback();
} catch (Exception e1) {
LogKit.error(e1.getMessage(), e1);
}
}
} catch (NestedTransactionHelpException e) {
if (conn != null) try {
conn.rollback();
} catch (Exception e1) {
LogKit.error(e1.getMessage(), e1);
}
LogKit.logNothing(e);
} catch (Throwable t) {
if (conn != null) try {
conn.rollback();
} catch (Exception e1) {
LogKit.error(e1.getMessage(), e1);
}
throw t instanceof RuntimeException ? (RuntimeException) t : new ActiveRecordException(t);
} finally {
canCommit = true;
try {
if (conn != null) {
if (autoCommit != null)
conn.setAutoCommit(autoCommit);
conn.close();
}
} catch (Throwable t) {
// can not throw exception here, otherwise the more important exception in previous catch block can not be thrown
LogKit.error(t.getMessage(), t);
} finally {
// prevent memory leak
config.removeThreadLocalConnection();
}
}
return retVal;
}
/**
* 获取配置的事务级别
*
* @param config
* @return
*/
protected int getTransactionLevel(Config config) {
return config.getTransactionLevel();
}
/**
* @param lwj
* @return Config
*/
public static Config getConfigWithTxConfig(ProceedingJoinPoint pjp) {
MethodSignature ms = (MethodSignature) pjp.getSignature();
Method method = ms.getMethod();
TxConfig txConfig = method.getAnnotation(TxConfig.class);
if (txConfig == null)
txConfig = pjp.getTarget().getClass().getAnnotation(TxConfig.class);
if (txConfig != null) {
Config config = DbKit.getConfig(txConfig.value());
if (config == null)
throw new RuntimeException("Config not found with TxConfig: " + txConfig.value());
return config;
}
return null;
}
public static boolean setRollbackOnly() {
canCommit = false;
try {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
} catch (NoTransactionException e) {
return false;
}
return true;
}
}
注意:
这个类上面要增加两个注解
@Aspect
作用是把当前类标识为一个切面供容器读取
@Component
标注一个类为Spring容器的Bean,(把普通pojo实例化到spring容器中,相当于配置文件中的)
启动类:
package com.rc.openapi;
import brave.sampler.Sampler;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Bean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableDiscoveryClient
@EnableTransactionManagement
/**
* @author lwj
*
*/
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
@Bean
public Sampler defaultSampler() {
return Sampler.ALWAYS_SAMPLE;
}
}
注意:
@EnableDiscoveryClient
是能够让注册中心能够发现,扫描到该服务
@EnableTransactionManagement
开启事务支持
service
package com.rc.openapi.service.serviceImpl;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import com.rc.openapi.datasource.JFinalTxAop;
import org.springframework.stereotype.Service;
import com.rc.openapi.domain.mysql.TestMapper1;
import com.rc.openapi.domain.oracle.TestMapper2;
import com.rc.openapi.service.TestDemoService;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Transactional
@Service
public class TestDemoServiceImpl implements TestDemoService {
@Resource
private TestMapper2 testMapper2;
@Resource
private TestMapper1 testMapper1;
@Override
public void insertTest() {
try {
Map<String,Object> map = new HashMap<>() ;
map.put("name","123");
testMapper1.insertTest(map);
int i = 100/1;//触发异常
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("test");
}
//或者手动回滚,不用抛出异常
JFinalTxAop.setRollbackOnly()
}
}
@ Transactional可以放在类上或方法上都没问题
JFinalTxAop.setRollbackOnly()和
throw new RuntimeException(“test”);
这和个时候可以不用加因为启动的是时候已经把切面类加载进去了.