MySQL-SpringBoot集成JPA實現數據讀寫分離(解決主從切換不成功問題)

一、配置數據源

# 數據源
spring.datasource.druid.write.url=jdbc:mysql://localhost:3380/test
spring.datasource.druid.write.username=root
spring.datasource.druid.write.password=
spring.datasource.druid.write.driver-class-name=com.mysql.jdbc.Driver
 
spring.datasource.druid.read.url=jdbc:mysql://localhost:3381/test
spring.datasource.druid.read.username=root
spring.datasource.druid.read.password=
spring.datasource.druid.read.driver-class-name=com.mysql.jdbc.Driver

# JPA
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.database=mysql
spring.jpa.generate-ddl=false
spring.jpa.hibernate.ddl-auto=none
spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.DefaultComponentSafeNamingStrategy
spring.jpa.show-sql=false


二、數據源配置類

/**
 * 數據源配置
 * 
 * @author Administrator
 *
 */
@Configuration
public class DataSourceConfig {
 
  public final static String WRITE_DATASOURCE_KEY = "writeDruidDataSource";
  public final static String READ_DATASOURCE_KEY = "readDruidDataSource";
 
  @ConfigurationProperties(prefix = "spring.datasource.druid.read")
  @Bean(name = READ_DATASOURCE_KEY)
  public DataSource readDruidDataSource() {
    return new DruidDataSource();
  }
 
  @ConfigurationProperties(prefix = "spring.datasource.druid.write")
  @Bean(name = WRITE_DATASOURCE_KEY)
  public DataSource writeDruidDataSource() {
    return new DruidDataSource();
  }
 
  /**
   * 注入AbstractRoutingDataSource
   * 
   * @param readDruidDataSource
   * @param writeDruidDataSource
   * @return
   * @throws Exception
   */
  @Bean

@Primary
  public AbstractRoutingDataSource routingDataSource(
      @Qualifier(READ_DATASOURCE_KEY) DataSource readDruidDataSource,
      @Qualifier(WRITE_DATASOURCE_KEY) DataSource writeDruidDataSource) throws Exception {
    DynamicDataSource dataSource = new DynamicDataSource();
    Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
    targetDataSources.put(WRITE_DATASOURCE_KEY, writeDruidDataSource);
    targetDataSources.put(READ_DATASOURCE_KEY, readDruidDataSource);
    dataSource.setTargetDataSources(targetDataSources);// 配置數據源
    dataSource.setDefaultTargetDataSource(writeDruidDataSource);// 默認爲主庫用於寫數據
    return dataSource;
  }
}


三、使用ThreadLocal使數據源與線程綁定

public class DynamicDataSourceHolder {
  // 使用ThreadLocal把數據源與當前線程綁定
  private static final ThreadLocal<String> dataSources = new ThreadLocal<String>();
 
  public static void setDataSource(String dataSourceName) {
    dataSources.set(dataSourceName);
  }
 
  public static String getDataSource() {
    return (String) dataSources.get();
  }
 
  public static void clearDataSource() {
    dataSources.remove();
  }
}


四、動態數據源配置

public class DynamicDataSource extends AbstractRoutingDataSource {
 
  @Override
  protected Object determineCurrentLookupKey() {
    // 可以做一個簡單的負載均衡策略
    String lookupKey = DynamicDataSourceHolder.getDataSource();
    System.out.println("------------lookupKey---------" + lookupKey);
    return lookupKey;
  }
}


五、自定義註解

@Target({
    ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDateSource {
  String dataSource() default "";// 數據源
}


七、定義切面,實現數據源切換

@Aspect
@Component
@Order(0)
public class DynamicDataSourceAspect {
 
  @Around("execution(public * com.study.mysql.jpa.core..*.*(..))")
  public Object around(ProceedingJoinPoint pjp) throws Throwable {
    MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
    Method targetMethod = methodSignature.getMethod();
    if (targetMethod.isAnnotationPresent(TargetDateSource.class)) {
      String targetDataSource = targetMethod.getAnnotation(TargetDateSource.class).dataSource();
      System.out.println("----------數據源是:" + targetDataSource + "------");
      DynamicDataSourceHolder.setDataSource(targetDataSource);
    }
    // 執行方法
    Object result = pjp.proceed();
    DynamicDataSourceHolder.clearDataSource();
    return result;
  }
}


通過註解@TargetDataSource註解實現讀寫分離。

@Override
@TargetDateSource(dataSource = DataSourceConfig.READ_DATASOURCE_KEY)
public WxSpUserInfo findById(String id) {
    return wxSpUserInfoRepository.findById(id).orElse(null);
}

註明:有些文章沒法主從切換,解決的辦法:@Primary這個註解加到AbstractRoutingDataSource類上 ,把writeDruidDataSource類上的@Primary去掉。事務問題:DynamicDataSourceAspect類上加上@Order(0)註解

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章