spring项目使用代理方式进行数据源动态切换(支持事务内切换)

网上有很多使用注解 + AOP进行数据源切换的代码,他们统一通过继承spring提供的AbstractRoutingDataSource去重写determineCurrentLookupKey去获得当前的数据源。

我通过查阅大量的资料发现网上基本主流的也是这种做法,但这种做法有一个致命的缺陷,那就是在事务内无法进行数据源切换。

下图展示了传统方式存在的问题:


image.png

我的解决思路是:


image.png

附上核心一段的伪代码,相信有一点技术功底的都可以写出来,如果大家有需要,可以留言,我给大家准备一下。

private class ConnectionProxy implements InvocationHandler {

        private String userName;
        private String password;
        private boolean usePwd;
        private boolean autoCommit = true;

        /**
         * cache connection
         */
        private Map cachedConnectionMap = new HashMap<>(8);

        private ConnectionProxy(){}

        private ConnectionProxy(String userName,String password){
            this.userName = userName;
            this.password = password;
            this.usePwd = true;
        }


        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            String methodName = method.getName();
            if("close".equals(methodName)){
                close();
            }else if("commit".equals(methodName)){
                commit();
            }else if("rollback".equals(methodName)){
                rollback();
            } else if("setAutoCommit".equals(methodName)){
                setAutoCommit((Boolean) args[0]);
            }else{
               // 获取当前上下文的数据源id
                String dsId = DynamicDataSourceContextHolder.peek();
               // 根据数据源id获取connection
                Connection connection = cachedConnectionMap.get(dsId);
                if(connection==null){
                   // 没有connection,手动创建一个,如果有说明当前在事务内,在之前已经被创建过了,直接复用以前的。
                    DataSource dataSource = determineDataSource();
                    connection = usePwd ? dataSource.getConnection(userName,password) : dataSource.getConnection();
                    if(!autoCommit){
                        connection.setAutoCommit(false);
                    }
                    cachedConnectionMap.put(dsId,connection);
                }

                return method.invoke(connection,args);
            }
        }



        private void setAutoCommit(final boolean autoCommit) throws SQLException {
            this.autoCommit = autoCommit;
           for (Connection connection : cachedConnectionMap.values()) {
                connection.setAutoCommit(autoCommit);
            }
        }

        private void commit() throws SQLException {
            for (Connection connection : cachedConnectionMap.values()) {
                connection.commit();
            }
        }

        private void rollback() throws SQLException {
           for (Connection connection : cachedConnectionMap.values()) {
                connection.rollback();
            }
        }

        private void close() throws SQLException {
            for (Connection connection : cachedConnectionMap.values()) {
                connection.close();
            }
        }
    }

你可能感兴趣的:(spring项目使用代理方式进行数据源动态切换(支持事务内切换))