zoukankan      html  css  js  c++  java
  • Spring Boot 入门实战(7)--JdbcTempalte、Mybatis、动态数据源及 Atomicos 整合(XA 事务)

    本文主要介绍 JdbcTempalte、Mybatis 在动态数据源下的配置及使用,实际的应用里可以根据情况选择其中之一或同时使用;动态数据源使用自定义的注解通过切面来实现数据源的切换;事务使用基于 Atomicos 的 XA 事务。文中所使用到的软件版本:Spring Boot 2.4.4、jdk1.8.0_181、Mybatis 3.5.6、Atomicos 4.0.6。

    1、工程整体结构

    2、pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.abc.demo</groupId>
        <artifactId>demosb-dd</artifactId>
        <version>1.0</version>
    
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.4.4</version>
            <relativePath />
        </parent>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-aop</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jta-atomikos</artifactId>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.1.4</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjrt</artifactId>
            </dependency>
    
            <dependency>
                <groupId>commons-lang</groupId>
                <artifactId>commons-lang</artifactId>
                <version>2.6</version>
            </dependency>
    
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
            <dependency>
                <groupId>com.oracle</groupId>
                <artifactId>ojdbc6</artifactId>
                <version>11.2.0.2.0</version>
                <!--systemPath>E:/bin/jar/oracle/ojdbc6.jar</systemPath>
                <scope>system</scope-->
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
        </dependencies>
    
        <build>
            <resources>
                <resource>
                    <directory>src/main/resources</directory>
                </resource>
                <resource>
                    <directory>src/main/java</directory>
                    <includes>
                        <include>**/*.xml</include>
                    </includes>
                </resource>
            </resources>
    
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    </project>
    pom.xml

    3、application.yml

    spring:
      datasource:
        atomic:
          datasource1:
            uniqueResourceName: datasource1
            xaDataSourceClassName: com.mysql.cj.jdbc.MysqlXADataSource
            testQuery: select 1
            maxPoolSize: 2
            minPoolSize: 2
            poolSize: 2
            borrowConnectionTimeout: 60
            reapTimeout: 360
            maxIdleTime: 60
            maintenanceInterval: 60
            loginTimeout: 60
            xaProperties:
              URL: jdbc:mysql://10.198.0.10:3306/itest?useUnicode=true&characterEncoding=UTF-8
              user: root
              password: 123456
          datasource2:
            uniqueResourceName: datasource2
            xaDataSourceClassName: oracle.jdbc.xa.client.OracleXADataSource
            testQuery: select 1 from dual
            maxPoolSize: 2
            minPoolSize: 2
            poolSize: 2
            borrowConnectionTimeout: 60
            reapTimeout: 360
            maxIdleTime: 60
            maintenanceInterval: 60
            loginTimeout: 60
            xaProperties:
              URL: jdbc:oracle:thin:@10.198.0.10:1521:test
              user: test
              password: 123456

    4、配置

    4.1、动态数据源配置

    4.1.1、自定义数据源注解

    package com.abc.demo.datasource;
    
    import java.lang.annotation.*;
    
    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    public @interface DataSource {
        String value() default "";
    }

    4.1.2、自定义数据源切面

    package com.abc.demo.datasource;
    
    
    import org.apache.commons.lang.StringUtils;
    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.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.core.Ordered;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.Method;
    
    
    @Aspect
    @Component
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public class DataSourceAspect {
        private static Logger logger = LoggerFactory.getLogger(DataSourceAspect.class);
    
        @Pointcut("@annotation(com.abc.demo.datasource.DataSource) || @within(com.abc.demo.datasource.DataSource)")
        public void dataSourcePointCut() {
            Object d;
        }
    
        //@Around("dataSourcePointCut()")
        @Around("execution(* com.abc.demo.dao..*.*(..))")
        public Object around(ProceedingJoinPoint point) throws Throwable {
            MethodSignature signature = (MethodSignature) point.getSignature();
            Class targetClass = point.getTarget().getClass();
            Method method = signature.getMethod();
    
            String dataSourceName = "";
            DataSource targetDataSource = (DataSource)targetClass.getAnnotation(DataSource.class);
            DataSource methodDataSource = method.getAnnotation(DataSource.class);
            if (targetDataSource != null || methodDataSource != null){
                if (methodDataSource != null) {
                    dataSourceName = methodDataSource.value();
                } else {
                    dataSourceName = targetDataSource.value();
                }
    
                //从接口中找注解
            } else {
                for (Class<?> clazz : targetClass.getInterfaces()) {
                    Class<?>[] types = method.getParameterTypes();
    
                    //接口上的注解
                    if (clazz.isAnnotationPresent(DataSource.class)) {
                        DataSource source = clazz.getAnnotation(DataSource.class);
                        dataSourceName = source.value();
                    }
    
                    //接口方法注解
                    Method m = clazz.getMethod(method.getName(), types);
                    if (m != null && m.isAnnotationPresent(DataSource.class)) {
                        DataSource source = m.getAnnotation(DataSource.class);
                        dataSourceName = source.value();
                    }
    
                    if (StringUtils.isNotBlank(dataSourceName)) {
                        break;
                    }
                }
            }
    
            if (StringUtils.isNotBlank(dataSourceName)) {
                DynamicContextHolder.push(dataSourceName);
                logger.info("the datasource is {}", dataSourceName);
            }
            try {
                return point.proceed();
            } finally {
                if (StringUtils.isNotBlank(dataSourceName)) {
                    DynamicContextHolder.poll();
                }
                logger.info("clean datasource");
            }
        }
    }

    4.1.3、动态数据源线程中实际数据源保存

    package com.abc.demo.datasource;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.util.ArrayDeque;
    import java.util.Deque;
    
    public class DynamicContextHolder {
        private static Logger logger = LoggerFactory.getLogger(DynamicContextHolder.class);
    
        private static final ThreadLocal<Deque<String>> CONTEXT_HOLDER = new ThreadLocal() {
            @Override
            protected Object initialValue() {
                return new ArrayDeque();
            }
        };
    
        /**
         * 获得当前线程数据源
         * @return 数据源名称
         */
        public static String peek() {
            String key = CONTEXT_HOLDER.get().peek();
            logger.info("key={}", key);
            return key;
        }
    
        /**
         * 设置当前线程数据源
         * @param dataSource 数据源名称
         */
        public static void push(String dataSource) {
            CONTEXT_HOLDER.get().push(dataSource);
        }
    
        /**
         * 清空当前线程数据源
         */
        public static void poll() {
            Deque<String> deque = CONTEXT_HOLDER.get();
            deque.poll();
            if (deque.isEmpty()) {
                CONTEXT_HOLDER.remove();
            }
        }
    
    }

    4.1.4、自定义动态数据源

    package com.abc.demo.datasource;
    
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    
    public class DynamicDataSource extends AbstractRoutingDataSource {
        @Override
        protected Object determineCurrentLookupKey() {
            return DynamicContextHolder.peek();
        }
    
        public javax.sql.DataSource getTargetDataSource() {
            return this.determineTargetDataSource();
        }
    
    }

    4.1.5、事务及动态数据源配置

    package com.abc.demo.datasource;
    
    import com.atomikos.icatch.jta.UserTransactionImp;
    import com.atomikos.icatch.jta.UserTransactionManager;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.DependsOn;
    import org.springframework.transaction.PlatformTransactionManager;
    import org.springframework.transaction.jta.JtaTransactionManager;
    
    import javax.sql.DataSource;
    import javax.transaction.UserTransaction;
    import java.util.HashMap;
    import java.util.Map;
    
    @Configuration
    public class DynamicDataSourceConfig {
        private static Logger logger = LoggerFactory.getLogger(DynamicDataSourceConfig.class);
    
        @Bean(name = "userTransaction")
        public UserTransaction userTransaction() throws Throwable {
            UserTransactionImp userTransactionImp = new UserTransactionImp();
            userTransactionImp.setTransactionTimeout(10000);
            return userTransactionImp;
        }
    
        @Bean(name = "userTransactionManager", initMethod = "init", destroyMethod = "close")
        public UserTransactionManager userTransactionManager() {
            UserTransactionManager userTransactionManager = new UserTransactionManager();
            userTransactionManager.setForceShutdown(false);
            return userTransactionManager;
        }
    
        @Bean(name = "xatm")
        @DependsOn({"userTransaction", "userTransactionManager"})
        public PlatformTransactionManager transactionManager() throws Throwable {
            return new JtaTransactionManager(userTransaction(), userTransactionManager());
        }
    
        @Bean(name = "dataSource1")
        @ConfigurationProperties(prefix="spring.datasource.atomic.datasource1")
        public DataSource dataSource1() {
            return new AtomikosDataSourceBean();
        }
    
        @Bean(name = "dataSource2")
        @ConfigurationProperties(prefix="spring.datasource.atomic.datasource2")
        public DataSource dataSource2() {
            return new AtomikosDataSourceBean();
        }
    
        @Bean(name = "dynamicDataSource")
        @DependsOn({"xatm"})
        public DynamicDataSource dynamicDataSource() {
            DynamicDataSource dynamicDataSource = new DynamicDataSource();
            Map<Object, Object> targetDataSources = new HashMap<>();
            targetDataSources.put("dataSource1", dataSource1());
            targetDataSources.put("dataSource2", dataSource2());
            dynamicDataSource.setTargetDataSources(targetDataSources);
    
            dynamicDataSource.setDefaultTargetDataSource(dataSource1());
    
            return dynamicDataSource;
        }
    }

    4.2、JdbcTemplate 配置

    package com.abc.demo.config;
    
    import com.abc.demo.datasource.DynamicDataSource;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.jdbc.core.JdbcTemplate;
    
    import javax.sql.DataSource;
    
    
    @Configuration
    public class JdbcTempalteConfig {
    //    @Bean
    //    public JdbcTemplate jdbcTemplate(@Qualifier("dynamicDataSource") DynamicDataSource dynamicDataSource) {
    //        return new JdbcTemplate(dynamicDataSource);
    //    }
    
        @Bean
        public MyJdbcTemplate jdbcTemplate(@Qualifier("dynamicDataSource") DynamicDataSource dynamicDataSource) {
            return new MyJdbcTemplate(dynamicDataSource);
        }
    
        /**
         * 需重写JdbcTemplate的getDataSource()方法,使之获取实际的数据源,否则在启动事务时不能切换数据源
         */
        class MyJdbcTemplate extends JdbcTemplate {
            public MyJdbcTemplate(DataSource dataSource) {
                super(dataSource);
            }
    
            @Override
            public DataSource getDataSource() {
                DynamicDataSource dynamicDataSource =  (DynamicDataSource) super.getDataSource();
                DataSource targetDataSource = dynamicDataSource.getTargetDataSource();
                return targetDataSource;
            }
        }
    }

    4.3、Mybatis配置

    4.3.1、重写 SqlSessionTemplate

    拷贝一份SqlSessionTemplate代码进行重写,主要需要重写getSqlSessionFactory、getConfiguration方法及内部类SqlSessionInterceptor。

    @Override
    public SqlSessionFactory getSqlSessionFactory() {
        String key = DynamicContextHolder.peek();
        if (key == null) {
            return defaultSqlSessionFactory;
        }
        return sqlSessionFactorys.get(key);
    }
    
    @Override
    public org.apache.ibatis.session.Configuration getConfiguration() {
        return this.getSqlSessionFactory().getConfiguration();
    }
    
    private class SqlSessionInterceptor implements InvocationHandler {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            SqlSession sqlSession = getSqlSession(DynamicSqlSessionTemplate.this.getSqlSessionFactory(),
                    DynamicSqlSessionTemplate.this.executorType, DynamicSqlSessionTemplate.this.exceptionTranslator);
            try {
                Object result = method.invoke(sqlSession, args);
                if (!isSqlSessionTransactional(sqlSession, DynamicSqlSessionTemplate.this.sqlSessionFactory)) {
                    // force commit even on non-dirty sessions because some databases require
                    // a commit/rollback before calling close()
                    sqlSession.commit(true);
                }
                return result;
            } catch (Throwable t) {
                Throwable unwrapped = unwrapThrowable(t);
                if (DynamicSqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
                    // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
                    closeSqlSession(sqlSession, DynamicSqlSessionTemplate.this.getSqlSessionFactory());
                    sqlSession = null;
                    Throwable translated = DynamicSqlSessionTemplate.this.exceptionTranslator
                            .translateExceptionIfPossible((PersistenceException) unwrapped);
                    if (translated != null) {
                        unwrapped = translated;
                    }
                }
                throw unwrapped;
            } finally {
                if (sqlSession != null) {
                    closeSqlSession(sqlSession, DynamicSqlSessionTemplate.this.getSqlSessionFactory());
                }
            }
        }
    }

    完整的代码如下:

    package com.abc.demo.config;
    
    import com.abc.demo.datasource.DynamicContextHolder;
    import org.apache.ibatis.cursor.Cursor;
    import org.apache.ibatis.exceptions.PersistenceException;
    import org.apache.ibatis.executor.BatchResult;
    import org.apache.ibatis.session.*;
    import org.mybatis.spring.MyBatisExceptionTranslator;
    import org.mybatis.spring.SqlSessionTemplate;
    import org.springframework.beans.factory.DisposableBean;
    import org.springframework.dao.support.PersistenceExceptionTranslator;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.sql.Connection;
    import java.util.List;
    import java.util.Map;
    
    import static java.lang.reflect.Proxy.newProxyInstance;
    import static org.apache.ibatis.reflection.ExceptionUtil.unwrapThrowable;
    import static org.mybatis.spring.SqlSessionUtils.*;
    import static org.mybatis.spring.SqlSessionUtils.closeSqlSession;
    
    /**
     * 重写SqlSessionTemplate
     */
    public class DynamicSqlSessionTemplate extends SqlSessionTemplate {
        private SqlSessionFactory sqlSessionFactory;
        private ExecutorType executorType;
        private SqlSession sqlSessionProxy;
        private PersistenceExceptionTranslator exceptionTranslator;
    
        private SqlSessionFactory defaultSqlSessionFactory = null;
        private Map<String, SqlSessionFactory> sqlSessionFactorys = null;
    
        public DynamicSqlSessionTemplate(SqlSessionFactory defaultSqlSessionFactory, Map<String, SqlSessionFactory> sqlSessionFactorys) {
            this(defaultSqlSessionFactory);
    
            this.defaultSqlSessionFactory = defaultSqlSessionFactory;
            this.sqlSessionFactorys = sqlSessionFactorys;
        }
    
        public DynamicSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
            this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
        }
    
        public DynamicSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
            this(sqlSessionFactory, executorType,
                    new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true));
        }
    
        public DynamicSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
            super(sqlSessionFactory, executorType, exceptionTranslator);
    
            this.sqlSessionFactory = sqlSessionFactory;
            this.executorType = executorType;
            this.exceptionTranslator = exceptionTranslator;
            this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
                    new Class[] { SqlSession.class }, new SqlSessionInterceptor());
        }
    
        @Override
        public SqlSessionFactory getSqlSessionFactory() {
            String key = DynamicContextHolder.peek();
            if (key == null) {
                return defaultSqlSessionFactory;
            }
            return sqlSessionFactorys.get(key);
        }
    
        @Override
        public org.apache.ibatis.session.Configuration getConfiguration() {
            return this.getSqlSessionFactory().getConfiguration();
        }
    
        public ExecutorType getExecutorType() {
            return this.executorType;
        }
    
        public PersistenceExceptionTranslator getPersistenceExceptionTranslator() {
            return this.exceptionTranslator;
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public <T> T selectOne(String statement) {
            return this.sqlSessionProxy.selectOne(statement);
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public <T> T selectOne(String statement, Object parameter) {
            return this.sqlSessionProxy.selectOne(statement, parameter);
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public <K, V> Map<K, V> selectMap(String statement, String mapKey) {
            return this.sqlSessionProxy.selectMap(statement, mapKey);
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {
            return this.sqlSessionProxy.selectMap(statement, parameter, mapKey);
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
            return this.sqlSessionProxy.selectMap(statement, parameter, mapKey, rowBounds);
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public <T> Cursor<T> selectCursor(String statement) {
            return this.sqlSessionProxy.selectCursor(statement);
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public <T> Cursor<T> selectCursor(String statement, Object parameter) {
            return this.sqlSessionProxy.selectCursor(statement, parameter);
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds) {
            return this.sqlSessionProxy.selectCursor(statement, parameter, rowBounds);
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public <E> List<E> selectList(String statement) {
            return this.sqlSessionProxy.selectList(statement);
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public <E> List<E> selectList(String statement, Object parameter) {
            return this.sqlSessionProxy.selectList(statement, parameter);
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
            return this.sqlSessionProxy.selectList(statement, parameter, rowBounds);
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public void select(String statement, ResultHandler handler) {
            this.sqlSessionProxy.select(statement, handler);
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public void select(String statement, Object parameter, ResultHandler handler) {
            this.sqlSessionProxy.select(statement, parameter, handler);
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
            this.sqlSessionProxy.select(statement, parameter, rowBounds, handler);
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public int insert(String statement) {
            return this.sqlSessionProxy.insert(statement);
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public int insert(String statement, Object parameter) {
            return this.sqlSessionProxy.insert(statement, parameter);
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public int update(String statement) {
            return this.sqlSessionProxy.update(statement);
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public int update(String statement, Object parameter) {
            return this.sqlSessionProxy.update(statement, parameter);
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public int delete(String statement) {
            return this.sqlSessionProxy.delete(statement);
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public int delete(String statement, Object parameter) {
            return this.sqlSessionProxy.delete(statement, parameter);
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public <T> T getMapper(Class<T> type) {
            return getConfiguration().getMapper(type, this);
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public void commit() {
            throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession");
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public void commit(boolean force) {
            throw new UnsupportedOperationException("Manual commit is not allowed over a Spring managed SqlSession");
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public void rollback() {
            throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession");
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public void rollback(boolean force) {
            throw new UnsupportedOperationException("Manual rollback is not allowed over a Spring managed SqlSession");
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public void close() {
            throw new UnsupportedOperationException("Manual close is not allowed over a Spring managed SqlSession");
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public void clearCache() {
            this.sqlSessionProxy.clearCache();
        }
    
    
        /**
         * {@inheritDoc}
         */
        @Override
        public Connection getConnection() {
            return this.sqlSessionProxy.getConnection();
        }
    
        /**
         * {@inheritDoc}
         *
         * @since 1.0.2
         *
         */
        @Override
        public List<BatchResult> flushStatements() {
            return this.sqlSessionProxy.flushStatements();
        }
    
        /**
         * Allow gently dispose bean:
         *
         * <pre>
         * {@code
         *
         * <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
         *  <constructor-arg index="0" ref="sqlSessionFactory" />
         * </bean>
         * }
         * </pre>
         *
         * The implementation of {@link DisposableBean} forces spring context to use {@link DisposableBean#destroy()} method
         * instead of {@link SqlSessionTemplate#close()} to shutdown gently.
         *
         * @see SqlSessionTemplate#close()
         * @see "org.springframework.beans.factory.support.DisposableBeanAdapter#inferDestroyMethodIfNecessary(Object, RootBeanDefinition)"
         * @see "org.springframework.beans.factory.support.DisposableBeanAdapter#CLOSE_METHOD_NAME"
         */
        @Override
        public void destroy() throws Exception {
            // This method forces spring disposer to avoid call of SqlSessionTemplate.close() which gives
            // UnsupportedOperationException
        }
    
        private class SqlSessionInterceptor implements InvocationHandler {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                SqlSession sqlSession = getSqlSession(DynamicSqlSessionTemplate.this.getSqlSessionFactory(),
                        DynamicSqlSessionTemplate.this.executorType, DynamicSqlSessionTemplate.this.exceptionTranslator);
                try {
                    Object result = method.invoke(sqlSession, args);
                    if (!isSqlSessionTransactional(sqlSession, DynamicSqlSessionTemplate.this.sqlSessionFactory)) {
                        // force commit even on non-dirty sessions because some databases require
                        // a commit/rollback before calling close()
                        sqlSession.commit(true);
                    }
                    return result;
                } catch (Throwable t) {
                    Throwable unwrapped = unwrapThrowable(t);
                    if (DynamicSqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
                        // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
                        closeSqlSession(sqlSession, DynamicSqlSessionTemplate.this.getSqlSessionFactory());
                        sqlSession = null;
                        Throwable translated = DynamicSqlSessionTemplate.this.exceptionTranslator
                                .translateExceptionIfPossible((PersistenceException) unwrapped);
                        if (translated != null) {
                            unwrapped = translated;
                        }
                    }
                    throw unwrapped;
                } finally {
                    if (sqlSession != null) {
                        closeSqlSession(sqlSession, DynamicSqlSessionTemplate.this.getSqlSessionFactory());
                    }
                }
            }
        }
    }
    DynamicSqlSessionTemplate.java

    4.3.2、Mybatis 配置

    package com.abc.demo.config;
    
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.mybatis.spring.SqlSessionTemplate;
    import org.mybatis.spring.annotation.MapperScan;
    import org.mybatis.spring.annotation.MapperScans;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    
    import javax.sql.DataSource;
    import java.util.HashMap;
    import java.util.Map;
    
    @Configuration
    @MapperScans(value = {@MapperScan(basePackages = {"com.abc.demo.dao"}, sqlSessionTemplateRef = "dynamicSqlSessionTemplate")})
    public class MybatisConfig {
        @Qualifier("dataSource1")
        @Autowired
        private DataSource dataSource1;
    
        @Qualifier("dataSource2")
        @Autowired
        private DataSource dataSource2;
        
        @Bean("sqlSessionFactory1")
        public SqlSessionFactory sqlSessionFactory1() {
            try {
                SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
                factoryBean.setDataSource(dataSource1);
                factoryBean.setConfigLocation(
                        new PathMatchingResourcePatternResolver().getResource("classpath:mybatis/mybatis-config.xml"));
                factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:com/abc/demo/dao/*.xml"));
                return factoryBean.getObject();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    
        @Bean("sqlSessionFactory2")
        public SqlSessionFactory sqlSessionFactory2() {
            try {
                SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
                factoryBean.setDataSource(dataSource2);
                factoryBean.setConfigLocation(
                        new PathMatchingResourcePatternResolver().getResource("classpath:mybatis/mybatis-config.xml"));
                factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:com/abc/demo/dao/*.xml"));
                return factoryBean.getObject();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    
        @Bean("dynamicSqlSessionTemplate")
        public SqlSessionTemplate sqlSessionTemplate() {
            Map<String, SqlSessionFactory> sqlSessionFactorys = new HashMap<>();
            //这边设置的key需与动态数据源里设置的key一致
            sqlSessionFactorys.put("dataSource1", sqlSessionFactory1());
            sqlSessionFactorys.put("dataSource2", sqlSessionFactory2());
            return new DynamicSqlSessionTemplate(sqlSessionFactory1(), sqlSessionFactorys);
        }
    }

    5、业务代码

    5.1、实体类

    package com.abc.demo.entity;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import lombok.ToString;
    
    
    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    @ToString
    public class School {
        private String name;
    
        private String location;
    }
    School.java

    School实体类对应a_school表,放在第一个数据库里面。

    package com.abc.demo.entity;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import lombok.ToString;
    
    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    @ToString
    public class Student {
        private String name;
    
        private Integer age;
    }
    Student.java

    Student实体类对应a_student表,放在第一二个数据库里面。

    5.2、DAO

    package com.abc.demo.dao;
    
    import com.abc.demo.datasource.DataSource;
    import com.abc.demo.entity.School;
    import com.abc.demo.entity.Student;
    
    import java.util.List;
    
    public interface IBusinessDao {
        @DataSource("dataSource1")
        void insertSchool(School school);
    
        @DataSource("dataSource1")
        List<School> selectSchool();
    
        @DataSource("dataSource2")
        void insertStudent(Student student);
    
        @DataSource("dataSource2")
        List<Student> selectStudent();
    }

    5.1.1、Jdbc实现

    package com.abc.demo.dao.impl;
    
    import com.abc.demo.dao.IBusinessDao;
    import com.abc.demo.datasource.DynamicDataSource;
    import com.abc.demo.entity.School;
    import com.abc.demo.entity.Student;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.jdbc.datasource.DataSourceUtils;
    import org.springframework.stereotype.Repository;
    
    import javax.sql.DataSource;
    import java.sql.*;
    import java.util.ArrayList;
    import java.util.List;
    
    
    @Repository("businessDaoImplJdbc")
    public class BusinessDaoImplJdbc implements IBusinessDao {
        private static Logger logger = LoggerFactory.getLogger(BusinessDaoImplJdbc.class);
    
        @Qualifier("dynamicDataSource")
        @Autowired
        private DataSource dataSource;
    
        private Connection getConnection() {
            DynamicDataSource dynamicDataSource = (DynamicDataSource)dataSource;
            return DataSourceUtils.getConnection(dynamicDataSource.getTargetDataSource());
        }
        private void releaseConnection(Connection con) {
            DynamicDataSource dynamicDataSource = (DynamicDataSource)dataSource;
            DataSourceUtils.releaseConnection(con, dynamicDataSource.getTargetDataSource());
        }
    
        @Override
        public void insertSchool(School school) {
            logger.info("insertSchool");
            Connection con = getConnection();
            PreparedStatement pst = null;
            try {
                pst = con.prepareStatement("insert into a_school(name,location) values(?,?)");
                pst.setString(1, school.getName());
                pst.setString(2, school.getLocation());
    
                pst.executeUpdate();
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                releaseConnection(con);
            }
        }
    
        @Override
        public List<School> selectSchool() {
            logger.info("selectSchool");
            Connection con = getConnection();
            Statement st = null;
            ResultSet rs = null;
            List<School> schools = new ArrayList<>();
            try {
                st = con.createStatement();
                rs = st.executeQuery("select * from a_school");
                while (rs.next()) {
                    School school = new School();
                    school.setName(rs.getString("name"));
                    school.setLocation(rs.getString("location"));
                    schools.add(school);
                }
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                releaseConnection(con);
            }
            return schools;
        }
    
        @Override
        public void insertStudent(Student student) {
            logger.info("insertStudent");
            Connection con = getConnection();
            PreparedStatement pst = null;
            try {
                pst = con.prepareStatement("insert into a_student(name,age) values(?,?)");
                pst.setString(1, student.getName());
                pst.setInt(2, student.getAge());
                pst.addBatch();
    
                pst.executeBatch();
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                releaseConnection(con);
            }
        }
    
        @Override
        public List<Student> selectStudent() {
            logger.info("selectStudent");
            Connection con = getConnection();
            Statement st = null;
            ResultSet rs = null;
            List<Student> students = new ArrayList<>();
            try {
                st = con.createStatement();
                rs = st.executeQuery("select * from a_student");
                while (rs.next()) {
                    Student student = new Student();
                    student.setName(rs.getString("name"));
                    student.setAge(rs.getInt("age"));
                    students.add(student);
                }
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                releaseConnection(con);
            }
            return students;
        }
    }

    5.1.2、JdbcTemplate实现

    package com.abc.demo.dao.impl;
    
    import com.abc.demo.dao.IBusinessDao;
    import com.abc.demo.entity.School;
    import com.abc.demo.entity.Student;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.jdbc.core.BeanPropertyRowMapper;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.stereotype.Repository;
    
    import java.util.List;
    
    
    @Repository("businessDaoImplJdbcTemplate")
    public class BusinessDaoImplJdbcTemplate implements IBusinessDao {
        private static Logger logger = LoggerFactory.getLogger(BusinessDaoImplJdbcTemplate.class);
    
        @Autowired
        private JdbcTemplate jdbcTemplate;
    
        @Override
        public void insertSchool(School school) {
            logger.info("insertSchool");
            jdbcTemplate.update("insert into a_school(name,location) values(?,?)", school.getName(), school.getLocation());
        }
    
        @Override
        public List<School> selectSchool() {
            logger.info("selectSchool");
            return jdbcTemplate.query("select * from a_school", new BeanPropertyRowMapper<>(School.class));
        }
    
        @Override
        public void insertStudent(Student student) {
            logger.info("insertStudent");
            jdbcTemplate.update("insert into a_student(name,age) values(?,?)", student.getName(), student.getAge());
        }
    
        @Override
        public List<Student> selectStudent() {
            logger.info("selectStudent");
            return jdbcTemplate.query("select * from a_student", new BeanPropertyRowMapper<>(Student.class));
        }
    }

    5.1.3、Mybatis实现

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    
    <mapper namespace="com.abc.demo.dao.IBusinessDao" >
        <insert id="insertSchool" parameterType="com.abc.demo.entity.School">
            insert into a_school(name,location) values(#{name}, #{location})
        </insert>
    
        <select id="selectSchool" resultType="com.abc.demo.entity.School">
            select name,location from a_school
        </select>
    
        <insert id="insertStudent" parameterType="com.abc.demo.entity.Student">
            insert into a_student(name,age) values(#{name}, #{age})
        </insert>
    
        <select id="selectStudent" resultType="com.abc.demo.entity.Student">
            select name,age from a_student
        </select>
    </mapper>

    5.3、Service

    package com.abc.demo.service;
    
    import java.util.List;
    import java.util.Map;
    
    public interface IBusinessService {
        void addSchoolAndStudent();
    
        Map<String, List<Object>> querySchoolAndStudent();
    }
    package com.abc.demo.service.impl;
    
    import com.abc.demo.dao.IBusinessDao;
    import com.abc.demo.entity.School;
    import com.abc.demo.entity.Student;
    import com.abc.demo.service.IBusinessService;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    @Service
    public class BusinessServiceImpl implements IBusinessService {
        private static Logger logger = LoggerFactory.getLogger(BusinessServiceImpl.class);
    
        /**
         * 可以切换不同的DAO实现,都可以实现同样的效果
         */
        //@Qualifier("IBusinessDao") //Mybatis实现
        //@Qualifier("businessDaoImplJdbc")
        @Qualifier("businessDaoImplJdbcTemplate")
        @Autowired
        private IBusinessDao businessDao;
    
        @Transactional
        @Override
        public void addSchoolAndStudent() {
            businessDao.insertSchool(new School("南京大学", "南京"));
            businessDao.insertSchool(new School("北京大学", "北京"));
            businessDao.insertStudent(new Student("李白", 20));
            businessDao.insertStudent(new Student("杜甫", 21));
        }
    
        @Override
        public Map<String, List<Object>> querySchoolAndStudent() {
            List<School> schools = businessDao.selectSchool();
            List<Student> students = businessDao.selectStudent();
            return new HashMap(){{
                put("school", schools);
                put("student", students);
            }};
        }
    }

    6、Controller

    package com.abc.demo.controller;
    
    import com.abc.demo.entity.R;
    import com.abc.demo.service.IBusinessService;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.List;
    import java.util.Map;
    
    @RestController
    @RequestMapping("/business")
    public class BusinessController {
        protected static Logger logger = LoggerFactory.getLogger(BusinessController.class);
    
        @Autowired
        private IBusinessService businessService;
    
    
        @RequestMapping("addSchoolAndStudent")
        public R addSchoolAndStudent() {
            businessService.addSchoolAndStudent();
            return R.ok();
        }
    
        @RequestMapping("querySchoolAndStudent")
        public R querySchool() {
            Map<String, List<Object>> map =  businessService.querySchoolAndStudent();
            return R.ok(map);
        }
    
    }
    BusinessController.java

    Controller用到的返回对象R:

    package com.abc.demo.entity;
    
    /**
     * 返回数据
     */
    public class R {
        private static final long serialVersionUID = 1L;
    
        /**
         * 返回码
         * 0 正常,其他异常
         */
        private int returnCode = 0;
    
        /**
         * 描述
         */
        private String description = "OK";
    
        /**
         * 结果数据
         */
        private Object result;
    
        public int getReturnCode() {
            return returnCode;
        }
        public String getDescription() {
            return description;
        }
        public Object getResult() {
            return result;
        }
    
        public static R ok() {
            return new R();
        }
    
        public static R ok(String description) {
            R r = new R();
            r.description = description;
            return r;
        }
    
        public static R ok(Object result) {
            R r = new R();
            r.result = result;
            return r;
        }
        
        public static R error() {
            R r = new R();
            r.returnCode = -1;
            r.description = "未知异常,请联系管理员";
            return r;
        }
        
        public static R error(int returnCode, String description) {
            R r = new R();
            r.returnCode = returnCode;
            r.description = description;
            return r;
        }
    
    }
    R.java
  • 相关阅读:
    IG GROUP开源RESTdoclet项目
    Visual Studio 2012 Update 1抢先看
    使用 Windows Azure 移动服务将云添加到您的应用
    WMF 3.0 RTM(包含PowerShell 3.0 )业已发布
    Node.js 0.9.2 发布(非稳定版)
    vsftpd 3.0.1 正式版发布
    Piggydb 6.2 发布,个人知识库管理
    Apache Commons Codec 1.7 发布
    Notepad++ 6.1.8 正式版发布
    gWaei 3.6.0 发布,英日词典
  • 原文地址:https://www.cnblogs.com/wuyongyin/p/14591115.html
Copyright © 2011-2022 走看看