场景
公司有一个业务需要控制db0和db1的两个数据源的表的事务,每次都要自己多个开启,比较麻烦,因为公司是自定义框架所以模拟springjdbc的DataSourceTransactionManager多数据源控制 和@Transactional注解模拟写了一个多数据源管理事务的注解@LdTransactional

解决方案
查看DataSourceTransactionManager源码得知,实际是多个数据源合并成一个
同时我们公司的事务的开启,提交,回滚,已经保障了线程安全,所以我才用的思路
1.建立一个能够合并多个数据源的对象类 MultiTransactionDao
2.建立一个控制事务的注解@LdTransactional
3.aop切入@LdTransactional注解,在到达事务控制方法之前开启事务,方法执行完毕提交事务LdTransactionalAop
4.然后测试多个效果

测试效果图
代码

package com.ld.action.index;import java.util.Date;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;import com.ld.aop.annotation.LdTransactional;
import com.ld.aop.model.ErrorLog;
import com.ld.aop.model.LoginLog;
import com.ld.dao.CreditBaseDB1Dao;
import com.ld.dao.CreditBaseNewDao;
import com.ld.model.creditsystem.jiben.IcpGudongModel;
import com.ld.service.creditsystem.jiben.IcpGuDongService;
import com.ld.service.log.ErrorLogService;
import com.ld.service.log.LoginLogService;
import com.ld.util.CmUtil;
import com.ld.util.SessionUtil;@Controller
@RequestMapping("ldTransactional")
public class LdTransactionalController extends BaseController {@Autowiredprivate ErrorLogService errorLogService;@Autowiredprivate LoginLogService loginLogService;@AutowiredIcpGuDongService icpGuDongService;// 出现特定异常不会滚@RequestMapping("")@LdTransactional(noRollbackFor = ArithmeticException.class)@ResponseBodypublic void test() {// 测试对应的事务System.err.println(Thread.currentThread().getName());// 记录错误日志ErrorLog errorLog = getErrorLog();errorLogService.save(errorLog);int i = 0 / 0;LoginLog loginLog = getLoginLog();loginLogService.save(loginLog);System.err.println("执行完毕");}// 多事务@RequestMapping("test2")@LdTransactional(dataSourceDao = { CreditBaseNewDao.class, CreditBaseDB1Dao.class })@ResponseBodypublic void test2() {// 测试对应的事务System.err.println(Thread.currentThread().getName());// 记录错误日志ErrorLog errorLog = getErrorLog();errorLogService.save(errorLog);int i = 0 / 0;IcpGudongModel IcpGudongModel = new IcpGudongModel();IcpGudongModel.setId(CmUtil.getPkId());IcpGudongModel.setGudongren("测试两个数据源的事务");System.err.println("执行完毕");icpGuDongService.save(IcpGudongModel);}private LoginLog getLoginLog() {LoginLog loginLog = new LoginLog();loginLog.setId(CmUtil.getPkId());loginLog.setLoginName("测试事务");loginLog.setLoginTime(new Date());loginLog.setIp(SessionUtil.getUserIP());loginLog.setStatus(1);loginLog.setMsg("测试事务");return loginLog;}private ErrorLog getErrorLog() {ErrorLog errorLog = new ErrorLog();errorLog.setId(CmUtil.getPkId());errorLog.setCreateTime(new Date());errorLog.setErrorInfo("测试事务");errorLog.setUserId(SessionUtil.getloginUserId());errorLog.setUserName(SessionUtil.getLoginName());errorLog.setIp(SessionUtil.getUserIP());errorLog.setUrl(request.getRequestURI());return errorLog;}
}

代码执行效果
test1(单个数据源事务) 按照预期效果执行 打印事务错误,并回滚事务
在这里插入图片描述
test2(多个数据源事务 ) 按照预期效果执行
在这里插入图片描述
test3(单个数据源事务 ,开启指定异常不需要回滚) 按照预期效果执行,提交了事务
在这里插入图片描述

下面是代码
MultiTransactionDao.java

