springboot整合mybatis并配置多数据源

原理

通过spring的AOP获取方法上的注解判断需要哪个数据源,给ThreadLocal绑定数据源的key,动态数据源类DynamicDataSource继承AbstractRoutingDataSource,构造方法里面配置需要路由的所有的数据源,以key-value形式,并重写路由方法,路由规则为根据ThreadLocal来获取数据源的key来决定选择哪个数据源

代码

包结构
在这里插入图片描述
pom

  <dependencies>
        <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.21</version>
        </dependency>
        <!-- druid依赖的日志包 -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.1</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
          <!--aop 配置动态数据源使用-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
        </dependency>
        <!--aop实现-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>


        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>

yml

spring:
  datasource:
    local:
      username: root
      password: root
      driver-class-name: com.mysql.cj.jdbc.Driver
      jdbc-url: jdbc:mysql://localhost:3306/law?serverTimezone=UTC&useUnicode=true@characterEncoding=utf-8
    remote:
      username: root
      password: root
      driver-class-name: com.mysql.cj.jdbc.Driver
      jdbc-url: jdbc:mysql://localhost:3306/law_file?serverTimezone=UTC&useUnicode=true@characterEncoding=utf-8

mybatis:
  mapper-locations: classpath:mybatis/mapper/*.xml
  config-location: classpath:/config/mybatis-config.xml

启动类
排除数据源自动配置类,以免启动时报错(依赖循环注入)

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class SpringbootMybatisDatadourceApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootMybatisDatadourceApplication.class, args);
    }

}

在这里插入图片描述

mybatis全局配置文件mybatis-config.xml
此处配置了别名,主要是因为mapper.xml报红,虽然不影响运行

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "mybatis-3-config.dtd" >
<configuration>
   <typeAliases>
       <package name="com.example.domain"/>
   </typeAliases>
</configuration>

UserMapper.xml

<?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.example.dao.UserDao">

    <select id="selectAll" resultType="user">
        select * from user
    </select>
</mapper>

UserDao
推荐写两个注解
@Mapper将实现类注入spring,可以在启动类上面写@MapperScan代替
@Repository 可以不写,主要是因为其他类注入dao会报红

@Mapper
@Repository
public interface UserDao {
    List<User> selectAll();
}

动态数据源
新建config包,下面的类全部放入其中

public enum DataSourceType {
    REMOTE,
    LOCAL


}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
    /**
     * 切换数据源名称
     */
    DataSourceType value() default DataSourceType.REMOTE;
}
@Aspect
@Order(1)
@Component
public class DataSourceAspect {

    @Pointcut("@annotation(com.example.config.DataSource)")
    public void dsPointCut() {

    }

    @Around("dsPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        DataSource dataSource = method.getAnnotation(DataSource.class);
        if (dataSource != null) {
            DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());
        }
        try {
            return point.proceed();
        } finally {
            // 销毁数据源 在执行方法之后
            DynamicDataSourceContextHolder.clearDataSourceType();
        }
    }
}
@Configuration
public class DataSourceConfig {
    @Bean
    @ConfigurationProperties("spring.datasource.remote")
    public DataSource remoteDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties("spring.datasource.local")
    public DataSource localDataSource() {
        return DataSourceBuilder.create().build();
    }
    
    @Bean(name = "dynamicDataSource")
    @Primary //代表默认选项
    public DynamicDataSource dataSource(DataSource remoteDataSource, DataSource localDataSource) {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceType.REMOTE.name(), remoteDataSource);
        targetDataSources.put(DataSourceType.LOCAL.name(), localDataSource);
        return new DynamicDataSource(remoteDataSource, targetDataSources);
    }
}

动态数据源类,配置路由规则,上面的代码DataSourceConfig 已经将其注入

public class DynamicDataSource extends AbstractRoutingDataSource {

    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.setTargetDataSources(targetDataSources);
        // afterPropertiesSet()方法调用时用来将targetDataSources的属性写入resolvedDataSources中的
        super.afterPropertiesSet();
    }

    /**
     * 根据Key获取数据源的信息
     *
     * @return
     */
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSourceType();
    }
}

threadLocal类

public class DynamicDataSourceContextHolder {

    /**
     * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
     *  所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
     */
    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

    /**
     * 设置数据源变量
     * @param dataSourceType
     */
    public static void setDataSourceType(String dataSourceType){
        System.out.printf("切换到{%s}数据源", dataSourceType);
        CONTEXT_HOLDER.set(dataSourceType);
    }

    /**
     * 获取数据源变量
     * @return
     */
    public static String getDataSourceType(){
        return CONTEXT_HOLDER.get();
    }

    /**
     * 清空数据源变量
     */
    public static void clearDataSourceType(){
        CONTEXT_HOLDER.remove();
    }
}

mybatis配置类

/**
 * mybatis configuration(建议使用配置类的方式重新声明相关bean)
 **/
@Configuration
@Slf4j
public class MybatisConfig {


    @Value("${mybatis.mapper-locations}")
    private String mapperLocations;

    @Value("${mybatis.config-location}")
    private String configLocation;

    @Autowired
    private DynamicDataSource dynamicDataSource;

    @Bean("sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
        sqlSessionFactory.setDataSource(dynamicDataSource);
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        Resource[] mapperResources = resourcePatternResolver.getResources(mapperLocations);
        sqlSessionFactory.setMapperLocations(mapperResources);
        sqlSessionFactory.setConfigLocation(resourcePatternResolver.getResource(configLocation));
        log.info("config location: {},mapper locations: {}", configLocation, mapperLocations);
        return sqlSessionFactory.getObject();
    }
}

业务代码
user

@Data
public class User {
    private String id;
    private String password;
    private String username;
}

controller

@RestController
public class UserController {
    @Autowired
    private UserDao userDao;
    @GetMapping("/law")
    @DataSource(DataSourceType.LOCAL)
    public List<User> findAll(){
        return userDao.selectAll();
    }

    @GetMapping("/lawfile")
    @DataSource(DataSourceType.REMOTE)
    public List<User> findAll1(){
        return userDao.selectAll();
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_44892460/article/details/108165933
今日推荐