Spring Boot与SQL数据库的集成

https://docs.spring.io/spring-boot/docs/3.2.0/reference/htmlsingle/#data.sql

Spring Framework 为与 SQL 数据库一起工作提供了广泛的支持,从使用 JdbcClientJdbcTemplate 进行直接 JDBC 访问,到完整的“对象关系映射”(Object-Relational Mapping,简称 ORM)技术,如 Hibernate。Spring Data 提供了额外的功能层:直接从接口创建 Repository 实现,并使用约定从方法名生成查询。

配置数据源(Configure a DataSource)

Java 的 javax.sql.DataSource 接口为与数据库连接提供了标准的工作方式。传统上,DataSource 使用 URL 和一些凭据(credentials )来建立数据库连接。

嵌入式数据库支持(Embedded Database Support)

使用内存中的嵌入式数据库来开发应用程序通常很方便。显然,内存中的数据库不提供持久存储。你需要在应用程序启动时填充数据库,并准备在应用程序结束时丢弃数据。

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_EXIT=FALSE 来禁用自动关闭。如果使用 HSQLDB,应确保未使用 shutdown=true。禁用数据库的自动关闭可以让 Spring Boot 控制何时关闭数据库,从而确保在不再需要访问数据库时关闭数据库。

连接到生产数据库

通过使用连接池 DataSource,也可以自动配置生产数据库连接。

数据源配置(DataSource Configuration)

数据源配置由 spring.datasource.* 中的外部配置属性进行控制。例如,你可能在 application.properties 中声明以下部分:

spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=dbuser
spring.datasource.password=dbpass

注意:应该至少通过设置 spring.datasource.url 属性来指定 URL。否则,Spring Boot 将尝试自动配置嵌入式数据库。

提示:Spring Boot 可以从 URL 中推断出大多数数据库的 JDBC 驱动程序类。如果你需要指定特定的类,可以使用 spring.datasource.driver-class-name 属性。

注意:为了创建连接池数据源,需要能够验证有效的 Driver 类是否可用,因此在执行任何操作之前都会检查这一点。换句话说,如果你设置了 spring.datasource.driver-class-name=com.mysql.jdbc.Driver,那么该类必须可加载。

还可以使用各自的前缀(spring.datasource.hikari.*spring.datasource.tomcat.*spring.datasource.dbcp2.*spring.datasource.oracleucp.*)来微调特定实现的设置。

例如,如果使用 Tomcat 连接池,可以按照以下示例自定义许多其它设置:

spring.datasource.tomcat.max-wait=10000
spring.datasource.tomcat.max-active=50
spring.datasource.tomcat.test-on-borrow=true

这将设置连接池在无法获取连接时等待 10000 毫秒后抛出异常,限制最大连接数为 50,并在从连接池中借用连接之前验证连接。

支持的连接池

Spring Boot 使用以下算法来选择特定的实现:

  • 优先选择 HikariCP,因为它在性能和并发方面表现出色。如果 HikariCP 可用,我们总是选择它。
  • 否则,如果 Tomcat 连接池 DataSource 可用,我们会使用它。
  • 否则,如果 Commons DBCP2 可用,我们会使用它。
  • 如果 HikariCP、Tomcat 和 DBCP2 都不可用,而 Oracle UCP 可用,我们将使用它。

注意:如果你使用 spring-boot-starter-jdbcspring-boot-starter-data-jpa “启动器”,将自动获得对 HikariCP 的依赖。

可以通过设置 spring.datasource.type 属性来完全绕过该算法,并指定要使用的连接池。如果你的应用程序在 Tomcat 容器中运行,这一点尤为重要,因为默认情况下会提供 tomcat-jdbc

始终可以使用 DataSourceBuilder 手动配置其它连接池。如果你定义了自己的 DataSource bean,则不会发生自动配置。DataSourceBuilder 支持以下连接池:

  • HikariCP
  • Tomcat 连接池 DataSource
  • Commons DBCP2
  • Oracle UCP 和 OracleDataSource
  • Spring Framework 的 SimpleDriverDataSource
  • H2 JdbcDataSource
  • PostgreSQL PGSimpleDataSource
  • C3P0

连接到 JNDI 数据源

如果将 Spring Boot 应用程序部署到应用服务器,你可能希望使用应用服务器的内置功能配置和管理 DataSource,并通过 JNDI 访问它。

spring.datasource.jndi-name 属性可以作为 spring.datasource.urlspring.datasource.usernamespring.datasource.password 属性的替代方案,用于从特定的 JNDI 位置访问 DataSource。例如,application.properties 中的以下部分展示了如何访问 JBoss AS 定义的 DataSource

spring.datasource.jndi-name=java:jboss/datasources/customers

使用 JdbcTemplate

Spring 的 JdbcTemplateNamedParameterJdbcTemplate 类是自动配置的,你可以直接将它们 @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 且不存在首选的候选项,则不会自动配置 NamedParameterJdbcTemplate

使用JdbcClient

Spring 的 JdbcClient 会根据 NamedParameterJdbcTemplate 的存在情况进行自动配置。你也可以像下面这个例子一样,直接将它注入到你自己的 bean 中:

