在 SpringBoot 和动态数据源的 MyBatis 应用程序中,查询总是先于 AOP 执行

Query is always execute before than AOP in SpringBoot and MyBatis application for dynamic datasource

这里,我想通过AOP做一个SpringBoot和MyBatis应用程序使用动态数据源;但 AOP 总是在从数据库查询后执行,因此切换数据源无效,因为 select 已完成。

我所有的代码都在https://github.com/helloworlde/SpringBoot-DynamicDataSource/tree/aspect_dao

我的依赖是

compile('org.mybatis.spring.boot:mybatis-spring-boot-starter:1.3.1')
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.springframework.boot:spring-boot-starter-aop')
runtime('mysql:mysql-connector-java')

和application.properties

application.server.db.master.driver-class-name=com.mysql.jdbc.Driver

application.server.db.master.url=jdbc:mysql://localhost/redisapi?useSSL=false
application.server.db.master.port=3306
application.server.db.master.username=root
application.server.db.master.password=ihaveapen*^@#
#application.server.db.master.database=123456
#
## application common config
application.server.db.slave.driver-class-name=com.mysql.jdbc.Driver
application.server.db.slave.url=jdbc:mysql:/localhost/redisapi2?useSSL=false
application.server.db.slave.port=3306
application.server.db.slave.username=root
application.server.db.slave.password=123456
#application.server.db.slave.database=redisapi

mybatis.type-aliases-package=cn.com.hellowood.dynamicdatasource.mapper
mybatis.mapper-locations=mappers/**Mapper.xml

Table

CREATE TABLE product(
  id INT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(50) NOT NULL,
  price DOUBLE(10,2) NOT NULL DEFAULT 0
);

DataSourceConfigur.java

    package cn.com.hellowood.dynamicdatasource.configuration;

    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    import org.springframework.jdbc.datasource.DataSourceTransactionManager;
    import org.springframework.transaction.PlatformTransactionManager;

    import javax.sql.DataSource;
    import java.util.HashMap;
    import java.util.Map;

    @Configuration
    public class DataSourceConfigurer {


        @Bean("master")
        @Primary
        @ConfigurationProperties(prefix = "application.server.db.master")
        public DataSource master() {
            return DataSourceBuilder.create().build();
        }


        @Bean("slave")
        @ConfigurationProperties(prefix = "application.server.db.slave")
        public DataSource slave() {
            return DataSourceBuilder.create().build();
        }


        @Bean("dynamicDataSource")
        public DataSource dynamicDataSource() {
            DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();
            Map<Object, Object> dataSourceMap = new HashMap<>(2);
            dataSourceMap.put("master", master());
            dataSourceMap.put("slave", slave());

            // Set master datasource as default
            dynamicRoutingDataSource.setDefaultTargetDataSource(master());
            // Set master and slave datasource as target datasource
            dynamicRoutingDataSource.setTargetDataSources(dataSourceMap);

            // To put datasource keys into DataSourceContextHolder to judge if the datasource is exist
            DynamicDataSourceContextHolder.dataSourceKeys.addAll(dataSourceMap.keySet());
            return dynamicRoutingDataSource;
        }


        @Bean
        @ConfigurationProperties(prefix = "mybatis")
        public SqlSessionFactoryBean sqlSessionFactoryBean() {
            SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
            // Here is very important, if don't config this, will can't switch datasource
            // put all datasource into SqlSessionFactoryBean, then will autoconfig SqlSessionFactory
            sqlSessionFactoryBean.setDataSource(dynamicDataSource());
            return sqlSessionFactoryBean;
        }


        @Bean
        public PlatformTransactionManager transactionManager() {
            return new DataSourceTransactionManager(dynamicDataSource());
        }
    }

DynamicRoutingDataSource.java

package cn.com.hellowood.dynamicdatasource.configuration;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class DynamicRoutingDataSource extends AbstractRoutingDataSource {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    protected Object determineCurrentLookupKey() {
        logger.info("Current DataSource is [{}]", DynamicDataSourceContextHolder.getDataSourceKey());
        return DynamicDataSourceContextHolder.getDataSourceKey();
    }
}

并且有Controller,Service,Dao查询,但是虽然我把aspect的Order设置为-100,但是还是在AOP之前执行query,请问有没有人能找出哪里错了,非常感谢。 This is log screenshot

最后我解决了这个问题,因为我注入了DataSourceTransactionManager的Bean,所以事务会在Service中打开,所以DAO方面只有在事务完成后才能工作。

删除此代码:

@Bean
public PlatformTransactionManager transactionManager() {
    return new DataSourceTransactionManager(dynamicDataSource());
}