为了账号安全,请及时绑定邮箱和手机立即绑定

springboot jpa多Repository(数据源)及读写分离

标签:
Java SpringBoot

为了解决数据库瓶颈,分散数据库压力,读写分离经常被使用到。接下来我们就来谈一谈,在spring boot 中如何使用jpa进行读写分离。本文提供示例源码

在只有一个数据源的时候,我们可以很简单的使用有关JPA的自动配置来完成数据库操作。但是读写分离的时候显然我们至少要两个DataSource了,那么这些都是需要我们手动配置了,因为自动配置代码都是使用了条件注解的,我们手动配置之后就不再帮我们自动配置了。这个可以通过查看源码发现。

多Repository配置

我们通过springbootspring data的官网及spring boot源码,可以看到Repository的自动配置过程及配置关键点。有三个比较关键的Bean就是DataSource/EntityManager/TransactionManager,再有就是Repository的接口类所在的包。这方面的配置大家可以在网上很轻松的搜索到,也可以查看文档及源码自己完成,参见github上对应源码.
关键代码:

   @Bean
    @Primary
    @ConfigurationProperties("spring.datasource.write")
    public DataSourceProperties writeDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    @Primary
    @ConfigurationProperties("spring.datasource.write")
    public DataSource writeDataSource() {
        return writeDataSourceProperties().initializeDataSourceBuilder().build();
    }

    @Bean
    @Primary
    public LocalContainerEntityManagerFactoryBean writeEntityManagerFactory(
            EntityManagerFactoryBuilder builder, @Qualifier("writeDataSource") DataSource dataSource) {
        return builder
                .dataSource(dataSource)
                .packages(User.class)
                .properties(getVendorProperties(dataSource))
                .persistenceUnit("write")
                .build();
    }

    @Bean
    @Primary
    public PlatformTransactionManager writeTransactionManager(@Qualifier("writeEntityManagerFactory") LocalContainerEntityManagerFactoryBean writeEntityManagerFactory) {
        JpaTransactionManager transactionManager = new JpaTransactionManager(writeEntityManagerFactory.getObject());
        return transactionManager;
    }
第一种方案

根据JPA多Repository的原理,我们可以知道,我们是可以通过配置扫描不同Repository接口类所在的包达到配置多Repository的目的。
这个这一点,第一种方案就很自然的可以想到。那就是通过接口继承的方式添加一个子接口,但是我们子接口里什么都不用操作,只维护原接口就可以了。这样我们让读的Repository扫描另外的Repository所在的包就可以轻松实现读写分离了。这种方案简单易实现,唯一的步骤就是多添加了一个接口。

第二种方案

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

自定义注解:

/**
* repository bean 名称的前缀
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface RepositoryBeanNamePrefix {
    String value();
}

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

String beanName = configurationSource.generateBeanName(beanDefinition);

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

//判断配置类是否使用primary进行了标注,如果有,就设为primary
if(metadata.hasAnnotation(Primary.class.getName())){
    beanDefinition.setPrimary(true);
}else  if(metadata.hasAnnotation(RepositoryBeanNamePrefix.class.getName())){
    // 再判断是否使用了RepositoryBeanNamePrefix进行了标注,如果有,添加名称前缀
    Map<String,Object> prefixData = metadata.getAnnotationAttributes(RepositoryBeanNamePrefix.class.getName());
    String prefix = (String) prefixData.get("value");
    beanName = prefix + beanName;
}

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

    @EnableJpaRepositories(basePackageClasses = UserRepository.class,
            entityManagerFactoryRef = "writeEntityManagerFactory", transactionManagerRef = "writeTransactionManager")
    @Primary
    public class WriteConfiguration {
    }

    @EnableJpaRepositories(basePackageClasses = UserRepository.class,
            entityManagerFactoryRef = "readEntityManagerFactory", transactionManagerRef = "readTransactionManager")
    @RepositoryBeanNamePrefix("second")
    public class SameRepositoryWriteConfiguration {
    }

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

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private UserReadRepository readRepository;

    @Autowired
    @Qualifier("seconduserRepository")
    private UserRepository seconduserRepository;
}

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

总结

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

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

点击查看更多内容
7人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
JAVA开发工程师
手记
粉丝
277
获赞与收藏
83

关注作者,订阅最新文章

阅读免费教程

感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消