详解基于Spring Boot与Spring Data JPA的多数据源配置

 更新时间:2017年05月19日 16:38:41   作者:gabriel_2  
本篇文章主要介绍了详解基于Spring Boot与Spring Data JPA的多数据源配置,非常具有实用价值,需要的朋友可以参考下

由于项目需要,最近研究了一下基于spring Boot与Spring Data JPA的多数据源配置问题。以下是传统的单数据源配置代码。这里使用的是Spring的Annotation在代码内部直接配置的方式,没有使用任何XML文件。

@Configuration 
@EnableJpaRepositories(basePackages = "org.lyndon.repository") 
@EnableTransactionManagement 
@PropertySource("classpath:application.properties") 
public class JpaConfig { 
   
  private static final String DATABASE_DRIVER = "db.driver"; 
  private static final String DATABASE_URL = "db.url"; 
  private static final String DATABASE_USER = "db.user"; 
  private static final String DATABASE_PASSWORD = "db.password"; 
  private static final String PACKAGES_TO_SCAN = "packages.to.scan"; 
  private static final String HIBERNATE_DIALECT = "hibernate.dialect"; 
  private static final String HIBERNATE_SHOW_SQL = "hibernate.show.sql"; 
   
  @Resource 
  private Environment env; 
   
  @Bean 
  public DataSource dataSource() { 
    DruidDataSource source = new DruidDataSource(); 
    source.setDriverClassName(env.getRequiredProperty(DATABASE_DRIVER)); 
    source.setUrl(env.getRequiredProperty(DATABASE_URL)); 
    source.setUsername(env.getRequiredProperty(DATABASE_USER)); 
    source.setPassword(env.getRequiredProperty(DATABASE_PASSWORD)); 
    return source; 
  } 
   
  @Bean 
  public LocalContainerEntityManagerFactoryBean entityManagerFactory() { 
    LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean(); 
    factory.setDataSource(dataSource()); 
    factory.setPersistenceProviderClass(HibernatePersistenceProvider.class); 
    factory.setPackagesToScan(env.getRequiredProperty(PACKAGES_TO_SCAN)); 
    factory.setJpaProperties(hibernateProperties()); 
    factory.afterPropertiesSet(); 
    return factory; 
  } 
   
  @Bean 
  public PlatformTransactionManager transactionManager() { 
    JpaTransactionManager manager = new JpaTransactionManager(); 
    manager.setEntityManagerFactory(entityManagerFactory().getObject()); 
    return manager; 
  } 
   
  @Bean 
  public HibernateExceptionTranslator hibernateExceptionTranslator() { 
    return new HibernateExceptionTranslator(); 
  } 
   
  private Properties hibernateProperties() { 
    Properties properties = new Properties(); 
    properties.put(HIBERNATE_DIALECT, env.getRequiredProperty(HIBERNATE_DIALECT)); 
    properties.put(HIBERNATE_SHOW_SQL, env.getRequiredProperty(HIBERNATE_SHOW_SQL)); 
    return properties; 
  } 
   
} 

但是这一配置是不能简单地扩展到多数据源配置的,因为Spring Boot默认会为开发人员做很多工作,而这些工作与多数据源的配置相冲突,因此需要修改原来的配置内容。网上有很多讲解多数据源配置的文章,但是这些文章大多使用的是XML配置的方式,而且没有使用Spring Boot等比较新的Spring框架技术(比如很多人使用实现AbstractRoutingDataSource这一Spring提供的抽象类的方式,还需要切面的支持,无疑是相当繁琐的),已经不适用于在最新的工程项目中使用了,因为最新的Spring框架可以为我们完成很多事情,我们只需要去适应新的方法即可。为此,我通过研究Spring的官方文档和不断调试,实现了新的多数据源配置的方法,在此贴出,仅供参考。

首先,Spring的JPA是直接支持多数据源配置的,因此我们可以在配置文件或者代码中直接配置多个数据源。由于多数据源配置可能会共享一些配置信息,因此使用继承体系实现这种配置是最合适的。在例子中,我的ORM框架使用的是hibernate,而与Hibernate相关的配置信息我都放在了配置基类BaseJpaConfg中,代码如下。

@Configuration 
@EnableTransactionManagement 
@PropertySource("classpath:application.properties") 
public class BaseJpaConfig { 
 
  private static final String HIBERNATE_DIALECT = "hibernate.dialect"; 
  private static final String HIBERNATE_SHOW_SQL = "hibernate.show.sql"; 
 
