Spring Boot之动态数据源配置二

AbstractRoutingDataSource动态路由方式:

利用AbstractRoutingDataSource实现动态切换数据源的功能

<?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">
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.2.RELEASE</version>
        <relativePath/>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.13</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.7.3</version>
        </dependency>
    </dependencies>
</project>


配置数据源

application.properties

#主数据源
spring.datasource.primary.url=jdbc:mysql://localhost:3306/test
spring.datasource.primary.username=test
spring.datasource.primary.password=test
spring.datasource.primary.driver-class-name=com.mysql.jdbc.Driver

#second数据源
spring.datasource.secondary.url=jdbc:mysql://localhost:3306/test2
spring.datasource.secondary.username=test2
spring.datasource.secondary.password=test2
spring.datasource.secondary.driver-class-name=com.mysql.jdbc.Driver

# MyBatis
mybatis.type-aliases-package=com.durid.model
mybatis.mapper-locations=classpath:mappers/*.xml

@Primary注解在哪个ds,默认使用那个ds

package com.durid.config;

import com.durid.dds.DRoutingDataSource;
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 DataSourceConfig {
    /**
     * @primary 设置在哪个ds,就优先读取那个ds
     *
     * @return
     */
    @Bean(name = "primary")
    @Primary
    @ConfigurationProperties(prefix="spring.datasource.primary")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "secondary")
    @ConfigurationProperties(prefix="spring.datasource.secondary")
    public DataSource secondaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    /**
     * 动态数据源设置
     * @return
     */
    @Bean(name = "ddataSource")
    public DataSource ddataSource(){
        DRoutingDataSource dRoutingDataSource = new DRoutingDataSource();

        //设置默认的数据源
        dRoutingDataSource.setDefaultTargetDataSource(primaryDataSource());

        //配置多数据源
        Map<Object, Object> ddsMap = new HashMap<>(2);
        ddsMap.put("primary", primaryDataSource());
        ddsMap.put("secondary", secondaryDataSource());
        dRoutingDataSource.setTargetDataSources(ddsMap);

        return dRoutingDataSource;
    }

    /**
     * mybatis前缀必需
     * 参考application.properties的配置
     * 用来映射xml和model
     * mybatis.type-aliases-package
     * mybatis.mapper-locations
     *
     * @return
     */
    @Bean
    @ConfigurationProperties(prefix = "mybatis")
    public SqlSessionFactoryBean sqlSessionFactoryBean() {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(ddataSource());
        return sqlSessionFactoryBean;
    }

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

继承AbstractRoutingDataSource,配合ThreadLocal存储数据源key

package com.durid.dds;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class DRoutingDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DdsHolder.getDS();
    }
}
package com.durid.dds;

public class DdsHolder {
    private static final ThreadLocal<String> HOLDER = new ThreadLocal<>();

    public static void setDS(String ddsName) {
        if (ddsName == null){
            throw new RuntimeException("set ddsMode failed");
        }

        HOLDER.set(ddsName);
    }

    public static void release(){
        HOLDER.remove();
    }

    public static String getDS(){
        return HOLDER.get();
    }
}

通过注解和切面的方式实现数据源的动态切换:

package com.durid.annotation;

import java.lang.annotation.*;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
    String value();
}
package com.durid.aop;

import com.durid.annotation.TargetDataSource;
import com.durid.dds.DdsHolder;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class DdsAspect {
    @Before("@annotation(targetDataSource))")
    public void switchDataSource(TargetDataSource targetDataSource) {
        DdsHolder.setDS(targetDataSource.value());
    }

    @After("@annotation(targetDataSource))")
    public void restoreDataSource(TargetDataSource targetDataSource) {
        DdsHolder.release();
    }
}

model类:

package com.durid.model;

import lombok.Data;

/**
 * Created by hao.g on 18/3/12.
 */
@Data
public class User {
    private Long id;
    private String name;
    private int age;
}
mapper类:
package com.durid.mapper;

import com.durid.model.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper {
    User selectById(long id);
}
<?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.durid.mapper.UserMapper">
    <resultMap id="baseResultMap" type="com.durid.model.User">
        <id column="id" property="id" javaType="java.lang.Long" jdbcType="BIGINT" />
        <result column="name" property="name" javaType="java.lang.String" jdbcType="VARCHAR" />
        <result column="age" property="age" javaType="java.lang.Integer" jdbcType="BIGINT" />
    </resultMap>

    <select id="selectById" resultMap="baseResultMap" parameterType="java.lang.Long">
        SELECT *
        FROM user
        WHERE id = #{id}
    </select>
</mapper>
Service类,可以使用注解在目标service方法完成数据源切换:
package com.durid.service;

import com.durid.annotation.TargetDataSource;
import com.durid.mapper.UserMapper;
import com.durid.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;

    /**
     * 动态切换数据源
     * @param id
     * @return
     */
//    @TargetDataSource("primary")
    @TargetDataSource("secondary")
    public User selectById(long id){
        return userMapper.selectById(id);
    }
}
测试类:
package com.test;

import com.durid.App;
import com.durid.aop.DdsAspect;
import com.durid.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = App.class)
public class AppTests {
    @Autowired
    private UserService userService;

    @Test
    public void test(){
        System.out.println(userService.selectById(1).getName());
    }
}

猜你喜欢

转载自blog.csdn.net/ghaohao/article/details/79531840
今日推荐