flowable 多数据源

2023-11-06


前言


        在springboot中使用flowable,此时flowable默认使用spring中的数据源。我这里flowable的表在一个数据库,业务表在另个一个数据库。

一、多数据源

        在项目中创建多数据源,给 DynamicDataSource 设置目标数据源和默认数据源,这个有很多教程,这里不详细说了。

public class DynamicDataSource extends AbstractRoutingDataSource {
    /**
     * 取得当前使用哪个数据源
     */
    @Override
    protected Object determineCurrentLookupKey() {
        return DbContextHolder.getDbType();
    }
}

给flowable数据源设置切面:

execution(* org.flowable.task.service..*.*(..))

给业务数据源设置切面:

execution(* cn.ac.iscas.pdm.biz.rest.dev.service..*.*(..)) 

二、测试

1.测试接口

public interface FolwableDSTestService {

     void test1();

    void test2();

    void test3();

    void test4();
}
@Service
public class FolwableDSTestServiceImpl implements FolwableDSTestService {

    @Autowired
    private TaskService taskService;

    @Autowired
    private PdmFileInfoService fileInfoService;

    @Autowired
    private FolwableDSTestService testService;
	......
}

1.不带事务

 	 @Override
    public void test1() {
        //查询数据源1中的任务
        List<Task> tasks = taskService.createTaskQuery()
                .active()
                .includeProcessVariables()
                .taskCandidateUser("张三")
                .includeIdentityLinks()
                .list();
        System.out.println("任务总数:" + tasks.size());

        //查询数据源2的业务数据
        List<PdmFileInfo> fileInfos = fileInfoService.list();
        System.out.println("文件总数:" + fileInfos.size());
    }

        通过数据源切面,各自访问自己的数据源,没有问题。

2.加上事务

 	
    @Transactional
    @Override
    public void test2() {
        //查询数据源1中的任务
        List<Task> tasks = taskService.createTaskQuery()
                .active()
                .includeProcessVariables()
                .taskCandidateUser("张三")
                .includeIdentityLinks()
                .list();
        System.out.println("任务总数:" + tasks.size());
        //查询数据源2的业务数据
        List<PdmFileInfo> fileInfos = fileInfoService.list();
        System.out.println("文件总数:" + fileInfos.size());
    }

        在有事务存在的情况下,会优先从事务中获取数据库连接,那这样就可能会存在访问的数据库不是目标库的问题。

看一下获取连接的源码:

DataSourceUtils.java

public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {
		try {
			return doGetConnection(dataSource);
		}
		catch (SQLException ex) {
			throw new CannotGetJdbcConnectionException("Failed to obtain JDBC Connection", ex);
		}
		catch (IllegalStateException ex) {
			throw new CannotGetJdbcConnectionException("Failed to obtain JDBC Connection: " + ex.getMessage());
		}
	}

public static Connection doGetConnection(DataSource dataSource) throws SQLException {
		Assert.notNull(dataSource, "No DataSource specified");
		
		//从事务中获取连接
		ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
		if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
			conHolder.requested();
			if (!conHolder.hasConnection()) {
				logger.debug("Fetching resumed JDBC Connection from DataSource");
				conHolder.setConnection(fetchConnection(dataSource));
			}
			return conHolder.getConnection();
		}
		// Else we either got no holder or an empty thread-bound holder here.

		logger.debug("Fetching JDBC Connection from DataSource");
		//获取目标数据源
		Connection con = fetchConnection(dataSource);
		......
		}

private static Connection fetchConnection(DataSource dataSource) throws SQLException {
		//从多数据源中获取目标数据源,dataSource为DynamicDataSource 
		Connection con = dataSource.getConnection();
		if (con == null) {
			throw new IllegalStateException("DataSource returned null from getConnection(): " + dataSource);
		}
		return con;
	}

	@Override
	public Connection getConnection() throws SQLException {
		//从 DbContextHolder 中数据源切面切到了哪个数据源
		return determineTargetDataSource().getConnection();
	}

三、解决方法

1.开启新事物

在 test4() 方法中新增方法,加上事务,设置事务传播为

Propagation.REQUIRES_NEW 或者 Propagation.NOT_SUPPORTED
 	@Transactional
    @Override
    public void test3() {
        //查询数据源1中的任务
        List<Task> tasks = taskService.createTaskQuery()
                .active()
                .includeProcessVariables()
                .taskCandidateUser("张三")
                .includeIdentityLinks()
                .list();
        System.out.println("任务总数:" + tasks.size());

        //查询数据源2的业务数据
        testService.test4();

    }

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    @Override
    public void test4() {
        List<PdmFileInfo> fileInfos = fileInfoService.list();
        System.out.println("文件总数:" + fileInfos.size());
    }

2.重写事务

(1)多数据源事务

public class MultiDataSourceTransaction implements Transaction {

    private final DataSource dataSource;

    private Connection mainConnection;

    private String mainDatabaseIdentification;

    private ConcurrentMap<String, Connection> otherConnectionMap;


    private boolean isConnectionTransactional;