  @Resource 
  protected Environment env; 
 
  @Bean 
  public HibernateExceptionTranslator hibernateExceptionTranslator() { 
    return new HibernateExceptionTranslator(); 
  } 
 
  protected Properties hibernateProperties() { 
    Properties properties = new Properties(); 
    properties.put(HIBERNATE_DIALECT, env.getRequiredProperty(HIBERNATE_DIALECT)); 
    properties.put(HIBERNATE_SHOW_SQL, env.getRequiredProperty(HIBERNATE_SHOW_SQL)); 
    return properties; 
  } 
 
} 

该类提供Hibernate相关信息。它有两个子类,分别是SpringJpaConfig以及MysqlJpaConfig,分别配置了两个数据源,连接到两个不同的数据库Schema,代码如下。

@Configuration 
@EnableJpaRepositories(basePackages = {"org.lyndon.repository1"}, entityManagerFactoryRef = "springEntityManagerFactory", 
    transactionManagerRef = "springTransactionManager") 
public class SpringJpaConfig extends BaseJpaConfig { 
 
  private static final String DATABASE_DRIVER = "db.driver"; 
  private static final String DATABASE_URL = "db.url"; 
  private static final String DATABASE_USER = "db.user"; 
  private static final String DATABASE_PASSWORD = "db.password"; 
  private static final String PACKAGES_TO_SCAN = "packages.to.scan"; 
 
  @Bean 
  @ConfigurationProperties(prefix = "datasource.primary") 
  public DataSource dataSource() { 
    DruidDataSource source = new DruidDataSource(); 
    source.setDriverClassName(env.getRequiredProperty(DATABASE_DRIVER)); 
    source.setUrl(env.getRequiredProperty(DATABASE_URL)); 
    source.setUsername(env.getRequiredProperty(DATABASE_USER)); 
    source.setPassword(env.getRequiredProperty(DATABASE_PASSWORD)); 
    return source; 
  } 
 
  @Bean(name = "springEntityManagerFactory") 
  public LocalContainerEntityManagerFactoryBean springEntityManagerFactory() { 
    LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean(); 
    factory.setDataSource(dataSource()); 
    factory.setPersistenceProviderClass(HibernatePersistenceProvider.class); 
    factory.setPackagesToScan(env.getRequiredProperty(PACKAGES_TO_SCAN).split(",")); 
    factory.setJpaProperties(hibernateProperties()); 
    factory.afterPropertiesSet(); 
    return factory; 
  } 
 
  @Bean(name = "springTransactionManager") 
  @Primary 
  public PlatformTransactionManager transactionManager() { 
    JpaTransactionManager manager = new JpaTransactionManager(); 
    manager.setEntityManagerFactory(springEntityManagerFactory().getObject()); 
    return manager; 
  } 
 
} 
@Configuration 
@EnableJpaRepositories(basePackages = {"org.lyndon.repository2"}, entityManagerFactoryRef = "mysqlEntityManagerFactory", 
    transactionManagerRef = "mysqlTransactionManager") 
public class MysqlJpaConfig extends BaseJpaConfig { 
 
  private static final String DATABASE_DRIVER = "db.driver"; 
  private static final String DATABASE_URL = "db.url2"; 
  private static final String DATABASE_USER = "db.user"; 
  private static final String DATABASE_PASSWORD = "db.password"; 
  private static final String PACKAGES_TO_SCAN = "packages.to.scan2"; 
 
  @Bean 
  @ConfigurationProperties(prefix = "datasource.secondary") 
  public DataSource dataSource2() { 
    DruidDataSource source = new DruidDataSource(); 
    source.setDriverClassName(env.getRequiredProperty(DATABASE_DRIVER)); 
    source.setUrl(env.getRequiredProperty(DATABASE_URL)); 
    source.setUsername(env.getRequiredProperty(DATABASE_USER)); 
    source.setPassword(env.getRequiredProperty(DATABASE_PASSWORD)); 
    return source; 
  } 
 
  @Bean(name = "mysqlEntityManagerFactory") 
  public LocalContainerEntityManagerFactoryBean mysqlEntityManagerFactory() { 
    LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean(); 
    factory.setDataSource(dataSource2()); 
    factory.setPersistenceProviderClass(HibernatePersistenceProvider.class); 
    factory.setPackagesToScan(env.getRequiredProperty(PACKAGES_TO_SCAN).split(",")); 
    factory.setJpaProperties(hibernateProperties()); 
    factory.afterPropertiesSet(); 
    return factory; 
  } 
 