import org.springframework.jdbc.core.simple.JdbcClient;
import org.springframework.stereotype.Component;

@Component
public class MyBean {

    private final JdbcClient jdbcClient;

    public MyBean(JdbcClient jdbcClient) {
        this.jdbcClient = jdbcClient;
    }

    public void doSomething() {
        this.jdbcClient ...
    }

}

如果你依赖自动配置来创建底层的 JdbcTemplate,那么使用 spring.jdbc.template.* 属性进行的任何自定义也会在客户端中考虑。

JPA 和 Spring Data JPA

Java Persistence API 是一种标准技术,它允许你将对象“映射”到关系数据库。spring-boot-starter-data-jpa POM 提供了一种快速入门的方法。它提供了以下关键依赖项:

  • Hibernate:最流行的 JPA 实现之一。
  • Spring Data JPA:帮助你实现基于 JPA 的存储库。
  • Spring ORM:Spring 框架的核心 ORM 支持。

实体类(Entity Classes)

传统上,JPA 的“实体”(Entity)类是在一个 persistence.xml 文件中指定的。使用 Spring Boot 时,这个文件是不必要的,而是使用“实体扫描”(Entity Scanning)。默认情况下,会扫描自动配置的包。

任何带有 @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 注解来自定义实体扫描位置。

Spring Data JPA 存储库

Spring Data JPA 存储库是你可以定义的接口,用于访问数据。JPA 查询会根据方法名自动创建。例如,一个 CityRepository 接口可能会声明一个 findAllByState(String state) 方法,以查找给定州的所有城市。

对于更复杂的查询,可以使用 Spring Data 的 @Query 注解来标注你的方法。

Spring Data 存储库通常扩展自 RepositoryCrudRepository 接口。如果使用自动配置,则会在自动配置的包中搜索存储库。

提示:可以使用 @EnableJpaRepositories 来自定义查找存储库的位置。

以下示例显示了典型的 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 存储库支持三种不同的引导模式:defaultdeferredlazy。要启用deferredlazy引导,请将 spring.data.jpa.repositories.bootstrap-mode 属性分别设置为 deferredlazy。当使用deferredlazy引导时,自动配置的 EntityManagerFactoryBuilder 将使用上下文的 AsyncTaskExecutor(如果有)作为引导执行器。如果存在多个,则使用名为 applicationTaskExecutor 的执行器。

注意:当使用deferredlazy引导时,请确保在应用程序上下文引导阶段之后延迟对 JPA 基础设施的任何访问。你可以使用 SmartInitializingSingleton 来调用需要 JPA 基础设施的任何初始化。对于作为 Spring bean 创建的 JPA 组件(如转换器),如果有任何依赖项,请使用 ObjectProvider 延迟依赖项的解析。

Spring Data Envers 存储库

如果 Spring Data Envers 可用,JPA 存储库将自动配置以支持典型的 Envers 查询。

要使用 Spring Data Envers,请确保你的存储库扩展自 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);

}

创建和删除 JPA 数据库

默认情况下,JPA 数据库仅在使用嵌入式数据库(如 H2、HSQL 或 Derby)时才会自动创建。可以通过使用 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(Open EntityManager in View)

如果你正在运行一个 Web 应用程序,Spring Boot 默认会注册 OpenEntityManagerInViewInterceptor,以便应用“Open EntityManager in View”模式,从而允许在 Web 视图中进行延迟加载。如果你不希望这种行为,你应该在你的 application.properties 文件中将 spring.jpa.open-in-view 设置为 false

Spring Data JDBC

Spring Data 包括了针对 JDBC 的 Repository 支持,并且可以自动为 CrudRepository 中的方法生成 SQL。对于更高级的查询,提供了 @Query 注解。

当类路径中存在必要的依赖时,Spring Boot 将自动配置 Spring Data 的 JDBC repositories。可以通过在项目中添加对 spring-boot-starter-data-jdbc 的单个依赖来添加这些 repositories。如果需要,可以通过将 @EnableJdbcRepositories 注解或 AbstractJdbcConfiguration 子类添加到应用程序中来控制 Spring Data JDBC 的配置。

使用 H2 的 Web 控制台

H2 数据库提供了一个基于浏览器的控制台,Spring Boot 可以为你自动配置它。当满足以下条件时,控制台将自动配置:

  • 你正在开发一个基于 Servlet 的 Web 应用程序。
  • com.h2database:h2 在类路径中。
  • 你正在使用 Spring Boot 的开发工具。

提示:如果你没有使用 Spring Boot 的开发工具,但仍然希望利用 H2 的控制台,可以将 spring.h2.console.enabled 属性配置为 true

注意:H2 控制台仅用于开发过程中的使用,因此应该确保在生产环境中不要将 spring.h2.console.enabled 设置为 true

更改 H2 控制台的路径

默认情况下,控制台位于 /h2-console。你可以使用 spring.h2.console.path 属性来自定义控制台的路径。

在受保护的应用程序中访问 H2 控制台