    private boolean autoCommit;

    private String defaultDbName;

    public MultiDataSourceTransaction(DataSource dataSource, String defaultDbName) {
        if (dataSource == null) {
            throw new RuntimeException("No DataSource specified");
        }
        this.dataSource = dataSource;
        this.defaultDbName = defaultDbName;
        otherConnectionMap = new ConcurrentHashMap<>();
        mainDatabaseIdentification = getDbType();
    }

    private String getDbType() {
        String dbType = DbContextHolder.getDbType();
        if (StringUtils.isBlank(dbType)) {
            return defaultDbName;
        }
        return dbType;
    }


    @Override
    public Connection getConnection() throws SQLException {
        String databaseIdentification = getDbType();

        //现在获取到的mainConnection是从事务管理器获取,或从DynamicDataSource获取目标数据源
        openMainConnection();
        mainDatabaseIdentification = ((ConnectionProxyImpl) ((DruidPooledConnection) mainConnection).getConnection()).getDirectDataSource().getName();
        //校验是否是mainDatabaseIdentification
        if (mainDatabaseIdentification.equalsIgnoreCase(databaseIdentification)) {
            return mainConnection;
        } else {
            if (!otherConnectionMap.containsKey(databaseIdentification)) {
                try {
                    //获取其他数据源
                    Connection conn = dataSource.getConnection();
                    otherConnectionMap.put(databaseIdentification, conn);
                } catch (SQLException ex) {
                    throw new RuntimeException("Could not get JDBC Connection", ex);
                }
            }
            return otherConnectionMap.get(databaseIdentification);
        }
    }


    private void openMainConnection() throws SQLException {
        if (mainConnection == null) {
            this.mainConnection = DataSourceUtils.getConnection(this.dataSource);
            this.autoCommit = this.mainConnection.getAutoCommit();
            this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.mainConnection, this.dataSource);

            if (log.isDebugEnabled()) {
                log.debug(
                        "JDBC Connection ["
                                + this.mainConnection
                                + "] will"
                                + (this.isConnectionTransactional ? " " : " not ")
                                + "be managed by Spring");
            }
        }
    }

    @Override
    public void commit() throws SQLException {
        if (this.mainConnection != null && !this.isConnectionTransactional && !this.autoCommit) {
            if (log.isDebugEnabled()) {
                log.debug("Committing JDBC Connection [" + this.mainConnection + "]");
            }
            this.mainConnection.commit();
            for (Connection connection : otherConnectionMap.values()) {
                connection.commit();
            }
        }
    }

    @Override
    public void rollback() throws SQLException {
        if (this.mainConnection != null && !this.isConnectionTransactional && !this.autoCommit) {
            if (log.isDebugEnabled()) {
                log.debug("Rolling back JDBC Connection [" + this.mainConnection + "]");
            }
            this.mainConnection.rollback();
            for (Connection connection : otherConnectionMap.values()) {
                connection.rollback();
            }
        }
    }

    @Override
    public void close() throws SQLException {
        DataSourceUtils.releaseConnection(this.mainConnection, this.dataSource);
        for (Connection connection : otherConnectionMap.values()) {
            DataSourceUtils.releaseConnection(connection, this.dataSource);
        }
    }

    @Override
    public Integer getTimeout() {
        return null;
    }

}

(2)多数据源事务工厂

public class MultiDataSourceTransactionFactory extends SpringManagedTransactionFactory {

    private String defaultDbName;

    public MultiDataSourceTransactionFactory(String defaultDbName) {
        this.defaultDbName = defaultDbName;
    }

    @Override
    public Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) {
        return new MultiDataSourceTransaction(dataSource, defaultDbName);
    }

}

(3)给数据源设置事务

		MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
        factory.setDataSource(dataSource);
        factory.setTransactionFactory(new MultiDataSourceTransactionFactory(defaultDbName));

总结

        这样只是暂时解决了多数据源的切换,能保证正常访问到目标数据源,但是对于事务回滚时仍存在着一些问题,要想把问题从解决,需要在项目中引入分布式事务。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

flowable 多数据源 的相关文章