  @Bean(name = "mysqlTransactionManager") 
  public PlatformTransactionManager transactionManager() { 
    JpaTransactionManager manager = new JpaTransactionManager(); 
    manager.setEntityManagerFactory(mysqlEntityManagerFactory().getObject()); 
    return manager; 
  } 
 
} 

两份配置代码使用了各自的数据源、实体管理对象以及事务管理对象。这里要注意的有两点,首先是实体管理对象。Spring Boot框架为方便开发者,默认会寻找名为“entityManagerFactory”的Bean作为实体管理的实现,但是我们这里使用了两个实体管理对象,名称也并不是默认的“entityManagerFactory”,因此必须在各自的EnableJpaRepositories的Annotation中指明使用的实体管理对象。第二点,就是事务管理对象。Spring Boot会提供一个默认的事务管理对象的实现,但是我们在这里使用了两个不同的事务管理对象,因此我们也需要在各自的EnableJpaRepositories的Annotation中指明使用的事务管理对象。除此以外,我们还必须指定其中一个事务管理对象为主要对象(使用Primary这一Annotation),让Spring能有主次地使用相应的事务管理对象。

以上就是本文的主要内容。关于Service的配置等问题,由于和单数据源时是一样的,因此就不再赘述了。基于上述代码,我们就可以使用Spring Boot实现多数据源之间的无缝切换了,是不是很轻松?如果你不想使用代码配置的方式,也可以使用XML文件代替,配置的核心属性与上文相同,可以自己加以琢磨。希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • Mybatis Lombok使用方法与复杂查询介绍

    Mybatis Lombok使用方法与复杂查询介绍

    Lombok是一种Java实用工具,可用来帮助开发人员消除Java的冗长,尤其是对于简单的Java对象(POJO),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2022-10-10
  • Java 全排列的几种实现方法

    Java 全排列的几种实现方法

    本文详细介绍了Java中全排列问题的几种实现方法,包括回溯法、字典序排列法和迭代法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-11-11
  • spring-boot中的SPI机制实例讲解

    spring-boot中的SPI机制实例讲解

    这篇文章主要介绍了spring-boot中的SPI机制实例讲解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • Spring容器注入bean的五种方法逐个解析

    Spring容器注入bean的五种方法逐个解析

    依赖注入(Dependency Injection)和控制反转(Inversion of Control)是同一个概念。具体含义是:当某个角色(可能是一个Java实例,调用者)需要另一个角色(另一个Java实例,被调用者)的协助时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例
    2023-02-02
  • java多线程编程之从线程返回数据的两种方法

    java多线程编程之从线程返回数据的两种方法

    从线程中返回数据和向线程传递数据类似。也可以通过类成员以及回调函数来返回数据。但类成员在返回数据和传递数据时有一些区别,下面让我们来看看它们区别在哪
    2014-01-01
  • Java之dao模式详解及代码示例

    Java之dao模式详解及代码示例

    这篇文章主要介绍了Java之dao模式详解及代码示例,具有一定参考价值,需要的朋友可以了解下。
    2017-11-11
  • 详解Java如何简化条件表达式

    详解Java如何简化条件表达式

    在复杂的实际业务中,往往会出现各种嵌套的条件判断逻辑。随着需求的增加,条件逻辑会变得越来越复杂。面对这种情况,简化判断逻辑就是不得不做的事情,下面为大家介绍几种方法
    2022-06-06
  • 浅析Java虚拟机详解之概述、对象生存法则

    浅析Java虚拟机详解之概述、对象生存法则

    这篇文章主要介绍了Java虚拟机详解之概述、对象生存法则,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-04-04
  • Java基于ShardingSphere实现分库分表的实例详解

    Java基于ShardingSphere实现分库分表的实例详解

    ShardingSphere 已于2020年4月16日成为 Apache 软件基金会的顶级项目, 它们均提供标准化的数据水平扩展、分布式事务和分布式治理等功能,可适用于如 Java 同构、异构语言、云原生等各种多样化的应用场景,对ShardingSphere分库分表相关知识感兴趣的朋友一起看看吧
    2022-03-03
  • Java关于JDK1.8中的Optional类

    Java关于JDK1.8中的Optional类

    本文主要介绍了Optional类的一些常用方法,以及其应用场景,其主要是为了规避空指针异常(NPE)。熟练的运用Optional类可以很大的简化我们的代码,使代码简洁明了。,需要的朋友可以参考下面文章内容
    2021-09-09

最新评论