Spring Data JPA 多数据源配置总结
Spring boot中Jpa的自动配置
Spring Boot 中,jpa 自动配置在JpaBaseConfiguration中能看到。
JpaBaseConfiguration是一个抽象类,只有HibernateJpaAutoConfiguration一个子类(?)
JpaBaseConfiguration提供JPA默认的基础配置,HibernateJpaAutoConfiguration则提供Vendor专有的配置。
其实JPA的Vendor只有Hibernate使用最广泛。
JpaBaseConfiguration主要提供4个Bean:
PlatformTransactionManager类型的transactionManagerJpaVendorAdapterEntityManagerFactoryBuilderLocalContainerEntityManagerFactoryBean类型的entityManagerFactory
这4个bean的主要职责分别为:
- 为JPA提供事务支持,注意这里需要使用
JpaTransactionManager - 为
EntityManagerFactoryBuilder提供Vender相关的特殊信息 - 用来构建
EntityManagerFactory的Builder - 使用
EntityManagerFactoryBuilder构建的entityManagerFactory
而在JpaBaseConfiguration的子类HibernateJpaAutoConfiguration中,则提供了Hibernate相关的Vender特殊配置。
另外,其中部分Hiberante专有Vender相关特殊配置来源于JpaProperties.getHibernateProperties(datasource)方法。
特别是hiberante.id.new_generator_mappings相关的配置,这个配置决定了Hibernate是否启用新ID Generator。
如果没有特别配置,并且当前hibernate的版本是5的话,这个设置会被默认设置为false。
在MySQL中,这决定了strategy=AUTO的@GeneratedValue是使用Column的auto increment
还是走由hibernate生成的sequence表(hibernate_sequence)。
JpaBaseConfiguration启用了一个配置属性Bean(ConfigurationProperties):
JpaProperties它的前缀为spring.jpa,用来参与jpa相关的配置。另外它还提供了Hibernate作为JPA的Vender相关的配置。
Spring boot中Datasource的自动配置
自动配置参考DataSourceAutoConfiguration类,这个类的主要目的是为了根据动态条件导入一下几个类:
EmbeddedDataSourceConfiguration,此类负责配置h2嵌入式数据库DataSourceConfiguration.Tomcat/Hikari/Dbcp/Dbcp2/Generic,此类负责配置数据库链接缓存池。
当自动配置条件符合h2的时候,EmbeddedDataSourceConfiguration会被导入并启用,可以在该类中看到,
真正的datasource是通过EmbeddedDatabaseBuilder加载DataSourceProperties类来构建的。
当自动配置条件符合其他,也就是Tomcat/Hikari/Dbcp/Dbcp2/Generic的情况下,
DataSource是通过DataSourceProperties类中的initializeDataSourceBuilder方法返回的DataSourceBuilder来构建的。
DataSourceProperties类的基本职责是为了读取application.yml文件中spring.datasource下的各项配置项的。
而该类中的initializeDataSourceBuilder方法则是将DataSourceProperties读取的数据库配置
放入一个新DataSourceBuilder实例并返回。
在DataSourceConfiguration类中,不同类型的数据库链接池通过对DataSourceProperties传入不同的type
来指定DataSourceBuilder构建不同类型的数据源。这个type可以通过spring.datasource.type来指定。
这个spring.datasource.type可以指定的值有:
org.apache.tomcat.jdbc.pool.DataSourcecom.zaxxer.hikari.HikariDataSourceorg.apache.commons.dbcp.BasicDataSourceorg.apache.commons.dbcp2.BasicDataSource
可以看到这些类都是常用的db pool。并且,如果在当前classpath上存在这些类中的某一个,但是
又没有在application.yml中指定spring.datasource.type,这些相应的配置也会生效。
譬如如果当前系统是tomcat容器,在classpath上存在org.apache.tomcat.jdbc.pool.DataSource,
但并没有在application.yml中指定spring.datasource.type=org.apache.tomcat.jdbc.pool.DataSource,
则Tomcat的数据库缓存池也会起效。
如果没有指定任何spring.datasource.type,并且在classpath上也不存在上述那些DataSource类,
则不会针对DataSourceBuilder指定任何type。一般来说会直接new一个在application.yml中
指定的driverClass的对象(一般来说是DataSource接口的实现)出来并返回。
另外需要提一下的是,如果你指定了type,或者知道系统会明确使用哪个db pool的话,
本来指定在spring.datasource下的配置可以指定到相应的type类型下,譬如tomcat的话可以指定到
spring.datasource.tomcat下,hikari的话是spring.datasource.hikari下。
针对DataSourceBuilder,我们还需要特别说明的是,当DataSourceBuilder build datasource的时候,
如果当前没有指定任何type,它还会再次尝试通过Class.forName的方式去加载上述4个db pool,
如果失败了,则会报No supported DataSource Type found的错。
另外,如果你没有指定driverClassName,这个类还会通过猜测database的url来获取driver class name。
自行配置数据源和相应的JPA
知道了Spring boot是如何自动配置JPA和数据源的,我们就能手动配置了。
首先需要配置数据源,数据源最简单的配置方式是,首先使用@ConfigurationProperties将相应的数据库配置
注入DataSourceProperties对象。然后使用该对象的initializeDataSourceBuilder方法初始化一个
DataSourceBuilder然后build即可。譬如
@Bean
@ConfigurationProperties("spring.datasource")
public DataSourceProperties dataSourceProperties() {
return new DataSourceProperties();
}
@Bean
public DataSource dataSource() {
return dataSourceProperties().initializeDataSourceBuilder().build();
}
这样我们就能根据spring.datasource下的配置注入数据源了。
然后JPA需要注入两个Bean,一个是LocalContainerEntityManagerFactoryBean,此FactoryBean
主要用来为JPA提供EntityManager。另外一个是PlatformTransactionManager,此transactionManager
主要为EntityManager提供事务管理的支持。
其中,LocalContainerEntityManagerFactoryBean可以使用auto config提供的EntityManagerFactoryBuilder来构建。
然后,为了让Spring Data JPA知道我们需要使用Repository并且扫描指定的目录,可以使用@EnableJpaRepository注解
来指定相应的package。另外,我们还可以使用JpaProperties来获取spring.jpa下的vender相关配置。
一个示例配置如下:
@Configuration
@EnableConfigurationProperties(JpaRepoerties.class)
@EnableJpaRepository(basePackageClasses = {RepositoryScanMarker.class}) // 指定Spring Data JPA的Repository所在包
public class JpaRepositoryConfiguration {
@Autowired
private JpaProperties jpaProperties;
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder, DataSource datasource) {
return builder.dataSource(datasource) // 指定数据源
.packages(EntityScanMarker.class) // 指定Entity所在包
.properties(jpaProperties.getHibernateProperties(dataSource)) // 获取并注入hibernate vender相关配置
.persistenceUnit("default") // 指定persistence unit
.build();
}
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory); // 构建事务管理器
}
}
如果要配置多个数据源和多个entityManager,只需要注入多个LocalContainerEntityManagerFactoryBean
和多个transactionManager即可。但需要注意以下事项:
- 为一个
LocalContainerEntityManagerFactoryBean和transactionManager的bean增加@Primary, 这样如果后面有自动配置的情况,系统能知道默认使用哪个EntityManagerFactoryBean和哪个transactionManager。 @EnableJpaRepository会自动使用默认名字为entityManagerFactory的LocalContainerEntityManagerFactoryBean和默认名字为transactionManager的TransactionManager。如果要指定多个EntityManager的情况, 需要在@EnableJpaRepository上使用entityManagerFactoryRef和transactionManagerRef另外指定不同的LocalContainerEntityManagerFactoryBean和TransactionManager.- 另外,
TransactionManager必须是JpaTransactionManager!必须是JpaTransactionManager! 必须是JpaTransactionManager!入过的坑必须要说3遍。
- 完 -
