9. 数据
Spring Boot与多个数据技术集成,包括SQL和NoSQL。
9.1. SQL数据库
Spring Framework为和谐提供扩展支持SQL数据工作,从使用JdbcTemplate
直接JDBC访问完整的对象关系映射技术,如Hibernate。Spring Data提供额外的功能级别:直接从接口创建Repository
实现并使用协议从方法名称生成查询。
9.1.1. 配置数据源
Java的javax.sql.DataSource
接口提供与数据库链接的标准方法。传统上,‘DataSource’使用URL
与证书一起建立数据库链接。
查看"How-to"了解更多高级示例,通常是为了完全控制数据源的配置。
嵌入式数据库支持
基于内存的嵌入式数据库开发应用程序通常非常方便。显然,内存数据库不提供持久存储。您需要在应用程序开始时填写数据,并在应用程序结束时丢弃数据。
"How-to"章节包括如何初始化数据库的一部分。
Spring Boot内嵌可自动配置H2,HSQL和Derby数据库。您不需要提供任何连接URL。你只需要包含你想要使用的内嵌数据库的构建依赖。如果在类路径有多个内嵌数据库,设置spring.datasource.embedded-database-connection
配置属性控制您使用的属性。设置属性值为none
禁止自动配置嵌入式数据。
如果您在测试中使用此功能,您可能会注意到,无论您使用多少在线应用程序,您的所有测试套件都重用了相同的数据库。如果您想确保每个上下文都有一个单独的嵌入式数据库,您应该设置它
spring;datasource.generate-unique-name
为true
。
例如,典型的POM依赖如下:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.hsqldb</groupId> <artifactId>hsqldb</artifactId> <scope>runtime</scope> </dependency>
你需要在
spring-jdbc
上一个依赖项用于自动配置嵌入式数据库。在这个例子中,它通过spring-boot-starter-data-jpa
传递拉入。
如果,无论什么原因,你所做的配置链接URL对于嵌入式数据库,请确保禁止自动停止数据库。如果您使用它H2.你应该为此使用它
DB_CLOSE_ON_EXIST=FALSE
。如果你使用HSQLDB,确保不使用shutdown=true
。当数据库关闭时,禁用数据库可以自动停止Spring Boot控制,确保不需要访问数据库自动关闭。
连接生产数据库
也可以使用生产数据库链接DataSource
自动配置连接池。
DataSource配置
在spring.datasource.*
外部配置属性控制DataSource配置。例如,你可以application.properties
声明如下:
spring: datasource: url: "jdbc:mysql://localhost/test"/span> username: "dbuser" password: "dbpass"
你应该至少通过设置
spring.datasource.url
属性指定URL。否则,Spring Boot尝试自动配置一个内嵌数据库。
Spring Boot可以从URL中推断大多数数据库的JDBC驱动。如果你需要指定一个特定的类,你可以使用
spring.datasource.driver-class-name
属性。
为创建
DataSource
池,我们需要可以校验一个有效的Driver
类是可用的,所以在做任何事情之前我们会对其进行检查。换句话说,如果你设置spring.datasource.driver-class-name=com.mysql.jdbc.Driver
,然后该类必须是可加载的。
请查看DataSourceProperties
了解更多的支持的选项。不管真正的实现这些都是标准的选项。也可以通过使用他们的各自的前缀微调特定的实现设置(spring.datasource.hikari.*
,spring.datasource.tomcat.*
,spring.datasource.dbcp2.*
和spring.datasource.oracleucp.*
)。请查看你正使用的连接池实现文档了解更多详情。
例如,如果你使用Tomcat 连接池,你可以定制许多额外的设置,如下示例所示:
spring:
datasource:
tomcat:
max-wait: 10000
max-active: 50
test-on-borrow: true
如果没有链接可用,这个将设置抛出异常之前链接池等待10000ms,限制最大的链接数是50并且在池中借出它之前校验链接。
支持的链接池
Spring Boot使用以下算法用于选择特定实现:
- 我们更喜欢HikariCP它的性能和并发性。如果HikariCP可用,我们总是选择它。
- 否则,如果Tomcat
DataSource
池可用,我们使用它。 - 否则,如果通用的DBCP2可用,我们使用它。
- 如果没有HikariCP,Tomcat和DBCP2可用,如果Oracle UCP可用,我们使用它。
如果你使用
spring-boot-starter-jdbc
或者spring-boot-starter-data-jap
启动器,你自动得到HikariCP
依赖项。
你可以完全绕过该算法并通过设置spring.datasource.type
属性指定链接池来使用。如果在tomcat容器运行应用程序这点非常重要,因为默认提供tomcat-jdbc
。
使用DataSourceBuilder
可以手工配置额外的链接池。如果你定义你自己的DataSource
bean,自动配置不会发生。下面的链接池通过DataSourceBuilder
支持。
- HikariCP
- Tomcat
DataSource
池 - 通用的DBCP2
- Oracel UCP&
OracelDataSource
- Spring Framework的
SimpleDriverDataSource
- H2
JdbcDataSource
- PostgreSQL
PGSimpleDataSource
链接到JNDI数据源
如果你部署你的Spring Boot应用程序到Application Server,你可能想要通过使用你的Application Server内建特性配置和管理你的DataSource并通过使用JNDI访问它。
spring.datasource.jndi-name
属性可以用来当做spring.datasource.url
的替代方案,spring.datasource.username
和spring.datasource.password
属性可以从特定的JNDI位置访问DataSource
。例如,下面的application.properties
中部分展示根据定义的DataSource
如何访问JBoss:
spring:
datasource:
jndi-name: "java:jboss/datasources/customers"
9.1.2. 使用JdbcTemplate
Spring的JdbcTempalte
和NamedParameterJdbcTemplate
类是自动配置的,你可以直接@Autowire
他们到自己的bean,如下示例所示:
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
@Component
public class MyBean {
private final JdbcTemplate jdbcTemplate;
public MyBean(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public void doSomething() {
this.jdbcTemplate ...
}
}
你可以通过使用spring.jdbc.template.*
属性定制一些模板的属性,如下示例所示:
spring:
jdbc:
template:
max-rows: 500
NamedParameterJdbcTemplate
在底层重用相同的JdbcTemplate
实例。如果超过一个JdbcTemplate
被定义并没有主要的候选者存在,NamedParameterJdbcTempalte
不会自动配置。
9.1.3. JPA和Spring Data JPA
Java Persistence API(Java持久化API)是标准的技术,让你“映射”对象到相关的数据库。spring-boot-starter-data-jpa
POM提供一个快速的方式开始。它提供以下关键依赖:
- Hibernate:最受欢迎之一的JPA实现。
- Spring Data JPA:帮助你实现基于JAP仓库。
- Spring ORM:来自Spring Framework核心ORM支持。
这里我们不深入了解JPA和Spring Data的更多细节。你可以遵循来自spring.io的“使用JPA访问数据”指南并阅读Spring Data JPA和Hibernate参考文档。
实体类
传统上,在persistence.xml
文件中指定JPA“实体”类。使用Spring Boot,这个文件不必须的并使用“Entity Scanning”代替。默认情况下,在你的主配置类(使用@EnableAutoConfiguration
或者@SpringBootApplication
注解的一个)下的所有包都会被搜索。
使用@Entity
、@Embeddable
或者@MappedSuperclass
注解的所有类都会考虑。一个典型的实体类类似于下面的示例:
import java.io.Serializable;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
@Entity
public class City implements Serializable {
@Id
@GeneratedValue
private Long id;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private String state;
// ... additional members, often include @OneToMany mappings
protected City() {
// no-args constructor required by JPA spec
// this one is protected since it should not be used directly
}
public City(String name, String state) {
this.name = name;
this.state = state;
}
public String getName() {
return this.name;
}
public String getState() {
return this.state;
}
// ... etc
}
通过使用
@EntityScan
注解你可以定制实体扫描位置。请查看“将@Entity 定义与Spring Configuration分隔”如果做。
Spring Data JPA仓库
Spring Data JPA仓库是接口,你可以定义以访问数据。JPA查询根据你的方法名称自动创建。例如,CityRepository
接口可能声明findAllByState(String state)
方法来查找给定状态的所有城市。
对于更复杂的查询,你可以使用Spring Data的Query
注解来注解你的方法。
Spring Data仓库通常集成自Repository
或者CrudRepository
接口。如果你使用自动配置,将从你的主配置类(使用@EnableAutoConfiguration
或者@SpringBootApplication
注解的一个)搜索仓库。
下面的示例展示了一个典型的Spring Data仓库接口定义:
import org.springframework.boot.docs.data.sql.jpaandspringdata.entityclasses.City;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.Repository;
public interface CityRepository extends Repository<City, Long> {
Page<City> findAll(Pageable pageable);
City findByNameAndStateAllIgnoringCase(String name, String state);
}
Spring Data JPA仓库支持不同的引导指令模式:defalut,defered和lazy。为启用延迟的或者懒惰的引导指令,分别设置spring.data.jpa.repositories.bootstrap-mode
属性为deferred
或者lazy
。当使用defered或者lazy引导指令,自动配置EntityManagerFactoryBuilder
将使用上下文的AsyncTaskExecutor
,若有的话,作为引导执行器。如果存在多余一个,名称为applicationTaskExecutor
的那个将被使用。
当使用deferred或者lazy引导指令,确保在应用程序上下文引导阶段之后延迟对JPA基础设施的任何访问。你可以使用
SmartInitializingSingleton
以调用任何需要JPA基础设施的初始化。对于JPA组件(例如converters)被创建为Spring bean,如果有依赖项,请使用ObjectProvider
来延迟解析。
我们只触碰到Spring Data JPA的皮毛。要了解完整的详情,请查阅Spring Data JPA参考文档。
Spring Data Envers仓库
如果Sprig Data Enver可用,JPA仓库自动配置以支持典型的Enver需求。
为使用Spring Data Enver,确保你的仓库继承于RevisionRepository
如下示例所示:
import org.springframework.boot.docs.data.sql.jpaandspringdata.entityclasses.Country;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.history.RevisionRepository;
public interface CountryRepository extends RevisionRepository<Country, Long, Integer>, Repository<Country, Long> {
Page<Country> findAll(Pageable pageable);
}
为了解更多详情,请查看Spring Data Enver参考文档。
创建和移除JPA数据库
默认情况下,只有使用内嵌数据库(H2,HSQL或者Derby)时JPA数据库是自动创建。你可以通过使用spring.jpa.*
属性明确的配置JPA设置。例如,为创建和移除表,你可以添加以下行到你的application.properties
:
spring:
jpa:
hibernate.ddl-auto: "create-drop"
Hibernate自己对此的内部属性名称是
hibernate.hbm2ddl.auto
。你可以通过使用spring.jpa.properties.*
(将他们添加到实体管理之前剔除前缀)设置它和其他Hibernate本身属性一起。以下行展示对Hibernate设置JPA属性的示例:
spring:
jpa:
properties:
hibernate:
"globally_quoted_identifiers": "true"
在前面的示例中的行传入hibernate.globally_quoted_identifiers
属性true
值到Hibernate实体管理。
默认情况下,DDL执行器(或者校验器)是延迟的直到ApplicationContext
已经开始。也存在spring.jpa.generate-ddl
标志,但是如果Hibernate自动配置是活跃的,它不会被使用,因为ddl-auto
设置是更细粒度的。
视图中打开EntityManager
如果你正在运行一个web应用程序,默认情况下,Spring Boot注册OpenEntityManagerInViewInterceptor
以应用“Open EntityManager In View”格式,允许在web视图中用于懒加载。如果你不希望这样的行为,你应该在application.properties
中的spring.jpa.open-in-view
设置为false
。
9.1.4. Spring Data JDBC
Spring Boot包含对JDBC支持的仓库并将对在CrudRepository
上的方法自动生成SQL。对于更高级的查询,提供@Query
注解。
当必须的依赖在类路径中时,Spring Boot将自动配置Spring Data的JDBC仓库。使用在spring-boot-starter-data-jdbc
上的单个依赖将他们加入的项目。如果必须,你可以通过添加@EnableJdbcRepositories
注解或者JdbcConfiguration
子类到你的应用程序以控制Spring Data JDBC的配置。
为了解Spring Data JDBC完整详情,请查看参考文档。
9.1.5.使用H2web 控制台
H2数据库提供基于浏览器的控制台,Spring Boot可以自动配置给你。当以下条件满足时自动配置控制台:
- 你正在开发一个基于Servlet的web应用程序。
com.h2database:h2
在类路径中。- 你正在使用Spring Boot开发者工具。
如果你没有使用开发者工具但是仍想使用H2控制台, 你可以使用
true
值配置spring.h2.console.enabled
属性。
H2控制台只打算用于开发期间使用,所以你应该小心,以确保生产环境
spring.h2.console.enabled
没有设置为true
。
更改H2控制台路径
默认情况下,控制台是使用在/h2-console
。你可以通过使用spring.h2.console.path
属性定制控制台路径。
在保障安全的应用程序中访问H2控制台
H2控制台使用框架并只打算用于开发环境,不支持CSRF保护措施。如果你的应用程序使用Spring Security,你需要配置它为:
- 针对控制台请求禁用CSRF保护。
- 在来自控制台的响应上设置头
X-Frame-Options
为SAMEORIGIN
。 在CSRF和头X-Frame-Options
更多信息可以在Spring Security参考指南中发现。
在简单的设置中,像以下的SecurityFilterChain
可以使用:
import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Profile("dev")
@Configuration(proxyBeanMethods = false)
public class DevProfileSecurityConfiguration {
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
SecurityFilterChain h2ConsoleSecurityFilterChain(HttpSecurity http) throws Exception {
http.requestMatcher(PathRequest.toH2Console());
http.authorizeRequests(yourCustomAuthorization());
http.csrf((csrf) -> csrf.disable());
http.headers((headers) -> headers.frameOptions().sameOrigin());
return http.build();
}
}
H2控制台只打算在开发期间使用。在生产环境,对站点禁用CSRF保护或者允许框架可能创建严重安全风险。
当控制台路径已经被定制,
PathRequest.toH2Console()
也可以返回正确的请求匹配器。
9.1.6. 使用jOOQ
jOOQ Object Oriented Querying(jOOQ)是来自Data Geekery非常受欢迎的产品,它根据你的数据库生成Java代码并允许你通过它的流式API构建类型安全的SQL查询。商业和开源版本都可以被Spring Boot使用。
代码生成
为了使用jOOQ类型安全查询,你需要从你的数据库模式生成Java类。你可以遵循jOOQ用户手册说明。如果你使用jooq-codegen-maven
插件并且你也可以spring-boot-start-parent
父POM,你可以安全地省去插件的<version>
标签。你也可以使用预定义的Spring Boot版本变量(比如h2.version
)以声明插件的数据库依赖。以下列表展示了一个示例:
<plugin>
<groupId>org.jooq</groupId>
<artifactId>jooq-codegen-maven</artifactId>
<executions>
...
</executions>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>${h2.version}</version>
</dependency>
</dependencies>
<configuration>
<jdbc>
<driver>org.h2.Driver</driver>
<url>jdbc:h2:~/yourdatabase</url>
</jdbc>
<generator>
...
</generator>
</configuration>
</plugin>
使用DSLContext
jOOQ提供的流式API通过org.jooq.DSLContext
接口发起的。Spring Boot自动配置DSLContext
作为Spring bean并将它与应用程序DataSource
连接。为使用DSLContext
,你可以注入它,如下示例所示:
import java.util.GregorianCalendar;
import java.util.List;
import org.jooq.DSLContext;
import org.springframework.stereotype.Component;
import static org.springframework.boot.docs.data.sql.jooq.dslcontext.Tables.AUTHOR;
@Component
public class MyBean {
private final DSLContext create;
public MyBean(DSLContext dslContext) {
this.create = dslContext;
}
}
jOOQ手册倾向于使用名为
create
变量名称来保存DSLContext
。
你仍可以使用DSLContext
来构造你的查询,如下示例所示:
public List<GregorianCalendar> authorsBornAfter1980() {
return this.create.selectFrom(AUTHOR)
.where(AUTHOR.DATE_OF_BIRTH.greaterThan(new GregorianCalendar(1980, 0, 1)))
.fetch(AUTHOR.DATE_OF_BIRTH);
jOOQ SQL Dialect
除了spring.jooq.sql-dialect
属性已经配置,Spring Boot决定SQL方言来用于数据源的使用。如果Spring Boot无法检测方言,他使用DEFAULT
。
Spring Boot只能通过开源的jOOQ版本自动配置支持的方言。
定制jOOQ
通过定义自己的DefaultConfigurationCustomizer
bean可以实现更高级的定制,该bean将在创建org.jooq.Configuration``@Bean
之前被调用。
9.1.7. 使用R2DBC
Reactive Relational Database Connectivity(R2DBC)项目采用响应式程序API关系型数据库。R2DBC的io.r2dbc.spi.Connection
提供标准的工作于非阻塞数据库链接的方法。通过使用ConnectionFactory
提供链接,类似于使用jdbc的DataSource
。
通过在spring.r2dbc.*
外部配置属性控制ConnectionFactory
配置。例如,你可以在application.properties
中声明以下部分:
spring:
r2dbc:
url: "r2dbc:postgresql://localhost/test"
username: "dbuser"
password: "dbpass"
你不需要指定驱动类名称,因为Spring Boot从R2DBC的链接工厂发现获得。
至少应该提供url,URL中特定的信息优先级高于个别属性,那是
name
,username
,password
和池化选项。
“如何做”章节包含在如何初始化数据库部分。
为了通过ConnectionFactory
定制创建的链接,就是说,在主要的数据库配置中设置你不想(或者不能)配置特定的参数,你可以使用ConnectionFactoryOptionsBuilderCustomizer``@Bean
。下面的示例展示了如何手工重写数据库端口,而其余的选项从应用程序配置中获取:
import io.r2dbc.spi.ConnectionFactoryOptions;
import org.springframework.boot.autoconfigure.r2dbc.ConnectionFactoryOptionsBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MyR2dbcConfiguration {
@Bean
public ConnectionFactoryOptionsBuilderCustomizer connectionFactoryPortCustomizer() {
return (builder) -> builder.option(ConnectionFactoryOptions.PORT, 5432);
}
}
下面的示例展示如何设置一些PostgreSQL连接选项:
import java.util.HashMap; import java.util.Map; import io.r2dbc.postgresql.PostgresqlConnectionFactoryProvider; import org.springframework.boot.autoconfigure.r2dbc.ConnectionFactoryOptionsBuilderCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration(proxyBeanMethods = false) public class MyPostgresR2dbcConfiguration { @Bean public ConnectionFactoryOptionsBuilderCustomizer postgresCustomizer() { Map<String, String> options = new HashMap<>(); options.put("lock_timeout", "30s"); options.put("statement_timeout", "60s"); return (builder) -> builder.option