数据访问
Spring Boot 提供了多个用于数据源操作的 starter。 本节解答与数据源相关的问题。
配置自定义 DataSource
要配置你自己的 DataSource
,只需在配置类中定义一个该类型的 @Bean
。
Spring Boot 会在任何需要的地方(包括数据库初始化)复用你的 DataSource
。
如果需要将部分设置外部化,可以将 DataSource
绑定到环境(参见 第三方配置)。
以下示例展示了如何在 bean 中定义数据源:
-
Java
-
Kotlin
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MyDataSourceConfiguration {
@Bean
@ConfigurationProperties("app.datasource")
public SomeDataSource dataSource() {
return new SomeDataSource();
}
}
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration(proxyBeanMethods = false)
class MyDataSourceConfiguration {
@Bean
@ConfigurationProperties("app.datasource")
fun dataSource(): SomeDataSource {
return SomeDataSource()
}
}
以下示例展示了如何通过属性设置数据源:
-
Properties
-
YAML
app.datasource.url=jdbc:h2:mem:mydb
app.datasource.username=sa
app.datasource.pool-size=30
app:
datasource:
url: "jdbc:h2:mem:mydb"
username: "sa"
pool-size: 30
假设 SomeDataSource
拥有 URL、用户名和连接池大小等常规 JavaBean 属性,这些设置会在 DataSource
被其他组件使用前自动绑定。
Spring Boot 还提供了一个名为 DataSourceBuilder
的实用构建器类,可用于创建标准数据源(如果在 classpath 上)。
该构建器会根据 classpath 上可用内容自动检测使用哪种数据源,并根据 JDBC URL 自动检测驱动。
以下示例展示了如何使用 DataSourceBuilder
创建数据源:
-
Java
-
Kotlin
import javax.sql.DataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MyDataSourceConfiguration {
@Bean
@ConfigurationProperties("app.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
}
import javax.sql.DataSource
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.jdbc.DataSourceBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration(proxyBeanMethods = false)
class MyDataSourceConfiguration {
@Bean
@ConfigurationProperties("app.datasource")
fun dataSource(): DataSource {
return DataSourceBuilder.create().build()
}
}
要运行带有该 DataSource
的应用,只需提供连接信息。
也可以提供特定于连接池的设置。
具体细节请参考运行时所用实现。
以下示例展示了如何通过属性定义 JDBC 数据源:
-
Properties
-
YAML
app.datasource.url=jdbc:mysql://localhost/test
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.pool-size=30
app:
datasource:
url: "jdbc:mysql://localhost/test"
username: "dbuser"
password: "dbpass"
pool-size: 30
但由于方法返回类型为 DataSource
,实际连接池类型被隐藏,因此不会为你的自定义 DataSource
生成配置属性元数据,IDE 也无法自动补全。
为解决此问题,可使用构建器的 type(Class)
方法指定要构建的 DataSource
类型,并更新方法返回类型。
例如,以下展示了如何用 DataSourceBuilder
创建 HikariDataSource
:
-
Java
-
Kotlin
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MyDataSourceConfiguration {
@Bean
@ConfigurationProperties("app.datasource")
public HikariDataSource dataSource() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
}
import com.zaxxer.hikari.HikariDataSource
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.jdbc.DataSourceBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration(proxyBeanMethods = false)
class MyDataSourceConfiguration {
@Bean
@ConfigurationProperties("app.datasource")
fun dataSource(): HikariDataSource {
return DataSourceBuilder.create().type(HikariDataSource::class.java).build()
}
}
但这种基础配置并不适用于 Hikari,因为 Hikari 没有 url
属性,而是 jdbc-url
,因此你必须将配置改写如下:
-
Properties
-
YAML
app.datasource.jdbc-url=jdbc:mysql://localhost/test
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.pool-size=30
app:
datasource:
jdbc-url: "jdbc:mysql://localhost/test"
username: "dbuser"
password: "dbpass"
pool-size: 30
为解决此问题,可使用 DataSourceProperties
,它会帮你处理 url
到 jdbc-url
的转换。
你可以通过其 initializeDataSourceBuilder()
方法,从任意 DataSourceProperties
对象的状态初始化 DataSourceBuilder
。
你可以注入 Spring Boot 自动创建的 DataSourceProperties
,但那样会让你的配置分散在 spring.datasource.*
和 app.datasource.*
。
为避免此问题,可自定义一个带有自定义配置前缀的 DataSourceProperties
,如下例所示:
-
Java
-
Kotlin
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
@Configuration(proxyBeanMethods = false)
public class MyDataSourceConfiguration {
@Bean
@Primary
@ConfigurationProperties("app.datasource")
public DataSourceProperties dataSourceProperties() {
return new DataSourceProperties();
}
@Bean
@ConfigurationProperties("app.datasource.configuration")
public HikariDataSource dataSource(DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
}
}
import com.zaxxer.hikari.HikariDataSource
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Primary
@Configuration(proxyBeanMethods = false)
class MyDataSourceConfiguration {
@Bean
@Primary
@ConfigurationProperties("app.datasource")
fun dataSourceProperties(): DataSourceProperties {
return DataSourceProperties()
}
@Bean
@ConfigurationProperties("app.datasource.configuration")
fun dataSource(properties: DataSourceProperties): HikariDataSource {
return properties.initializeDataSourceBuilder().type(HikariDataSource::class.java).build()
}
}
这种配置等价于 Spring Boot 默认行为,只是连接池类型在代码中指定,相关设置以 app.datasource.configuration.*
属性暴露。
DataSourceProperties
负责 url
到 jdbc-url
的转换,因此你可以这样配置:
-
Properties
-
YAML
app.datasource.url=jdbc:mysql://localhost/test
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.configuration.maximum-pool-size=30
app:
datasource:
url: "jdbc:mysql://localhost/test"
username: "dbuser"
password: "dbpass"
configuration:
maximum-pool-size: 30
注意,由于自定义配置在代码中指定了 Hikari,app.datasource.type
不再生效。
如 支持的连接池 所述,DataSourceBuilder
支持多种连接池。
如需使用非 Hikari 连接池,将其加入 classpath,使用 type(Class)
方法指定连接池类,并更新 @Bean
方法返回类型。
这样还能为你选择的连接池生成配置属性元数据。
Spring Boot 会将 Hikari 专属设置暴露为 spring.datasource.hikari 。
本例采用更通用的 configuration 子命名空间,因为示例不支持多种数据源实现。
|
更多细节参见 配置 DataSource 及 DataSourceAutoConfiguration
类。
配置两个 DataSource
要定义额外的 DataSource
,可采用与前述类似的方法。
关键区别在于 DataSource
的 @Bean
必须声明 defaultCandidate=false
,以防止自动配置的 DataSource
退让。
Spring Framework 参考文档对此特性有更详细说明。 |
如需让额外的 DataSource
可被注入,还需用 @Qualifier
注解,如下例所示:
-
Java
-
Kotlin
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MyAdditionalDataSourceConfiguration {
@Qualifier("second")
@Bean(defaultCandidate = false)
@ConfigurationProperties("app.datasource")
public HikariDataSource secondDataSource() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
}
import com.zaxxer.hikari.HikariDataSource
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.jdbc.DataSourceBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration(proxyBeanMethods = false)
class MyAdditionalDataSourceConfiguration {
@Qualifier("second")
@Bean(defaultCandidate = false)
@ConfigurationProperties("app.datasource")
fun secondDataSource(): HikariDataSource {
return DataSourceBuilder.create().type(HikariDataSource::class.java).build()
}
}
要使用该额外的 DataSource
,在注入点同样加上 @Qualifier
。
自动配置的数据源和额外数据源可按如下方式配置:
-
Properties
-
YAML
spring.datasource.url=jdbc:mysql://localhost/first
spring.datasource.username=dbuser
spring.datasource.password=dbpass
spring.datasource.configuration.maximum-pool-size=30
app.datasource.url=jdbc:mysql://localhost/second
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.max-total=30
spring:
datasource:
url: "jdbc:mysql://localhost/first"
username: "dbuser"
password: "dbpass"
configuration:
maximum-pool-size: 30
app:
datasource:
url: "jdbc:mysql://localhost/second"
username: "dbuser"
password: "dbpass"
max-total: 30
更高级、特定实现的数据源配置可通过 spring.datasource.configuration.*
属性实现。
你也可以对额外的数据源采用同样思路,如下例所示:
-
Java
-
Kotlin
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MyCompleteAdditionalDataSourceConfiguration {
@Qualifier("second")
@Bean(defaultCandidate = false)
@ConfigurationProperties("app.datasource")
public DataSourceProperties secondDataSourceProperties() {
return new DataSourceProperties();
}
@Qualifier("second")
@Bean(defaultCandidate = false)
@ConfigurationProperties("app.datasource.configuration")
public HikariDataSource secondDataSource(
@Qualifier("secondDataSourceProperties") DataSourceProperties secondDataSourceProperties) {
return secondDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
}
}
import com.zaxxer.hikari.HikariDataSource
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration(proxyBeanMethods = false)
class MyCompleteAdditionalDataSourceConfiguration {
@Qualifier("second")
@Bean(defaultCandidate = false)
@ConfigurationProperties("app.datasource")
fun secondDataSourceProperties(): DataSourceProperties {
return DataSourceProperties()
}
@Qualifier("second")
@Bean(defaultCandidate = false)
@ConfigurationProperties("app.datasource.configuration")
fun secondDataSource(secondDataSourceProperties: DataSourceProperties): HikariDataSource {
return secondDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource::class.java).build()
}
}
上述示例用与 Spring Boot 自动配置相同的逻辑配置了额外数据源。
注意,app.datasource.configuration.*
属性根据所选实现提供高级设置。
如 配置单个自定义 DataSource
,可通过 DataSourceBuilder
的 type(Class)
方法自定义一个或两个 DataSource
bean 的类型。
支持类型详见 支持的连接池。
使用 Spring Data Repositories
Spring Data 可为多种风格的 Repository
接口自动创建实现。
只要这些 Repository
实现包含在 自动配置包(通常是主应用类所在包或其子包,且带有 @SpringBootApplication
或 @EnableAutoConfiguration
注解)中,Spring Boot 会自动处理。
对于大多数应用,只需将合适的 Spring Data 依赖加入 classpath。
JPA 可用 spring-boot-starter-data-jpa
,MongoDB 可用 spring-boot-starter-data-mongodb
,其它技术也有相应 starter。
开始时,创建一些用于处理 @Entity
对象的 repository 接口即可。
Spring Boot 通过扫描 自动配置包确定 Repository
实现的位置。
如需更细致控制,可用 Spring Data 的 @Enable…Repositories
注解。
更多 Spring Data 相关内容,参见 Spring Data 项目主页。
将 @Entity 定义与 Spring 配置分离
Spring Boot 通过扫描 自动配置包确定 @Entity
定义的位置。
如需更细致控制,可用 @EntityScan
注解,如下例所示:
-
Java
-
Kotlin
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration
@EntityScan(basePackageClasses = City.class)
public class MyApplication {
// ...
}
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.autoconfigure.domain.EntityScan
import org.springframework.context.annotation.Configuration
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration
@EntityScan(basePackageClasses = [City::class])
class MyApplication {
// ...
}
过滤扫描到的 @Entity 定义
可通过 ManagedClassNameFilter
bean 过滤 @Entity
定义。
这在测试时只需考虑部分实体时很有用。
如下例,仅包含 com.example.app.customer
包下的实体:
-
Java
-
Kotlin
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.persistenceunit.ManagedClassNameFilter;
@Configuration(proxyBeanMethods = false)
public class MyEntityScanConfiguration {
@Bean
public ManagedClassNameFilter entityScanFilter() {
return (className) -> className.startsWith("com.example.app.customer.");
}
}
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.orm.jpa.persistenceunit.ManagedClassNameFilter
@Configuration(proxyBeanMethods = false)
class MyEntityScanConfiguration {
@Bean
fun entityScanFilter() : ManagedClassNameFilter {
return ManagedClassNameFilter { className ->
className.startsWith("com.example.app.customer.")
}
}
}
配置 JPA 属性
Spring Data JPA 已提供部分与厂商无关的配置选项(如 SQL 日志),Spring Boot 还将这些选项及部分 Hibernate 选项暴露为外部配置属性。 部分属性会根据上下文自动检测,无需手动设置。
spring.jpa.hibernate.ddl-auto
是一个特殊配置项,因为它会根据运行时条件采用不同的默认值。
如果使用的是嵌入式数据库,且没有像 Liquibase 或 Flyway 这样的 schema 管理器处理 DataSource
,则默认值为 create-drop
。
在所有其他情况下,默认值为 none
。
JPA 提供者会自动检测要使用的方言(dialect)。
如果你希望自行指定方言,请设置 spring.jpa.database-platform
属性。
最常用的配置选项如下例所示:
-
Properties
-
YAML
spring.jpa.hibernate.naming.physical-strategy=com.example.MyPhysicalNamingStrategy
spring.jpa.show-sql=true
spring:
jpa:
hibernate:
naming:
physical-strategy: "com.example.MyPhysicalNamingStrategy"
show-sql: true
此外,所有 spring.jpa.properties.*
下的属性在创建本地 EntityManagerFactory
时会作为常规 JPA 属性(前缀会被去除)传递。
你需要确保在 例如,如果你想配置 Hibernate 的批量大小,必须使用 |
如果你需要对 Hibernate 属性进行高级自定义,可以注册一个 HibernatePropertiesCustomizer bean,该 bean 会在创建 EntityManagerFactory 之前被调用。
这会优先生效于自动配置应用的任何内容。
|
配置 Hibernate 命名策略
Hibernate 使用 两种不同的命名策略,将对象模型中的名称映射到对应的数据库名称。
可以通过设置 spring.jpa.hibernate.naming.physical-strategy
和 spring.jpa.hibernate.naming.implicit-strategy
属性,分别配置物理和隐式策略实现类的全限定类名。
或者,如果应用上下文中存在 ImplicitNamingStrategy
或 PhysicalNamingStrategy
bean,Hibernate 会自动使用它们。
默认情况下,Spring Boot 使用 CamelCaseToUnderscoresNamingStrategy
配置物理命名策略。
采用该策略时,所有点(.)会被下划线替换,驼峰命名也会被下划线分隔。
此外,默认所有表名都会转为小写。
例如,TelephoneNumber
实体会映射为 telephone_number
表。
如果你的 schema 需要混合大小写标识符,可以像下面这样自定义 CamelCaseToUnderscoresNamingStrategy
bean:
-
Java
-
Kotlin
import org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MyHibernateConfiguration {
@Bean
public CamelCaseToUnderscoresNamingStrategy caseSensitivePhysicalNamingStrategy() {
return new CamelCaseToUnderscoresNamingStrategy() {
@Override
protected boolean isCaseInsensitive(JdbcEnvironment jdbcEnvironment) {
return false;
}
};
}
}
import org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration(proxyBeanMethods = false)
class MyHibernateConfiguration {
@Bean
fun caseSensitivePhysicalNamingStrategy(): CamelCaseToUnderscoresNamingStrategy {
return object : CamelCaseToUnderscoresNamingStrategy() {
override fun isCaseInsensitive(jdbcEnvironment: JdbcEnvironment): Boolean {
return false
}
}
}
}
如果你更倾向于使用 Hibernate 的默认策略,只需设置如下属性:
-
Properties
-
YAML
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring:
jpa:
hibernate:
naming:
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
或者,你也可以配置如下 bean:
-
Java
-
Kotlin
import org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
class MyHibernateConfiguration {
@Bean
PhysicalNamingStrategyStandardImpl caseSensitivePhysicalNamingStrategy() {
return new PhysicalNamingStrategyStandardImpl();
}
}
import org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration(proxyBeanMethods = false)
internal class MyHibernateConfiguration {
@Bean
fun caseSensitivePhysicalNamingStrategy(): PhysicalNamingStrategyStandardImpl {
return PhysicalNamingStrategyStandardImpl()
}
}
配置 Hibernate 二级缓存
Hibernate 二级缓存 可针对多种缓存提供者进行配置。 通常建议直接提供应用上下文中已有的缓存实现,而不是让 Hibernate 再次查找缓存提供者。
若要结合 JCache 使用,首先确保 org.hibernate.orm:hibernate-jcache
已在 classpath 中。
然后,添加一个 HibernatePropertiesCustomizer
bean,如下例所示:
-
Java
-
Kotlin
import org.hibernate.cache.jcache.ConfigSettings;
import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer;
import org.springframework.cache.jcache.JCacheCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MyHibernateSecondLevelCacheConfiguration {
@Bean
public HibernatePropertiesCustomizer hibernateSecondLevelCacheCustomizer(JCacheCacheManager cacheManager) {
return (properties) -> properties.put(ConfigSettings.CACHE_MANAGER, cacheManager.getCacheManager());
}
}
import org.hibernate.cache.jcache.ConfigSettings
import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer
import org.springframework.cache.jcache.JCacheCacheManager
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration(proxyBeanMethods = false)
class MyHibernateSecondLevelCacheConfiguration {
@Bean
fun hibernateSecondLevelCacheCustomizer(cacheManager: JCacheCacheManager): HibernatePropertiesCustomizer {
return HibernatePropertiesCustomizer { properties ->
properties[ConfigSettings.CACHE_MANAGER] = cacheManager.cacheManager
}
}
}
该自定义器会让 Hibernate 使用与应用一致的 CacheManager
。
当然,也可以使用不同的 CacheManager
实例。
详情请参见 Hibernate 用户指南。
在 Hibernate 组件中使用依赖注入
默认情况下,Spring Boot 会注册一个基于 BeanFactory
的 BeanContainer
实现,这样转换器和实体监听器就可以使用常规依赖注入。
你可以通过注册一个 HibernatePropertiesCustomizer
,移除或修改 hibernate.resource.beans.container
属性,从而禁用或调整该行为。
使用自定义 EntityManagerFactory
如果你需要完全控制 EntityManagerFactory
的配置,只需添加一个名为 ‘entityManagerFactory’ 的 @Bean
。
只要存在该类型的 bean,Spring Boot 的自动配置就会关闭自身的 entity manager。
如果你自行创建了 LocalContainerEntityManagerFactoryBean ,则自动配置期间应用的任何自定义都会失效。
请务必使用自动配置的 EntityManagerFactoryBuilder ,以保留 JPA 及厂商属性。
如果你依赖 spring.jpa.* 属性配置命名策略或 DDL 模式,这一点尤为重要。
|
使用多个 EntityManagerFactory
如果你需要针对多个数据源使用 JPA,通常每个数据源都需要一个 EntityManagerFactory
。
Spring ORM 提供的 LocalContainerEntityManagerFactoryBean
允许你为每个数据源配置 EntityManagerFactory
。
你还可以复用 JpaProperties
,为第二个 EntityManagerFactory
绑定设置。
基于 配置第二个 DataSource
的示例,可以如下定义第二个 EntityManagerFactory
:
-
Java
-
Kotlin
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Function;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
@Configuration(proxyBeanMethods = false)
public class MyAdditionalEntityManagerFactoryConfiguration {
@Qualifier("second")
@Bean(defaultCandidate = false)
@ConfigurationProperties("app.jpa")
public JpaProperties secondJpaProperties() {
return new JpaProperties();
}
@Qualifier("second")
@Bean(defaultCandidate = false)
public LocalContainerEntityManagerFactoryBean secondEntityManagerFactory(@Qualifier("second") DataSource dataSource,
@Qualifier("second") JpaProperties jpaProperties) {
EntityManagerFactoryBuilder builder = createEntityManagerFactoryBuilder(jpaProperties);
return builder.dataSource(dataSource).packages(Order.class).persistenceUnit("second").build();
}
private EntityManagerFactoryBuilder createEntityManagerFactoryBuilder(JpaProperties jpaProperties) {
JpaVendorAdapter jpaVendorAdapter = createJpaVendorAdapter(jpaProperties);
Function<DataSource, Map<String, ?>> jpaPropertiesFactory = (dataSource) -> createJpaProperties(dataSource,
jpaProperties.getProperties());
return new EntityManagerFactoryBuilder(jpaVendorAdapter, jpaPropertiesFactory, null);
}
private JpaVendorAdapter createJpaVendorAdapter(JpaProperties jpaProperties) {
// ... map JPA properties as needed
return new HibernateJpaVendorAdapter();
}
private Map<String, ?> createJpaProperties(DataSource dataSource, Map<String, ?> existingProperties) {
Map<String, ?> jpaProperties = new LinkedHashMap<>(existingProperties);
// ... map JPA properties that require the DataSource (e.g. DDL flags)
return jpaProperties;
}
}
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.orm.jpa.JpaVendorAdapter
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter
import javax.sql.DataSource
@Configuration(proxyBeanMethods = false)
class MyAdditionalEntityManagerFactoryConfiguration {
@Qualifier("second")
@Bean(defaultCandidate = false)
@ConfigurationProperties("app.jpa")
fun secondJpaProperties(): JpaProperties {
return JpaProperties()
}
@Qualifier("second")
@Bean(defaultCandidate = false)
fun firstEntityManagerFactory(
@Qualifier("second") dataSource: DataSource,
@Qualifier("second") jpaProperties: JpaProperties
): LocalContainerEntityManagerFactoryBean {
val builder = createEntityManagerFactoryBuilder(jpaProperties)
return builder.dataSource(dataSource).packages(Order::class.java).persistenceUnit("second").build()
}
private fun createEntityManagerFactoryBuilder(jpaProperties: JpaProperties): EntityManagerFactoryBuilder {
val jpaVendorAdapter = createJpaVendorAdapter(jpaProperties)
val jpaPropertiesFactory = { dataSource: DataSource ->
createJpaProperties(dataSource, jpaProperties.properties) }
return EntityManagerFactoryBuilder(jpaVendorAdapter, jpaPropertiesFactory, null)
}
private fun createJpaVendorAdapter(jpaProperties: JpaProperties): JpaVendorAdapter {
// ... map JPA properties as needed
return HibernateJpaVendorAdapter()
}
private fun createJpaProperties(dataSource: DataSource, existingProperties: Map<String, *>): Map<String, *> {
val jpaProperties: Map<String, *> = LinkedHashMap(existingProperties)
// ... map JPA properties that require the DataSource (e.g. DDL flags)
return jpaProperties
}
}
上述示例通过 @Qualifier("second")
注解,使用名为 second 的 DataSource
bean 创建 EntityManagerFactory
。
它会扫描与 Order
同包下的实体。
也可以通过 app.jpa
命名空间映射额外的 JPA 属性。
使用 @Bean(defaultCandidate=false)
可定义 secondJpaProperties
和 secondEntityManagerFactory
bean,而不会影响同类型的自动配置 bean。
Spring Framework 参考文档对此特性有更详细说明。 |
如需为更多数据源提供 JPA 支持,可采用类似配置。
此外,还需为每个 EntityManagerFactory
配置一个 JpaTransactionManager
。
或者,也可以使用同时管理多个数据源的 JTA 事务管理器。
如果你使用 Spring Data,需要相应配置 @EnableJpaRepositories
,如下例所示:
-
Java
-
Kotlin
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@Configuration(proxyBeanMethods = false)
@EnableJpaRepositories(basePackageClasses = Order.class, entityManagerFactoryRef = "entityManagerFactory")
public class OrderConfiguration {
}
import org.springframework.context.annotation.Configuration
import org.springframework.data.jpa.repository.config.EnableJpaRepositories
@Configuration(proxyBeanMethods = false)
@EnableJpaRepositories(basePackageClasses = [Order::class], entityManagerFactoryRef = "firstEntityManagerFactory")
class OrderConfiguration
-
Java
-
Kotlin
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@Configuration(proxyBeanMethods = false)
@EnableJpaRepositories(basePackageClasses = Customer.class, entityManagerFactoryRef = "secondEntityManagerFactory")
public class CustomerConfiguration {
}
import org.springframework.context.annotation.Configuration
import org.springframework.data.jpa.repository.config.EnableJpaRepositories
@Configuration(proxyBeanMethods = false)
@EnableJpaRepositories(basePackageClasses = [Customer::class], entityManagerFactoryRef = "secondEntityManagerFactory")
class CustomerConfiguration
使用传统 persistence.xml 文件
Spring Boot 默认不会查找或使用 META-INF/persistence.xml
。
如果你更倾向于使用传统的 persistence.xml
,需要自行定义一个类型为 LocalEntityManagerFactoryBean
(ID 为 ‘entityManagerFactory’)的 @Bean
,并在其中设置持久化单元名称。
默认设置请参见 JpaBaseConfiguration
。
同时使用 Spring Data JPA 与 Mongo 仓库
Spring Data JPA 和 Spring Data Mongo 都可以自动为你创建 Repository
实现。
如果它们同时存在于 classpath 上,可能需要额外配置以告知 Spring Boot 需要创建哪些仓库。
最直接的方式是使用标准的 Spring Data @EnableJpaRepositories
和 @EnableMongoRepositories
注解,并指定 Repository
接口的位置。
你还可以通过配置 spring.data.*.repositories.enabled
和 spring.data.*.repositories.type
等标志,在外部配置中切换自动配置的仓库开关。
例如,如果你只想关闭 Mongo 仓库但仍使用自动配置的 MongoTemplate
,这种方式非常有用。
对于其他自动配置的 Spring Data 仓库类型(如 Elasticsearch、Redis 等),同样适用。 只需相应更改注解和配置项名称即可。
自定义 Spring Data 的 Web 支持
Spring Data 提供了 Web 支持,简化了在 Web 应用中使用 Spring Data 仓库的流程。
Spring Boot 提供了 spring.data.web
命名空间下的属性用于自定义相关配置。
注意,如果你使用 Spring Data REST,则应使用 spring.data.rest
命名空间下的属性。
将 Spring Data 仓库暴露为 REST 端点
Spring Data REST 可以将 Repository
实现自动暴露为 REST 端点,前提是应用已启用 Spring MVC。
Spring Boot 提供了一组有用的属性(位于 spring.data.rest
命名空间),用于自定义 RepositoryRestConfiguration
。
如需进一步自定义,可使用 RepositoryRestConfigurer
bean。
如果你没有为自定义的 RepositoryRestConfigurer 指定顺序(order),它会在 Spring Boot 内部使用的配置器之后运行。
如需指定顺序,请确保其值大于 0。
|
配置 JPA 依赖的组件
如果你需要配置一个被 JPA 使用的组件,必须确保该组件在 JPA 之前初始化。 如果该组件是自动配置的,Spring Boot 会自动处理依赖顺序。 例如,当 Flyway 被自动配置时,Hibernate 会被配置为依赖 Flyway,从而保证 Flyway 能在 Hibernate 使用数据库前完成初始化。
如果你自行配置组件,可以通过继承 EntityManagerFactoryDependsOnPostProcessor
,方便地设置所需依赖关系。
例如,如果你将 Hibernate Search 与 Elasticsearch 结合使用,所有 EntityManagerFactory
bean 都必须依赖 elasticsearchClient
bean,如下例所示:
-
Java
-
Kotlin
import jakarta.persistence.EntityManagerFactory;
import org.springframework.boot.autoconfigure.orm.jpa.EntityManagerFactoryDependsOnPostProcessor;
import org.springframework.stereotype.Component;
/**
* {@link EntityManagerFactoryDependsOnPostProcessor} that ensures that
* {@link EntityManagerFactory} beans depend on the {@code elasticsearchClient} bean.
*/
@Component
public class ElasticsearchEntityManagerFactoryDependsOnPostProcessor
extends EntityManagerFactoryDependsOnPostProcessor {
public ElasticsearchEntityManagerFactoryDependsOnPostProcessor() {
super("elasticsearchClient");
}
}
import org.springframework.boot.autoconfigure.orm.jpa.EntityManagerFactoryDependsOnPostProcessor
import org.springframework.stereotype.Component
@Component
class ElasticsearchEntityManagerFactoryDependsOnPostProcessor :
EntityManagerFactoryDependsOnPostProcessor("elasticsearchClient")
配置 jOOQ 支持多个数据源
如果你需要让 jOOQ 支持多个数据源,应为每个数据源分别创建 DSLContext
。
更多细节请参见 JooqAutoConfiguration
。
特别地,ExceptionTranslatorExecuteListener 和 SpringTransactionProvider 可复用,实现类似单一 DataSource 自动配置下的功能。
|