Spring Boot jpa multiple Repository (data source) and read-write separation

In order to solve database bottlenecks and distribute database pressure, read-write separation is often used. Next, let's talk about how to use jpa to separate read and write in spring boot. This article provides sample source code .


When there is only one data source, we can easily use the automatic configuration of JPA to complete database operations. But when reading and writing are separated, obviously we need at least two DataSources, so these all need to be configured manually, because the automatic configuration code uses conditional annotations, and we will not automatically configure it for us after we manually configure it. This can be found by looking at the source code.



Multiple Repository Configuration


We can see the automatic configuration process and configuration key points of Repository through springboot, spring data's official website and spring boot source code. There are three key Beans are DataSource/EntityManager/TransactionManager, and then there is the package where the interface class of Repository is located. The configuration of this aspect can be easily searched on the Internet, or you can check the documentation and source code to complete it yourself, see the corresponding source code on github . Key code:


 
 
  1.  @Bean

  2.  @Primary

  3.  @ConfigurationProperties("spring.datasource.write")

  4.  public DataSourceProperties writeDataSourceProperties() {

  5.  return new DataSourceProperties();

  6.  }

  7.  @Bean

  8.  @Primary

  9.  @ConfigurationProperties("spring.datasource.write")

  10.  public DataSource writeDataSource() {

  11.  return writeDataSourceProperties().initializeDataSourceBuilder().build();

  12.  }

  13.  @Bean

  14.  @Primary

  15.  public LocalContainerEntityManagerFactoryBean writeEntityManagerFactory(

  16.  EntityManagerFactoryBuilder builder, @Qualifier("writeDataSource"DataSource dataSource) {

  17.  return builder

  18.  .dataSource(dataSource)

  19.  .packages(User.class)

  20.  .properties(getVendorProperties(dataSource))

  21.  .persistenceUnit("write")

  22.  .build();

  23.  }

  24.  @Bean

  25.  @Primary

  26.  public PlatformTransactionManager writeTransactionManager(@Qualifier("writeEntityManagerFactory"LocalContainerEntityManagerFactoryBean writeEntityManagerFactory) {

  27.  JpaTransactionManager transactionManager = new JpaTransactionManager(writeEntityManagerFactory.getObject());

  28.  return transactionManager;

  29.  }



first option

According to the principle of JPA multi-repository, we can know that we can configure multi-repository by configuring and scanning the packages where different Repository interface classes are located. At this point, the first solution is naturally conceivable. That is to add a sub-interface through interface inheritance, but we do not need to operate anything in the sub-interface, only maintain the original interface. In this way, we can easily achieve read-write separation by letting the read Repository scan the package where the other Repository is located. This solution is simple and easy to implement, the only step is to add one more interface.



The second option

如果是已经完成的项目,添加接口也有可能是非常庞大的一个工程。那么我们就可以利用第二种方案了。这是通过修改注册Bean时候的源码,让两次扫描到的Repository生成不同的名称注册到Spring容器当中,把原来的标记为Primary。通过源码我们可以发现生成Bean名称的类是 org.springframework.data.repository.config.RepositoryConfigurationDelegate.我们就拦截这个类中生成Bean名称的过程,把不同配置类扫描到的Repository使用不同的名称,虽然它们类型是一样的,我们可以通过名称限定来注入相应的Bean。我们可以添加一个自己的注解,用来给我们的配置类添加元信息。


自定义注解:

 
 
  1. /**

  2. * repository bean 名称的前缀

  3. */

  4. @Target(ElementType.TYPE)

  5. @Retention(RetentionPolicy.RUNTIME)

  6. @Documented

  7. @Inherited

  8. public @interface RepositoryBeanNamePrefix {

  9.  String value();

  10. }


修改RepositoryConfigurationDelegate,添加对应的逻辑:


 
 
  1. String beanName = configurationSource.generateBeanName(beanDefinition);

  2. AnnotationMetadata metadata = (AnnotationMetadata) configurationSource.getSource();

  3. //判断配置类是否使用primary进行了标注,如果有,就设为primary

  4. if(metadata.hasAnnotation(Primary.class.getName())){

  5.  beanDefinition.setPrimary(true);

  6. }else if(metadata.hasAnnotation(RepositoryBeanNamePrefix.class.getName())){

  7.  // 再判断是否使用了RepositoryBeanNamePrefix进行了标注,如果有,添加名称前缀

  8.  Map<String,Object> prefixData = metadata.getAnnotationAttributes(RepositoryBeanNamePrefix.class.getName());

  9.  String prefix = (String) prefixData.get("value");

  10.  beanName = prefix + beanName;

  11. }


配置多个Repository扫描的配置类:

 
 
  1.  @EnableJpaRepositories(basePackageClasses = UserRepository.class,

  2.  entityManagerFactoryRef = "writeEntityManagerFactory", transactionManagerRef = "writeTransactionManager")

  3.  @Primary

  4.  public class WriteConfiguration {

  5.  }

  6.  @EnableJpaRepositories(basePackageClasses = UserRepository.class,

  7.  entityManagerFactoryRef = "readEntityManagerFactory", transactionManagerRef = "readTransactionManager")

  8.  @RepositoryBeanNamePrefix("second")

  9.  public class SameRepositoryWriteConfiguration {

  10.  }


使用时我们可以通过名称注入的方式,使用读Repository:


 
 
  1. @RestController

  2. @RequestMapping("/user")

  3. public class UserController {

  4.  @Autowired

  5.  private UserRepository userRepository;

  6.  @Autowired

  7.  private UserReadRepository readRepository;

  8.  @Autowired

  9.  @Qualifier("seconduserRepository")

  10.  private UserRepository seconduserRepository;

  11. }


这样就达到我们读写分离的目的了。



总结


本文提供了Jpa多Repository连接多数据源的实现方式,及两种读写分离方案。以上列出的只是关键代码,很难通过语言完整详细的描述整个过程。大家如果有什么疑问,欢迎讨论,最好还是下载示例代码自己看看代码也许会清楚很多。


示例代码 下载完后可以直接运行。因为数据库使用的时内存数据库h2,可以换成自己使用的数据库。



互联网金融是当下非常流行的热点领域。


作为一个紧跟时代的程序员,如果你对互联网金融领域感兴趣却不知如何进入,那这门课程就是你入行的加速器,能让你积累实际项目经验,提供技术综合运用能力!


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325518165&siteId=291194637