zoukankan      html  css  js  c++  java
  • Spring Boot 2.x基础教程:Spring Data JPA的多数据源配置

    上一篇我们介绍了在使用JdbcTemplate来做数据访问时候的多数据源配置实现。接下来我们继续学习如何在使用Spring Data JPA的时候,完成多数据源的配置和使用。

    添加多数据源的配置

    先在Spring Boot的配置文件application.properties中设置两个你要链接的数据库配置,比如这样:

    spring.datasource.primary.jdbc-url=jdbc:mysql://localhost:3306/test1
    spring.datasource.primary.username=root
    spring.datasource.primary.password=123456
    spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver
    
    spring.datasource.secondary.jdbc-url=jdbc:mysql://localhost:3306/test2
    spring.datasource.secondary.username=root
    spring.datasource.secondary.password=123456
    spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver
    
    # 日志打印执行的SQL
    spring.jpa.show-sql=true
    # Hibernate的DDL策略
    spring.jpa.hibernate.ddl-auto=create-drop
    

    这里除了JPA自身相关的配置之外,与JdbcTemplate配置时候的数据源配置完全是一致的

    说明与注意

    1. 多数据源配置的时候,与单数据源不同点在于spring.datasource之后多设置一个数据源名称primarysecondary来区分不同的数据源配置,这个前缀将在后续初始化数据源的时候用到。
    2. 数据源连接配置2.x和1.x的配置项是有区别的:2.x使用spring.datasource.secondary.jdbc-url,而1.x版本使用spring.datasource.secondary.url。如果你在配置的时候发生了这个报错java.lang.IllegalArgumentException: jdbcUrl is required with driverClassName.,那么就是这个配置项的问题。

    初始化数据源与JPA配置

    完成多数据源的配置信息之后,就来创建个配置类来加载这些配置信息,初始化数据源,以及初始化每个数据源要用的JdbcTemplate。

    由于JPA的配置要比JdbcTemplate的负责很多,所以我们将配置拆分一下来处理:

    1. 单独建一个多数据源的配置类,比如下面这样:
    @Configuration
    public class DataSourceConfiguration {
    
        @Primary
        @Bean
        @ConfigurationProperties(prefix = "spring.datasource.primary")
        public DataSource primaryDataSource() {
            return DataSourceBuilder.create().build();
        }
    
        @Bean
        @ConfigurationProperties(prefix = "spring.datasource.secondary")
        public DataSource secondaryDataSource() {
            return DataSourceBuilder.create().build();
        }
    
    }
    

    可以看到内容跟JdbcTemplate时候是一模一样的。通过@ConfigurationProperties可以知道这两个数据源分别加载了spring.datasource.primary.*spring.datasource.secondary.*的配置。@Primary注解指定了主数据源,就是当我们不特别指定哪个数据源的时候,就会使用这个Bean真正差异部分在下面的JPA配置上。

    1. 分别创建两个数据源的JPA配置。

    Primary数据源的JPA配置:

    @Configuration
    @EnableTransactionManagement
    @EnableJpaRepositories(
            entityManagerFactoryRef="entityManagerFactoryPrimary",
            transactionManagerRef="transactionManagerPrimary",
            basePackages= { "com.didispace.chapter38.p" }) //设置Repository所在位置
    public class PrimaryConfig {
    
        @Autowired
        @Qualifier("primaryDataSource")
        private DataSource primaryDataSource;
    
        @Autowired
        private JpaProperties jpaProperties;
        @Autowired
        private HibernateProperties hibernateProperties;
    
        private Map<String, Object> getVendorProperties() {
            return hibernateProperties.determineHibernateProperties(jpaProperties.getProperties(), new HibernateSettings());
        }
    
        @Primary
        @Bean(name = "entityManagerPrimary")
        public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
            return entityManagerFactoryPrimary(builder).getObject().createEntityManager();
        }
    
        @Primary
        @Bean(name = "entityManagerFactoryPrimary")
        public LocalContainerEntityManagerFactoryBean entityManagerFactoryPrimary (EntityManagerFactoryBuilder builder) {
            return builder
                    .dataSource(primaryDataSource)
                    .packages("com.didispace.chapter38.p") //设置实体类所在位置
                    .persistenceUnit("primaryPersistenceUnit")
                    .properties(getVendorProperties())
                    .build();
        }
    
        @Primary
        @Bean(name = "transactionManagerPrimary")
        public PlatformTransactionManager transactionManagerPrimary(EntityManagerFactoryBuilder builder) {
            return new JpaTransactionManager(entityManagerFactoryPrimary(builder).getObject());
        }
    
    }
    

    Secondary数据源的JPA配置:

    @Configuration
    @EnableTransactionManagement
    @EnableJpaRepositories(
            entityManagerFactoryRef="entityManagerFactorySecondary",
            transactionManagerRef="transactionManagerSecondary",
            basePackages= { "com.didispace.chapter38.s" }) //设置Repository所在位置
    public class SecondaryConfig {
    
        @Autowired
        @Qualifier("secondaryDataSource")
        private DataSource secondaryDataSource;
    
        @Autowired
        private JpaProperties jpaProperties;
        @Autowired
        private HibernateProperties hibernateProperties;
    
        private Map<String, Object> getVendorProperties() {
            return hibernateProperties.determineHibernateProperties(jpaProperties.getProperties(), new HibernateSettings());
        }
    
        @Bean(name = "entityManagerSecondary")
        public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
            return entityManagerFactorySecondary(builder).getObject().createEntityManager();
        }
    
        @Bean(name = "entityManagerFactorySecondary")
        public LocalContainerEntityManagerFactoryBean entityManagerFactorySecondary (EntityManagerFactoryBuilder builder) {
            return builder
                    .dataSource(secondaryDataSource)
                    .packages("com.didispace.chapter38.s") //设置实体类所在位置
                    .persistenceUnit("secondaryPersistenceUnit")
                    .properties(getVendorProperties())
                    .build();
        }
    
        @Bean(name = "transactionManagerSecondary")
        PlatformTransactionManager transactionManagerSecondary(EntityManagerFactoryBuilder builder) {
            return new JpaTransactionManager(entityManagerFactorySecondary(builder).getObject());
        }
    
    }
    

    说明与注意

    • 在使用JPA的时候,需要为不同的数据源创建不同的package来存放对应的Entity和Repository,以便于配置类的分区扫描
    • 类名上的注解@EnableJpaRepositories中指定Repository的所在位置
    • LocalContainerEntityManagerFactoryBean创建的时候,指定Entity所在的位置
    • 其他主要注意在互相注入时候,不同数据源不同配置的命名,基本就没有什么大问题了

    测试一下

    完成了上面之后,我们就可以写个测试类来尝试一下上面的多数据源配置是否正确了,比如下面这样:

    @Slf4j
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class Chapter38ApplicationTests {
    
        @Autowired
        private UserRepository userRepository;
        @Autowired
        private MessageRepository messageRepository;
    
        @Test
        public void test() throws Exception {
            userRepository.save(new User("aaa", 10));
            userRepository.save(new User("bbb", 20));
            userRepository.save(new User("ccc", 30));
            userRepository.save(new User("ddd", 40));
            userRepository.save(new User("eee", 50));
    
            Assert.assertEquals(5, userRepository.findAll().size());
    
            messageRepository.save(new Message("o1", "aaaaaaaaaa"));
            messageRepository.save(new Message("o2", "bbbbbbbbbb"));
            messageRepository.save(new Message("o3", "cccccccccc"));
    
            Assert.assertEquals(3, messageRepository.findAll().size());
        }
    
    }
    

    说明与注意

    • 测试验证的逻辑很简单,就是通过不同的Repository往不同的数据源插入数据,然后查询一下总数是否是对的
    • 这里省略了Entity和Repository的细节,读者可以在下方代码示例中下载完整例子对照查看

    代码示例

    本文的相关例子可以查看下面仓库中的chapter3-8目录:

    如果您觉得本文不错,欢迎Star支持,您的关注是我坚持的动力!

    相关阅读

    本文首发:Spring Boot 2.x基础教程:Spring Data JPA的多数据源配置,转载请注明出处。
    欢迎关注我的公众号:程序猿DD,获得独家整理的学习资源和日常干货推送。
    如果您对我的其他专题内容感兴趣,直达我的个人博客:didispace.com

  • 相关阅读:
    常看本地是否安装Git和maven工具
    Git 安装和使用Tips
    Jmeter 获取系统时间,和对系统时间进行增减时间
    Java 中的系统时间
    Go语言
    数据拆分之 垂直拆分 and 水平拆分
    在Windows和UNIX下利用PHP和LDAP进行身份验证
    Linux中使用crontab命令定时执行shell脚本或其他Linux命令
    php empty()和isset()的区别
    php 判断是否get传值的参数是否存在
  • 原文地址:https://www.cnblogs.com/didispace/p/13181203.html
Copyright © 2011-2022 走看看