MySQL-SpringBoot集成JPA实现数据读写分离(解决主从切换不成功问题)

    技术2022-07-11  146

    一、配置数据源

    # 数据源 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)注解

    Processed: 0.012, SQL: 9