Springboot 整合数据源

Springboot 整合数据源

整合 Mybaits

pom.xml

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.0</version>
</dependency>

添加配置 application.yml

spring:
  datasource:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql:///bank?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
      username: root
      password: 123456
mybatis:
  # 扫描 mapper.xml 文件路径
  mapper-locations: classpath:mapper/*.xml

定义接口 com.demo.mapper.UserMapper

public interface UserMapper {
    List<User> findAll();
}

编写 xml 文件 mapper/UserMapper.xml

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.demo.mapper.UserMapper">

    <select id="findAll" resultType="com.demo.pojo.User">
        select * from user
    </select>

</mapper>

多数据源

添加配置 application.yml

spring:
  datasource:
    master:
      driver-class-name: com.mysql.cj.jdbc.Driver
      jdbc-url: jdbc:mysql:///bank? useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
      username: root
      password: 123456
    slave:
      driver-class-name: com.mysql.cj.jdbc.Driver
      jdbc-url: jdbc:mysql:///test? useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
      username: root
      password: 123456
mybatis:
  mapper-locations: classpath:mapper/*.xml

根据配置生成 DataSource

@Configuration
public class MultipleDataSourceConfig {

    @Bean("masterDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.master")
    private DataSource masterDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean("slaveDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    private DataSource slaveDataSource() {
        return DataSourceBuilder.create().build();
    }
}

将生成的 DataSource 放入 targetDataSources 这个 Map中

@Configuration
public class PrimaryDataSourceConfig {

    @Autowired
    @Qualifier("masterDataSource")
    private DataSource masterDataSource;

    @Autowired
    @Qualifier("slaveDataSource")
    private DataSource slaveDataSource;

    @Bean
    @Primary
    DataSource primaryDataSource() {
        Map<Object, Object> map = new HashMap<>();
        map.put("masterDataSource", masterDataSource);
        map.put("slaveDataSource", slaveDataSource);
        RoutingDataSource routing = new RoutingDataSource();
        routing.setTargetDataSources(map);
        return routing;
    }
}

继承 AbstractRoutingDataSource 返回需要使用的 数据源的 key

public class RoutingDataSource extends AbstractRoutingDataSource {
	@Override
    protected Object determineCurrentLookupKey() {
        return "masterDataSource";
    }
}

此时 key 不能写死,将 key 放入 ThreadLocal 中

public class RoutingDataSourceContext {

    private static final ThreadLocal<String> threadLocalDataSourceKey = new ThreadLocal<>();

    // 获取数据源的 key
    public static String getDataSourceRoutingKey() {
        String key = threadLocalDataSourceKey.get();
        return key == null ? "masterDataSource" : key;
    }

    // 往 ThreadLocal 中设置 key
    public RoutingDataSourceContext(String key) {
        threadLocalDataSourceKey.set(key);
    }

    // 移除 ThreadLocal 中的 key
    public void close() {
        threadLocalDataSourceKey.remove();
    }
}

// 进行设置 key
// RoutingDataSourceContext routingDataSourceContext = new RoutingDataSourceContext("slaveDataSource");

public class RoutingDataSource extends AbstractRoutingDataSource {
	@Override
    protected Object determineCurrentLookupKey() {
        return RoutingDataSourceContext.getDataSourceRoutingKey();
    }
}

此时每次调用方法都需要设置数据源的 key,可以按照 AOP 思想,将设置 key 的方法提取出来

定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RoutingWith {
    String value() default "masterDataSource";
}
@Aspect
@Component
public class RoutingAspect {
    @Around("@annotation(routingWith)")
    public Object routingWithDataSource(ProceedingJoinPoint joinPoint, RoutingWith routingWith) throws Throwable {
        String key = routingWith.value();
        RoutingDataSourceContext ctx = new RoutingDataSourceContext(key);
        return joinPoint.proceed();
    }
}
@RequestMapping("/findAll")
// 该注解能够判断使用哪个数据源
@RoutingWith("slaveDataSource")
public List<User> findAll() {
    return userMapper.findAll();
}

整合 spring data JPA

pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

定义接口

public interface UserRepository extends JpaRepository<User, Integer> {}

多数据源

application.yml 配置同上

根据配置生成 DataSource

@Configuration
public class MultipleDataSourceConfig {
    // 此处和 Mybatis 的设置区别,需要 @Primary
    @Primary
    @Bean("masterDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.master")
    DataSource masterDataSource() {
        System.out.println("create master datasource...");
        return DataSourceBuilder.create().build();
    }

    @Bean("slaverDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    DataSource slaveDataSource() {
        System.out.println("create slave datasource...");
        return DataSourceBuilder.create().build();
    }
}

根据两个 DataSource 生成 entityManager

@Configuration
@EnableJpaRepositories(
    // //配置连接工厂 entityManagerFactory
    entityManagerFactoryRef = "entityManagerFactoryMaster",
    // com.demo.repository 为 repository 接口所在的目录
    basePackages = {"com.demo.repository"}
)
public class MasterDataSourceConfig {

    @Autowired
    @Qualifier("masterDataSource")
    private DataSource dataSourceMaster;

    @Primary
    @Bean("entityManagerMaster")
    public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
        return entityManagerFactoryBean(builder).getObject().createEntityManager();
    }

    @Primary
    @Bean("entityManagerFactoryMaster")
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(EntityManagerFactoryBuilder builder) {
        // com.demo.pojo 为实体类所在目录
        return builder.dataSource(dataSourceMaster).packages("com.demo.pojo").build();
    }
}
@Configuration
@EnableJpaRepositories(
    entityManagerFactoryRef = "entityManagerFactorySlave",
    basePackages = {"com.demo.slaveRepository"}
)
public class SlaveDataSourceConfig {

    @Autowired
    @Qualifier("slaverDataSource")
    private DataSource dataSource;

    @Bean("entityManagerSlave")
    public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
        return entityManagerFactoryBean(builder).getObject().createEntityManager();
    }

    @Bean("entityManagerFactorySlave")
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(EntityManagerFactoryBuilder builder) {
        return builder.dataSource(dataSource).packages("com.demo.slave").build();
    }
}

定义 repository 接口

  1. 接口名不同的情况
public interface MasterUserRepository extends JpaRepository<User, Integer> {}

public interface SlaveUserRepository extends JpaRepository<User, Integer> {}

@Autowired
private masterUserRepository userRepository;

List<User> all = userRepository.findAll();
  1. 接口名相同的情况
@Repository("masterUserRepository")
public interface UserRepository extends JpaRepository<User, Integer> {}

@Repository("slaveUserRepository")
public interface UserRepository extends JpaRepository<User, Integer> {}

@Autowired
@Qualifier("masterUserRepository")
private UserRepository userRepository;

List<User> all = userRepository.findAll();

参考git:https://gitee.com/zhangyizhou/learning-spring-boot-datasource-demo.git

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值