H2 控制台使用frame,并且由于它仅用于开发,因此没有实施 CSRF 保护措施。如果你的应用程序使用 Spring Security,需要配置它以:

  • 对控制台请求禁用 CSRF 保护,
  • 在来自控制台的响应上设置标头 X-Frame-OptionsSAMEORIGIN

在简单的设置中,可以使用类似以下的 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.securityMatcher(PathRequest.toH2Console());
        http.authorizeHttpRequests(yourCustomAuthorization());
        http.csrf((csrf) -> csrf.disable());
        http.headers((headers) -> headers.frameOptions((frame) -> frame.sameOrigin()));
        return http.build();
    }


}

注意:H2 控制台仅用于开发过程中的使用。在生产环境中,禁用 CSRF 保护或允许网站使用frame可能会带来严重的安全风险。

提示:当控制台的路径被自定义时,PathRequest.toH2Console() 也会返回正确的请求匹配器。

使用 jOOQ

jOOQ(Java Object Oriented Querying,即面向对象的Java查询)是Data Geekery公司的一个流行产品,它可以根据你的数据库生成Java代码,并通过其流畅的API让构建类型安全的SQL查询。无论是商业版还是开源版,都可以与Spring Boot一起使用。

代码生成

要使用 jOOQ 的类型安全查询,你需要根据数据库模式生成 Java 类。如果你使用 jooq-codegen-maven 插件,并且同时使用 spring-boot-starter-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 支持的 SQL 方言。

定制 jOOQ

通过定义你自己的 DefaultConfigurationCustomizer bean,可以实现更高级的定制,该 bean 将在创建 org.jooq.Configuration @Bean 之前被调用。这将优先于自动配置所应用的任何内容。

如果希望完全控制jOOQ配置,还可以创建自己的org.jooq.Configuration @Bean

使用R2DBC

Reactive关系型数据库连接(R2DBC)项目为关系型数据库带来了响应式编程API。R2DBC的io.r2dbc.spi.Connection提供了与非阻塞数据库连接交互的标准方法。通过使用ConnectionFactory提供连接,这与使用jdbc的DataSource类似。

ConnectionFactory的配置由spring.r2dbc.*中的外部配置属性进行控制。例如,你可能会在application.properties中声明以下部分:

spring.r2dbc.url=r2dbc:postgresql://localhost/test
spring.r2dbc.username=dbuser
spring.r2dbc.password=dbpass

提示:你不需要指定驱动程序类名,因为Spring Boot会从R2DBC的ConnectionFactory发现中获取驱动程序。

注意:至少应该提供url。在URL中指定的信息将优先于各个属性,即名称、用户名、密码和池选项。

要自定义由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(PostgresqlConnectionFactoryProvider.OPTIONS, options);
    }

}

ConnectionFactory bean可用时,常规的JDBC DataSource自动配置将退出。如果想保留JDBC DataSource自动配置,并且愿意承担在响应式应用程序中使用阻塞JDBC API的风险,请在应用程序中的@Configuration类上添加@Import(DataSourceAutoConfiguration.class)以重新启用它。

嵌入式数据库支持

与JDBC支持类似,Spring Boot可以自动为响应式使用配置嵌入式数据库。你无需提供任何连接URL。只需要包含对要使用的嵌入式数据库的构建依赖,如下所示:

<dependency>
    <groupId>io.r2dbc</groupId>
    <artifactId>r2dbc-h2</artifactId>
    <scope>runtime</scope>
</dependency>

注意:如果在测试中使用此功能,你可能会注意到,无论使用多少个应用程序上下文,整个测试套件都会重复使用相同的数据库。如果想确保每个上下文都有一个单独的嵌入式数据库,应该将spring.r2dbc.generate-unique-name设置为true

使用DatabaseClient

将自动配置DatabaseClient bean,可以直接将其@Autowired到自己的bean中,如下所示:

import java.util.Map;

import reactor.core.publisher.Flux;

import org.springframework.r2dbc.core.DatabaseClient;
import org.springframework.stereotype.Component;

@Component
public class MyBean {

    private final DatabaseClient databaseClient;

    public MyBean(DatabaseClient databaseClient) {
        this.databaseClient = databaseClient;
    }

    public Flux<Map<String, Object>> someMethod() {
        return this.databaseClient.sql("select * from user").fetch().all();
    }

}

Spring Data R2DBC存储库

Spring Data R2DBC存储库是你可以定义的用于访问数据的接口。查询是根据你的方法名自动创建的。例如,CityRepository接口可能会声明一个findAllByState(String state)方法来查找给定州的所有城市。

对于更复杂的查询,可以使用Spring Data的Query注解来注解您的方法。

Spring Data存储库通常从RepositoryCrudRepository接口扩展。如果使用自动配置,将搜索自动配置包以查找存储库。

以下示例显示了典型的Spring Data存储库接口定义:

import reactor.core.publisher.Mono;

import org.springframework.data.repository.Repository;

public interface CityRepository extends Repository<City, Long> {

    Mono<City> findByNameAndStateAllIgnoringCase(String name, String state);

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值