package com.ld.aop.dao;import java.sql.SQLException;
import java.util.HashSet;
import java.util.Set;import com.ld.dao.TransactionDao;/*** 多个不同数据源事务的dao进行合并* * @ClassName MultiTransactionDao* @author <a href="892042158@qq.com" target="_blank">于国帅</a>* @date 2019年2月13日 下午3:05:02**/
public class MultiTransactionDao {private Set<TransactionDao> multiDaoSet = new HashSet<>();// 通过构造函数合并多个数据源public MultiTransactionDao(Class<? extends TransactionDao>[] dataSourceDaos) { // 直接注入已经实例化好的for (Class<? extends TransactionDao> class1 : dataSourceDaos) {multiDaoSet.add(MultiDataSourceFactory.getDao(class1));}}public MultiTransactionDao(Set<TransactionDao> multiDaoSet) {this.multiDaoSet = multiDaoSet;}// 开启事务public void starTransaction() throws SQLException {for (TransactionDao transactionDao : multiDaoSet) {transactionDao.starTransaction();}}// 提交事务public void commitTransaction() throws Exception {for (TransactionDao transactionDao : multiDaoSet) {transactionDao.commitTransaction();}}// 回滚事务public void rollbackTransaction() {for (TransactionDao transactionDao : multiDaoSet) {transactionDao.rollbackTransaction();}}
}

为了防止反复处理dao对象,导致内存变大,建立缓存MultiDataSourceFactory.java

package com.ld.aop.dao;import java.util.HashSet;
import java.util.Set;import com.ld.dao.CreditBaseDB1Dao;
import com.ld.dao.CreditBaseNewDao;
import com.ld.dao.TransactionDao;public class MultiDataSourceFactory {public static final CreditBaseNewDao DB0_DAO = new CreditBaseNewDao();public static final CreditBaseDB1Dao DB1_DAO = new CreditBaseDB1Dao();private static Set<TransactionDao> multiDaoSet = new HashSet<>();static {multiDaoSet.add(DB0_DAO);multiDaoSet.add(DB1_DAO);}/**** 获取需要开启事务的dao* * @Title getDao* @author 于国帅* @date 2019年3月6日 下午2:18:01* @param dao* @return TransactionDao*/public static TransactionDao getDao(Class<? extends TransactionDao> dao) {for (TransactionDao transactionDao : multiDaoSet) {transactionDao.getClass().isAssignableFrom(dao);return transactionDao;}return DB0_DAO;}/*** 返回一个multiDaoSet的完整副本* * @Title getAllDao* @author 于国帅* @date 2019年3月6日 下午1:41:26* @return Set<TransactionDao>*/public static Set<TransactionDao> getAllDao() {return new HashSet<>(multiDaoSet);}
}

@LdTransactional

package com.ld.aop.annotation;import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;import org.springframework.core.annotation.AliasFor;import com.ld.dao.CreditBaseNewDao;
import com.ld.dao.TransactionDao;/*** Transactional 一步步模拟这个注解* * @ClassName LdTransactional* @author <a href="892042158@qq.com" target="_blank">于国帅</a>* @date 2019年2月13日 上午10:58:19**/
@Documented
@Retention(RUNTIME)
@Target(METHOD)
public @interface LdTransactional {/*** 指定数据源的Dao,存在多个的时候,多个数据源执行提交* * @默认为db0的mysqlDao* @Title dataSourceDao* @author 于国帅* @date 2019年2月13日 下午2:53:43* @return Class<? extends TransactionDao>[]*/@AliasFor("dataSourceDao")Class<? extends TransactionDao>[] value() default { CreditBaseNewDao.class };/*** 指定数据源的Dao,存在多个的时候,多个数据源执行提交* * @默认为db0的mysqlDao* @Title dataSourceDao* @author 于国帅* @date 2019年2月13日 下午2:53:43* @return Class<? extends TransactionDao>[]*/@AliasFor("value")Class<? extends TransactionDao>[] dataSourceDao() default { CreditBaseNewDao.class };/*** 是否开启所有的数据源的事务,默认不开启,采用dataSourceDao 管理需要开启的事务* * @Title daoAll* @author 于国帅* @date 2019年2月13日 下午4:16:35* @return boolean*/boolean daoAll() default false;/*** Class对象数组,必须继承自Throwable 不会导致事务回滚的异常类数组* * @默认所有异常都要回滚* @Title noRollbackFor* @author 于国帅* @date 2019年2月13日 上午11:41:16* @return Class<? extends Throwable>[]*/Class<? extends Throwable>[] noRollbackFor() default {};/*** * Class对象数组,必须继承自Throwable 会导致事务回滚的异常类数组* * @当有不需要回滚的异常中包含一个异常需要回滚的时候用这个* @Title rollbackFor* @author 于国帅* @date 2019年2月13日 下午12:55:33* @return Class<? extends Throwable>[]*/Class<? extends Throwable>[] rollbackFor() default {};
}

