最近在SpringBoot中使用JFinal的ActiveRecordPlugin插件,虽然事物可以直接通过@Before(Tx.class)来解决,但是后面项目的需要将事物交给spring来管理,具体实现看下去
思路:使用spring AOP代理
这里使用springboot来实现,spring同理
maven 依赖
<dependency><!-- spring boot aop starter依赖 --> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <!-- 数据源 --> <dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> </dependency> 其他的依赖直接省略了,做程序的都知道的
感谢 如梦技术的代码片段 , JFinal
JFinalTxAop
package com.zgxl.common.aop; 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 java.lang.reflect.Method; import java.sql.Connection; import java.sql.SQLException; /** * @author choxsu * @date 2018/4/13 */ @Aspect @Component public class JFinalTxAop { /** * 自定义JFinal 事物注解 * value中的意思解释 * * @annotation 表示注解只能支持方法上 * @within 表示注解在类下面所有的方法 , 暂时不使用这种方式 */ @Pointcut("@annotation(com.zgxl.common.aop.JFinalTx)") private void method() { } @Around(value = "method()", argNames = "pjp") 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(); conn.commit(); } 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 { 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 pjp * @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; } }
JFinalTx
package com.zgxl.common.aop; /** * @author choxsu */ import java.lang.annotation.*; /** * Jfinal事物交给spring管理注解 * 目前只支持用在方法上 */ @Inherited @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface JFinalTx { }
使用
TestController
package com.choxsu.elastic.controller; import com.choxsu.elastic.service.TestService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author choxsu */ @RestController @RequestMapping(value = {"/test/v1"}) public class TestController { @Autowired private TestService testService; @GetMapping(value = "/testTran") public Object testTran(){ return testService.testTran(); } }
TestService
package com.choxsu.elastic.service; import com.choxsu.elastic.config.JFinalTx; import com.jfinal.kit.Ret;import com.jfinal.plugin.activerecord.Db; import com.jfinal.plugin.activerecord.Record; import org.springframework.stereotype.Service; /** * @author choxsu */ @Service public class TestService { /** * 事物测试 * * @return */ @JFinalTx public Object testTran() { Record record = new Record(); record.set("id", 10); Db.save("test", record); if (true) { throw new RuntimeException("test"); } return Ret.by("msg", "success"); } }
sql执行了
Sql: insert into `test`(`id`) values(?)
但是数据库没有数据
到此证明事物拦截成功,可以使用spring来管理ActiveRecordPlugin的事物了
去掉throw new RuntimeException("test");
的效果
sql
Sql: insert into `test`(`id`) values(?)