美文网首页编程技术极乐科技spring
Spring Boot多数据源配置与使用

Spring Boot多数据源配置与使用

作者: 程序猿DD | 来源:发表于2016-04-07 10:45 被阅读61470次

    之前在介绍使用JdbcTemplate和Spring-data-jpa时,都使用了单数据源。在单数据源的情况下,Spring Boot的配置非常简单,只需要在application.properties文件中配置连接参数即可。但是往往随着业务量发展,我们通常会进行数据库拆分或是引入其他数据库,从而我们需要配置多个数据源,下面基于之前的JdbcTemplate和Spring-data-jpa例子分别介绍两种多数据源的配置方式。

    多数据源配置

    创建一个Spring配置类,定义两个DataSource用来读取application.properties中的不同配置。如下例子中,主数据源配置为spring.datasource.primary开头的配置,第二数据源配置为spring.datasource.secondary开头的配置。

    
    @Configuration
    public class DataSourceConfig {
    
        @Bean(name = "primaryDataSource")
        @Qualifier("primaryDataSource")
        @ConfigurationProperties(prefix="spring.datasource.primary")
        public DataSource primaryDataSource() {
            return DataSourceBuilder.create().build();
        }
    
        @Bean(name = "secondaryDataSource")
        @Qualifier("secondaryDataSource")
        @Primary
        @ConfigurationProperties(prefix="spring.datasource.secondary")
        public DataSource secondaryDataSource() {
            return DataSourceBuilder.create().build();
        }
    
    }
    
    

    对应的application.properties配置如下:

    spring.datasource.primary.url=jdbc:mysql://localhost:3306/test1
    spring.datasource.primary.username=root
    spring.datasource.primary.password=root
    spring.datasource.primary.driver-class-name=com.mysql.jdbc.Driver
    
    spring.datasource.secondary.url=jdbc:mysql://localhost:3306/test2
    spring.datasource.secondary.username=root
    spring.datasource.secondary.password=root
    spring.datasource.secondary.driver-class-name=com.mysql.jdbc.Driver
    

    JdbcTemplate支持

    对JdbcTemplate的支持比较简单,只需要为其注入对应的datasource即可,如下例子,在创建JdbcTemplate的时候分别注入名为primaryDataSourcesecondaryDataSource的数据源来区分不同的JdbcTemplate。

    
        @Bean(name = "primaryJdbcTemplate")
        public JdbcTemplate primaryJdbcTemplate(
                @Qualifier("primaryDataSource") DataSource dataSource) {
            return new JdbcTemplate(dataSource);
        }
    
        @Bean(name = "secondaryJdbcTemplate")
        public JdbcTemplate secondaryJdbcTemplate(
                @Qualifier("secondaryDataSource") DataSource dataSource) {
            return new JdbcTemplate(dataSource);
        }
    
    

    接下来通过测试用例来演示如何使用这两个针对不同数据源的JdbcTemplate。

    
    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(Application.class)
    public class ApplicationTests {
    
        @Autowired
        @Qualifier("primaryJdbcTemplate")
        protected JdbcTemplate jdbcTemplate1;
    
        @Autowired
        @Qualifier("secondaryJdbcTemplate")
        protected JdbcTemplate jdbcTemplate2;
    
        @Before
        public void setUp() {
            jdbcTemplate1.update("DELETE  FROM  USER ");
            jdbcTemplate2.update("DELETE  FROM  USER ");
        }
    
        @Test
        public void test() throws Exception {
    
            // 往第一个数据源中插入两条数据
            jdbcTemplate1.update("insert into user(id,name,age) values(?, ?, ?)", 1, "aaa", 20);
            jdbcTemplate1.update("insert into user(id,name,age) values(?, ?, ?)", 2, "bbb", 30);
    
            // 往第二个数据源中插入一条数据,若插入的是第一个数据源,则会主键冲突报错
            jdbcTemplate2.update("insert into user(id,name,age) values(?, ?, ?)", 1, "aaa", 20);
    
            // 查一下第一个数据源中是否有两条数据,验证插入是否成功
            Assert.assertEquals("2", jdbcTemplate1.queryForObject("select count(1) from user", String.class));
    
            // 查一下第一个数据源中是否有两条数据,验证插入是否成功
            Assert.assertEquals("1", jdbcTemplate2.queryForObject("select count(1) from user", String.class));
    
        }
    
    
    }
    
    

    完整示例:Chapter3-2-3

    Spring-data-jpa支持

    对于数据源的配置可以沿用上例中DataSourceConfig的实现。

    新增对第一数据源的JPA配置,注意两处注释的地方,用于指定数据源对应的Entity实体和Repository定义位置,用@Primary区分主数据源。

    @Configuration
    @EnableTransactionManagement
    @EnableJpaRepositories(
            entityManagerFactoryRef="entityManagerFactoryPrimary",
            transactionManagerRef="transactionManagerPrimary",
            basePackages= { "com.didispace.domain.p" }) //设置Repository所在位置
    public class PrimaryConfig {
    
        @Autowired @Qualifier("primaryDataSource")
        private DataSource primaryDataSource;
    
        @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)
                    .properties(getVendorProperties(primaryDataSource))
                    .packages("com.didispace.domain.p") //设置实体类所在位置
                    .persistenceUnit("primaryPersistenceUnit")
                    .build();
        }
    
        @Autowired
        private JpaProperties jpaProperties;
    
        private Map<String, String> getVendorProperties(DataSource dataSource) {
            return jpaProperties.getHibernateProperties(dataSource);
        }
    
        @Primary
        @Bean(name = "transactionManagerPrimary")
        public PlatformTransactionManager transactionManagerPrimary(EntityManagerFactoryBuilder builder) {
            return new JpaTransactionManager(entityManagerFactoryPrimary(builder).getObject());
        }
    
    }
    

    新增对第二数据源的JPA配置,内容与第一数据源类似,具体如下:

    @Configuration
    @EnableTransactionManagement
    @EnableJpaRepositories(
            entityManagerFactoryRef="entityManagerFactorySecondary",
            transactionManagerRef="transactionManagerSecondary",
            basePackages= { "com.didispace.domain.s" }) //设置Repository所在位置
    public class SecondaryConfig {
    
        @Autowired @Qualifier("secondaryDataSource")
        private DataSource secondaryDataSource;
    
        @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)
                    .properties(getVendorProperties(secondaryDataSource))
                    .packages("com.didispace.domain.s") //设置实体类所在位置
                    .persistenceUnit("secondaryPersistenceUnit")
                    .build();
        }
    
        @Autowired
        private JpaProperties jpaProperties;
    
        private Map<String, String> getVendorProperties(DataSource dataSource) {
            return jpaProperties.getHibernateProperties(dataSource);
        }
    
        @Bean(name = "transactionManagerSecondary")
        PlatformTransactionManager transactionManagerSecondary(EntityManagerFactoryBuilder builder) {
            return new JpaTransactionManager(entityManagerFactorySecondary(builder).getObject());
        }
    
    }
    

    完成了以上配置之后,主数据源的实体和数据访问对象位于:com.didispace.domain.p,次数据源的实体和数据访问接口位于:com.didispace.domain.s

    分别在这两个package下创建各自的实体和数据访问接口

    • 主数据源下,创建User实体和对应的Repository接口
    @Entity
    public class User {
    
        @Id
        @GeneratedValue
        private Long id;
    
        @Column(nullable = false)
        private String name;
    
        @Column(nullable = false)
        private Integer age;
    
        public User(){}
    
        public User(String name, Integer age) {
            this.name = name;
            this.age = age;
        }
    
        // 省略getter、setter
    
    }
    
    public interface UserRepository extends JpaRepository<User, Long> {
    
    }
    
    • 从数据源下,创建Message实体和对应的Repository接口
    @Entity
    public class Message {
    
        @Id
        @GeneratedValue
        private Long id;
    
        @Column(nullable = false)
        private String name;
    
        @Column(nullable = false)
        private String content;
    
        public Message(){}
    
        public Message(String name, String content) {
            this.name = name;
            this.content = content;
        }
    
        // 省略getter、setter
    
    }
    
    public interface MessageRepository extends JpaRepository<Message, Long> {
    
    }
    

    接下来通过测试用例来验证使用这两个针对不同数据源的配置进行数据操作。

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(Application.class)
    public class ApplicationTests {
    
        @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());
    
        }
    
    }
    

    完整示例:Chapter3-2-4

    相关文章

      网友评论

      • 不得真假:哥 三个数据源的话 需要怎么写啊 弄了好久了 🙏🙏🙏
      • 马拉松Mara:配置成功了,谢谢
      • 马拉松Mara:谢谢分享
      • 赵诺西:一模一样的问题正好需要
      • 漠小帆:你好,问下在Spring-data-jpa,不同数据源是通过什么进行匹配对应的实体类的呢?通过Repository和实体类 位置么??那假如Repository在同一目录下,实体类也在同一目录下呢?
      • c5cb4ac608a5:JpaProperties 注入报错,在哪里定义这个bean的呢???
      • 从你说谎:想知道配置原理是怎么样的? 官网有写吗?
        西部码农琼斯:如果不用框架,让你写一个多数据源,你怎么写?无非就是多个url user password driver了,你就用原始的jdbc,不用说多数据源,几千万个数据源你都能写出来。原理想必你懂了吧
      • 乌尔苏拉:数据库连接池要怎么配置?
        马拉松Mara:spring:
        datasource:
        druid:
        one:
        url: jdbc:mysql://one
        username: one
        password: onepwd
        two:
        url: jdbc:mysql://two
        username: two
        password: twopwd
        马拉松Mara:我用的是Druid
      • 87aee9d511ba:博主,通过上述实现方式,我这边遇到的问题在于,使用mysql数据库是正常的,但换成oracle则报出这样的错误提示:java.sql.SQLException: Unable to load class: oracle.jdbc.driver.OracleDriver
        程序猿DD:@xufanevel 要配置下驱动class
        87aee9d511ba:会不会是我oracle的驱动包的问题吗
      • 晨风苏苏:你好,我用的是Spring-data-jpa,按照你说的配置,项目在跑起来的时候报错,要怎么解决呢:
        org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'javax.sql.DataSource' available: more than one 'primary' bean found among candidates: [dataSource, primaryDS, secondaryDS]
        马拉松Mara:看上去像是定义了两个primary
      • Luckyqiao:请问怎么配置连接两个不同的数据库呢?
      • 1cfd8e1a861c:楼主,我新建个实体类,怎么指向哪个数据库?弄不明白了,救救我
        马拉松Mara:放在不同的包里,来进行区分
      • 周浩_52f2:你好,请问一下,按照你的方法,我进行了两个数据源的配置,使用的是jdbcTemplate,但是发现druid监控错误,监控台可以访问,数据源 (*) property for user to setup,druid在这种情况下,如何配置参数进行监控~~
        简单的土豆:看看是不是这样:https://github.com/alibaba/druid/issues/1802
        周浩_52f2:@尔东陈_92 并没有,还在寻找答案
        _尔东陈_:出现同样的问题,有解决方案吗?
      • 袏扌戒指:你好,按照你的方法多数据源配置成功,启动没有问题,但是在调用其中一个对象的时候会报jdbcUrl is required with driverClassName 这个问题,我用的sql server的数据库spring.datasource.cas.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver
      • niuxx:同时操作多个数据源 可以保证事务完整吗?
      • f58e261eec45:博主你好,想请问:
        比如我配置mysql与oracle两个数据源,使用的是jpa的方式,请问我的hibernate方言怎么配置,要配置两个吗
      • 07f266cd80eb:能不能只通过一个配置文件,动态切换多个数据源呐,假如有10几个数据源,那不是要写10几个配置类啊
        6760c9c79a35:写个aop注解在使用threadlocal放入数据源,就可以了
        Lion_4e3b:这样的话 每个项目不是都要有很多要复制的代码了??
        程序猿DD:@对我是坏人 用yaml配置
      • 烟雨凌:只能spring boot这样配置吗 普通的spring mvc项目可以这样配置多数据源吗
      • c62ce50e8fbb:有没有对mybatis的支持?
      • bf1aa371bed7:感谢,成功的配置好了,但是如果两个数据源不同例如一个mysql一个mongodb,也可以按照这种方式配置吗
        Luckyqiao:@程序猿DD 实测,并不能这样配置
        程序猿DD:@liiohm11 是的
      • 程序熊大:这两个数据源可以互相作热备份嘛,自动切换?
        马拉松Mara:哈哈、在这里碰到你了
      • youngjdong:实用

      本文标题:Spring Boot多数据源配置与使用

      本文链接:https://www.haomeiwen.com/subject/znbmlttx.html