LdTransactionalAop

package com.ld.aop;import java.lang.reflect.Method;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.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;import com.ld.aop.annotation.LdTransactional;
import com.ld.aop.dao.MultiDataSourceFactory;
import com.ld.aop.dao.MultiTransactionDao;import lombok.extern.slf4j.Slf4j;@Slf4j
@Aspect
@Component
public class LdTransactionalAop {@Pointcut("@annotation(com.ld.aop.annotation.LdTransactional)")public void cutLdTransactional() {}/*** 因为aop 切入时 与当前的方法为一个线程,所以随便找一个dao 开启事务就可以*/@Around("cutLdTransactional()")public Object doLdTransactional(ProceedingJoinPoint point) {MethodSignature signature = (MethodSignature) point.getSignature();Method method = signature.getMethod();LdTransactional ldTransactional = AnnotationUtils.findAnnotation(method, LdTransactional.class);MultiTransactionDao multiTransactionDao = null;if (ldTransactional.daoAll()) { // 如果开启了全部 那么加载全部multiTransactionDao = new MultiTransactionDao(MultiDataSourceFactory.getAllDao());} else {multiTransactionDao = new MultiTransactionDao(ldTransactional.dataSourceDao());}Object result = null;System.err.println(Thread.currentThread().getName());try {multiTransactionDao.starTransaction();result = point.proceed();} catch (Throwable e) {handleThrowable(e, ldTransactional, multiTransactionDao);} finally {try {multiTransactionDao.commitTransaction();} catch (Exception e) {handleThrowable(e, ldTransactional, multiTransactionDao);}}return result;}// 处理异常是否回滚private void handleThrowable(Throwable e, LdTransactional ldTransactional, MultiTransactionDao mysqlBaseDao) {log.error("打印事务异常信息", e);boolean rollbackFalg = true; // 默认回滚Class<? extends Throwable>[] noRollbackFor = ldTransactional.noRollbackFor();for (Class<? extends Throwable> class1 : noRollbackFor) {if (class1.isAssignableFrom(e.getClass())) { // 如果有符合不会滚的条件那么 不会滚rollbackFalg = false;break;}}Class<? extends Throwable>[] rollbackFor = ldTransactional.rollbackFor();for (Class<? extends Throwable> class1 : rollbackFor) {if (class1.isAssignableFrom(e.getClass())) { // 如果有符合不会滚的条件那么 不会滚rollbackFalg = true;break;}}if (rollbackFalg) {mysqlBaseDao.rollbackTransaction();}}
}

SpringBoot +自定义dao框架 自定义注解管理多数据源与事务相关推荐

  1. MyBatis-学习笔记04【04.自定义Mybatis框架基于注解开发】

    Java后端 学习路线 笔记汇总表[黑马程序员] MyBatis-学习笔记01[01.Mybatis课程介绍及环境搭建][day01] MyBatis-学习笔记02[02.Mybatis入门案例] M ...

  2. MyBatis-学习笔记03【03.自定义Mybatis框架】

    Java后端 学习路线 笔记汇总表[黑马程序员] MyBatis-学习笔记01[01.Mybatis课程介绍及环境搭建][day01] MyBatis-学习笔记02[02.Mybatis入门案例] M ...

  3. JDBC 学习笔记(四)—— 自定义JDBC框架+Apache—DBUtils框架+事务管理+操作多表 - 解无邪

    本文目录: 1.自定义JDBC框架 --数据库元数据:DataBaseMetaData 2.自定义JDBC框架 --数据库元数据:DataBaseMetaData 3.自定义JDBC框架 --结果集元 ...