随机推荐

  • 手把手教你在vue中使用自定义指令全局封装防抖节流函数

    第一步在src下创建utils文件夹并创建common js文件 utils common js 文件 function throttle bindObj fn delay bindObj prevTime Date now return
  • 高并发和海量数据下的 9 个 Redis 经典案例剖析!

    往期热门文章 1 往期精选优秀博文都在这里了 2 人见人爱 收款码背后的原理是什么 3 这么设计 Redis 10亿数据量只需要100MB内存 4 当Docker遇到Intellij IDEA 再次解放了生产力 5 闲鱼靠什么支撑起万亿的交
  • Qt 窗口几何坐标系统(示例程序): geometry,frameGeometry

    注意事项 geometry和framGeometry 中的几何数据必须在 show 调用后 才有效 Qt 窗口几何布局 geometry frameGeometry https blog csdn net qq 35621436 artic
  • Python编程指南:利用HTTP和HTTPS适配器实现智能路由

    目录 HTTP和HTTPS适配器 什么是智能路由 利用HTTP和HTTPS适配器实现智能路由 总结 在Python编程中 利用HTTP和HTTPS适配器实现智能路由是一项非常实用的技能 智能路由可以根据不同的条件选择不同的路由 从而提高网络
  • Doris---索引

    前缀索引 doris中 对于前缀索引有如下约束 他的索引键最大长度是36个字节 当他遇到了varchar数据类型的时候 即使没有超过36个字节 也会自动截断 示例1 以下表中我们定义了 user id age message作为表的key
  • 2023年最新版Windows环境下

    个人主页 平行线也会相交 欢迎 点赞 收藏 留言 加关注 本文由 平行线也会相交 原创 收录于专栏 JavaSE primary jdk1 8的下载和使用总共分为3个步骤 jdk1 8的下载 jdk1 8的安装 配置环境变量 目录 一 jd
  • Jsp Unescaped xml character报错的解决办法

    鼠标点击IDEA左上角File 进入Settings 快捷键Ctrl Alt S 选择Editor Inspections 并在右边找到HTML下的Malformed content of
  • win终端好用助手——MOBAXterm和Xshell

    目录 0 前言 1 资源 2 区别 2 1MobaXterm 2 1 1ssh 2 1 2串口 2 1 3 tftp服务 2 2Xshell 2 2 1 ssh 3 总结 0 前言 总是推荐歌 大部分都是网易的 不乏一些VIP歌曲 今天分享
  • windows安装rocketmq

    windows安装rocketmq 问题背景 操作步骤 Lyric 请再给我 一个理由 问题背景 最近有使用rocketmq 为测试方便 在本地安装rocketmq 注意事项 默认已安装java1 8 启动mq必须是1 8版本 我之前使用1
  • 解决 fatal: unable to access ‘https://github.com/.../.git‘: Could not resolve host: github.com

    git config global unset http proxy git config global unset https proxy 解决办法 禁用http代理 因为github被GFW墙了 但是这样做没加密不安全
  • CentOS7.4部署ELK日志分析系统

    官网地址 https www elastic co cn 官网权威指南 https www elastic co guide cn elasticsearch guide current index html 安装指南 https www
  • 关于android中图片裁剪以及PorterDuffXfermode的使用经验小结

    1 关于图片 裁剪 出现锯齿的问题 使用canvas的clipXxx函数 可以获取只显露出某一区域的图形 但是有锯齿 即使paint使用了setAntiAlias true 函数依然无法消除锯齿问题 解决方案 使用shader方案 即 pa
  • 激光散斑成像

    激光散斑成像系统原理 当激光照射在足够粗糙 表面的高度变化大于激光的波长 的组织表面 组织表面的散射粒子会使入射光发生背向散射 由于不同散射光到达相机成像面的光程差不同 不同散射光之间会在像面上形成随机干涉现象 在空间分布上表现为明暗变化的
  • linux下查看进程的线程运行情况

    1 首先查看进程的PID 2 查看所在 进程的线程运行情况 top H p 5066
  • vue2 vue-amap plugin ControlBar插件使用和高德3D效果

    网络上有不少vue amap的插件使用教程 我看了许多都没有适用的 要高德3D效果必须用到ControlBar插件 核心是viewMode的开启和ControlBar插件的配置 以下是我试出来的使用方法 在main js中配置全局vue a
  • 基于89C51单片机的智能小车——06.测速小车

    测速模块 用途 广泛用于电机转速检测 脉冲计数 位置限位等 接线 VCC 接电源正极3 3 5V GND 接电源负极 DO TTL开关信号输出 AO 此模块不起作用 测试原理和单位换算 轮子走一圈 经过一个周长 C 2x3 14x半径 3
  • 5. 数学导论 - 图论(图的概念)

    文章目录 图论 Hi 大家好 我是茶桁 今天这节课呢 内容非常的少 少到你可能会认为我偷懒了 还真不是 因为就目前基础来说 图论这一节尚且没有太多可讲的东西 重点是带大家混个脸熟 那么多高强度内容之后 就当给自己放个假吧 图论 前面说过 这
  • 19.学习Camera之——相机驱动层–V4L2框架解析

    相机驱动层 V4L2框架解析 一 概览 相机驱动层位于HAL Moudle与硬件层之间 借助linux内核驱动框架 以文件节点的方式暴露接口给 用户空间 让HAL Module通过标准的文件访问接口 从而能够将请求顺利地下发到内核中 而在内
  • hdu 1827(tarjan)

    先用tarjan缩点 然后入度为0的点就是必须要选择点同时也是最小的情况 Summer Holiday Time Limit 10000 1000 MS Java Others Memory Limit 32768 32768 K Java
  • flowable 多数据源

    目录 前言 一 多数据源 二 测试 1 测试接口 1 不带事务 2 加上事务 三 解决方法 1 开启新事物 2 重写事务 总结 前言 在springboot中使用flowable 此时flowable默认使用spring中的数据源 我这里f