  4. 在SpringBoot项目中,自定义注解+拦截器优雅的实现敏感数据的加解密!

    在实际生产项目中,经常需要对如身份证信息.手机号.真实姓名等的敏感数据进行加密数据库存储,但在业务代码中对敏感信息进行手动加解密则十分不优雅,甚至会存在错加密.漏加密.业务人员需要知道实际的加密规则等 ...

  5. SpringBoot和日志框架:缘由,日志框架的选择,使用,自定义配置,日志框架切换

    日志框架 缘由 如果像我们初学者,想知道代码运行到哪里了,一般都是会System.out.println() 进行输出到控制台查看代码运行的情况,好知道代码错误在哪里 但是大型的系统里面,如果有很多的 ...

  6. java day53【 Mybatis框架概述 、 Mybatis 框架快速入门、自定义 Mybatis 框架 】

    第1章 框架概述 1.1 什么是框架 1.1.1 什么是框架 框架(Framework)是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法;另一种 定义认为,框架是可被应用开发者定 ...

  7. PHP笔记-自定义MVC框架

    膜拜 膜拜下黑马大佬程序员的项目,学习到了这样的手写MVC框架的方式,受益匪浅,感觉自己在C/C++和Java方面,还有许多要学习的地方,看看能不能抄下这个php自己撸一个C/C++的MVC框架. 下 ...

  8. RPC系列之Netty实现自定义RPC框架

    进行这个章节之前,需要去看一下RMI的实现哈,如果了解过的童鞋可以直接跳过,如果没有或者不知道RMI的童鞋,移驾到下面的链接看完之后再回来继续看这篇 RPC系列之入门_阿小冰的博客-CSDN博客RPC ...

  9. springboot过滤字段_SpringBoot自定义过滤器的两种方式及过滤器执行顺序

    第一种 @WebFilter + @ServletComponentScan 注解 1.首先自定义过滤器 如下自定义过滤器 ReqResFilter必须实现  javax.servlet.Filter ...

  10. SpringBoot集成Redis--配置自定义的RedisCacheManager

    2019独角兽企业重金招聘Python工程师标准>>> 配置自定义的RedisCacheManager--1自定义键生成规则 默认的键生成器 当不指定缓存的key时,SpringBo ...

最新文章

  1. eclipse CreateProcess error=87 的解决办法
  2. 如何为ccflow工作流引擎增加一个优先级PRI?
  3. Python 爬虫练手项目—酒店信息爬取
  4. python - django (auth 的使用)
  5. Visual C#的SQL Server编程
  6. 高级C语言教程-存储器和指针
  7. Android实战——第三方服务之Bmob后端云的推送服务的集成和使用(三)
  8. SQL Server Management Studio 过期无法使用解决办法
  9. 用java实现验证码(CAPTCHA)
  10. ntpd、ntpdate、hwclock的区别
  11. 如何在firefox下获取下列框选中option的text
  12. 为什么我加了过滤器然后就登不进去了_布隆过滤器过时了,未来属于布谷鸟过滤器?...
  13. 【转载】中国煤层气资源量
  14. PDE10 wave equation: d'Alembert's formula
  15. Windows live Writer的安装配置
  16. Java并发编程实践-this溢出
  17. 伪彩色增强(基于MATLAB)
  18. 用vs code 搭建stm32 开发环境(详细)
  19. MIT 线性代数导论 第十九、二十讲:行列式公式、代数余子式、克拉默法则
  20. Delaunay三角网之逐点插入法

热门文章

  1. 【记录】ASP.NET MVC MapRoute .htm 不起作用
  2. Search in Rotated Sorted Array II leetcode java
  3. ubuntu 安装 sublime
  4. 携程分销联盟-旅游度假接口实现1
  5. 容器 - HashTable
  6. javaScript中简单数据类型和复杂数据类型赋值拷贝的理解
  7. 阿里开发者们的第15个感悟:做一款优秀大数据引擎,要找准重点解决的业务场景... 1
  8. BZOJ2038:[2009国家集训队]小Z的袜子——题解
  9. 【原创】自己动手写控件----XSmartNote控件
  10. 10月全球浏览器份额态势:Chrome领先Firefox7.34%