原创作品,可以转载,但是请标注出处地址:
因为需要使用到这方面内容,所有对这一部分进行了翻译。
29 使用SQL数据源
SpringBoot为SQL数据源提供了广泛支持,从直接使用JdbcTemplate的JDBC访问到完整的ORM(关系映射型)框架(例如Hibernate)。String Data提供了一份经典的功能级别,直接从接口创建存储库实现,并使用约定从方法名生成查询。
29.1 配置数据源
java的javax.sql.DataSource接口提供了一个经典的方法来使用SQL数据源。一般来说,一个数据源需要一个带有验证的URL来创建一个数据库连接。
29.1.1 嵌入式数据库支持
使用嵌入内存的数据源来开发一个应用很方便。显然,内存嵌入式数据源并不支持持久存储,因此你必须在应用开始的时候进行数据填充,并在应用停止时有丢失数据的准备。
SpringBoot可以自动配置需要嵌入的数据源:H2、HSQL和Derby等。这并不需要你提供链接URL,仅仅只需要添加对应的数据源依赖即可。
注意:如果你在你的测试中使用了嵌入式数据库,为防止你的整个测试套件中的所有应用上下文会共用同一个数据源,需要做以下设置:spring.datasource.generate-unique-name=true
例如:经典的POM依赖为
1 <dependency> 2 <groupId>org.springframework.boot</groupId> 3 <artifactId>spring-boot-starter-data-jpa</artifactId> 4 </dependency> 5 <dependency> 6 <groupId>org.hsqldb</groupId> 7 <artifactId>hsqldb</artifactId> 8 <scope>runtime</scope> 9 </dependency>
注意:为了实现嵌入式数据源的自动配置,你需要添加Spring-jdbc的依赖,在这个实例中Spring-jdbc的依赖被spring-boot-starter-data-jpa所导入(言外之意,你无须关心这一点,因为已经被自动导入了)。
注意:如果因为一些原因,你需要为嵌入式数据源配置URL,一定要记得将该数据源的自动shutdown功能关闭。如果你使用的是H2数据源,你需要配置DB_CLOSE_ON_EXIT=FALSE;如果你使用的是HSQLDB数据源,你需要确保shutdown=true失效。禁用嵌入式数据源的自动shutdown功能之后当数据源被关闭时转由SpringBoot来进行控制,这样一来不必再确保数据源只被一次访问。
29.1.2 连接生产数据库
生产的数据库也可以进行自动配置,只需要使用一个数据源池。下面是一些实现方式:
(1)我们一般热衷于HikariCP ,因为它的高性能和高并发性,一般情况下我们都会选择它。
(2)另外,如果tomcat的数据源池可用,我们也可以选择使用它。
(3)如果前两个都不能使用,而DBCP2可用的话,我们选择它。
如果是添加了spring-boot-starter-jdbc或者是spring-boot-starter-data-jpa依赖starter的话,它们会自动添加对HikariCP的依赖。
注意:你完全可以忽视其实现算法,而使用spring.datasource.type属性来指定连接池。当你的应用正在使用默认的tomcat-jdbc运行在Tomcat容器时,尤其要注意这一点。
注意:额外的连接池可以通过手动进行配置,如果你自定义了DataSource的Bean实例,那么自动配置将不会生效。
数据源的配置由外部属性配置文件:spring.datasource.*来配置。例如:你可以在application.properties文件中申明如下配置:
spring.datasource.url=jdbc:mysql://localhost/test spring.datasource.username=dbuser spring.datasource.password=dbpass spring.datasource.driver-class-name=com.mysql.jdbc.Driver
注意:你至少要使用spring.datasource.url属性来指定url链接,或者由SpringBoot自动配置一个内嵌数据源(内嵌数据源不需要指定url)
注意:你一般不需要指定driver-class-name,因为SpringBoot可以从url之中自动识别出大多数数据库的驱动类名。
注意:要想得到一个数据源池,我们需要确保对应的驱动类是有效的,这项工作需要最先进行验证。例如:如果你设置spring.datasource.driver-class-name=com.mysql.jdbc.Driver的话,那么这个驱动类必须是可被加载的。
参照DataSourceProperties类可查看更多配置项。不论实际上如何实现,这些都是一些标准的配置项。还可以通过一些带有前缀的指定配置进行微调(例如:spring.datasource.hikari.*、spring.datasource.tomcat.*、spring.datasource.dbcp2.*等)。更多信息请参考你使用的连接池的实现文档。
例如:如果你使用的是Tomcat连接池,你需要如下额外配置内容:
# Number of ms to wait before throwing an exception if no connection is available.(如果连接无效,等待多久抛出异常) spring.datasource.tomcat.max-wait=10000 # Maximum number of active connections that can be allocated from this pool at the same time.(连接池中可同时分配的最大连接数量) spring.datasource.tomcat.max-active=50 # Validate the connection before borrowing it from the pool.(获取池中连接前是否进行连接验证) spring.datasource.tomcat.test-on-borrow=true
29.1.3 连接一个JNDI数据源
如果你正在将你的应用部署到一个应用服务器中,你肯能会想要使用JDNI和服务器特性来配置和管理你的数据源。
spring.datasource.jndi-name属性可被用来替换spring.datasource.url、spring.datasource.username和spring.datasource.password三个属性来达成定位连接一个JNDI数据源的目的。例如:在application.properties文件中进如下配置可以显示如何连接JBoss中定义的数据源。
spring.datasource.jndi-name=java:jboss/datasources/customers
29.2使用Jdbc Template(Jdbc模板)
Spring中的JdbcTemplate类和NamedParameterJdbcTemplate类可被自动配置,你可以使用@Autowired注解来将其自动配置到自定义的Bean中。
1 import org.springframework.beans.factory.annotation.Autowired; 2 import org.springframework.jdbc.core.JdbcTemplate; 3 import org.springframework.stereotype.Component; 4 5 @Component 6 public class MyBean { 7 8 private final JdbcTemplate jdbcTemplate; 9 10 @Autowired 11 public MyBean(JdbcTemplate jdbcTemplate) { 12 this.jdbcTemplate = jdbcTemplate; 13 } 14 15 // ... 16 17 }
你可以使用spring.jdbc.template.*系列属性自定义一些Template的属性值,例如:
spring.jdbc.template.max-rows=500
注意:NamedParameterJdbcTemplate在幕后需要使用同一个JdbcTemplate实例,当你定义了超多一个JdbcTemplate实例并且未指定主要 候选对象的情况下,NamedParameterJdbcTemplate将不会被自动配置。
29.3 JPA和Spring Data
Java持久化API是一个很经典的技术,它可以实现对象到数据源的映射。spring-boot-starter-data-jpa的POM提供了一种快速达成的方式,它提供了一下主要依赖:
Hibernate——最流行的JPA实现之一
Spring Data JPA——简化基于JPA的数据源实现
Spring ORMs——来自Spring框架的核心ORM支持
注意:这里我们不会深入讨论JPA和Spring Data的详情。你可以通过阅读对应的引用文档来进行详细了解。
29.3.1 Entity Classes(实体类)
一般情况下,JPA实体类在persistence.xml文件中指定。在SpringBoot中不再需要这个文件,而是使用Entity Scanner(实体类扫描器)来完成同样的功能。默认情况下,所有位于启动配置类(被注解@EnableAutoConfiguration或者@SpringBootApplication标注的类)所在目录之下的包都将会被扫描。
任何一个使用注解@Entity, @Embeddable或者@MappedSuperclass标注的类都将会被扫描到。下面展示一个经典的实体类样板:
1 package com.example.myapp.domain; 2 3 import java.io.Serializable; 4 import javax.persistence.*; 5 6 @Entity 7 public class City implements Serializable { 8 9 @Id 10 @GeneratedValue 11 private Long id; 12 13 @Column(nullable = false) 14 private String name; 15 16 @Column(nullable = false) 17 private String state; 18 19 // ... additional members, often include @OneToMany mappings 20 21 protected City() { 22 // no-args constructor required by JPA spec 23 // this one is protected since it shouldn‘t be used directly 24 } 25 26 public City(String name, String state) { 27 this.name = name; 28 this.country = country; 29 } 30 31 public String getName() { 32 return this.name; 33 } 34 35 public String getState() { 36 return this.state; 37 } 38 39 // ... etc 40 41 }
注意:你可以使用@EntityScan注解来自定义实体扫描器的扫描路径(标注于启动类),详见第78.4节内容
29.3.2 Spring Data JPA Repository
Spring Data JPA Repository是可以使你定义连接数据的接口。JPA查询可以通过你自定义的方法名自动创建。例如:一个CityRspository接口可以申明一个findAllByState(String state)方法来根据指定state获取所有城市。
你可以使用Spring Data的查询注解来定义更加复杂的查询。
Spring Data Repository接口通常继承自Repository或者CrudRepository接口,如果你正在使用自动配置功能,Respository将会在启动配置类所在包及其下级包内被扫描到。
下面是一个经典的Spring Data Repository:
1 package com.example.myapp.domain; 2 3 import org.springframework.data.domain.*; 4 import org.springframework.data.repository.*; 5 6 public interface CityRepository extends Repository<City, Long> { 7 8 Page<City> findAll(Pageable pageable); 9 10 City findByNameAndCountryAllIgnoringCase(String name, String country); 11 12 }
注意:我们仅仅了解了Spring Data JPA的基础知识,详情查看引导文档(http://projects.spring.io/spring-data-jpa/)
29.3.3 创建和丢弃JPA数据源
默认情况下,当你使用了一个嵌入式数据库(H2、 HSQL、Derby)时,JPA Repository将会被自动创建。你可以使用spring.jpa.*来进行JPA的相关配置。例如:为了创建和删除表,你可以在application.properties文件中添加如下配置:
spring.jpa.hibernate.ddl-auto=create-drop
注意:Hibernate针对该配置的内部属性为hibernate.hbm2ddl.auto。你可以使用spring.jpa.properties.*的方式协同Hibernate的其他属性进行一起配置,这个前缀会在添加到实体管理器之前被去除,例如:
spring.jpa.properties.hibernate.globally_quoted_identifiers=true
通过hibernate.globally_quoted_identifiers来到达Hibernate实体管理器。
默认情况下,DDL执行或者验证会推迟到ApplicationContext启动后执行。还有一个spring.jpa.generate-ddl标志,但如果Hibernate的 autoconfig是活动状态的,则不会使用它,因为ddl-auto设置更具细粒度。
29.3.4 Open EntityManager in View
如果你正在运行一个web应用,SpringBoot将默认注册OpenEntityManagerInViewInterceptor来服务于"Open EntityManager in View"模式,例如,它将会开启懒加载功能(在web视图中)。如果你不想要这种功能,你可以通过在application.properties中配置spring.jpa.open-in-view=false来达成。
29.3.5 使用H2的web控制台
H2数据库提供了一个基于浏览器的web控制台,这个可以有SpringBoot自动配置加载。控制台在达成以下条件后将会被自动配置加载:
你正在开发的是一个web应用
com.h2database:h2在类路径下
你正在使用SpringBoot开发者工具(spring-boot-devtools)
注意:如果你没有正在使用SpringBoot开发者工具,但却想要使用H2的web控制台功能,你可以配置spring.h2.console.enabled=true来达成目的。该功能仅用于开发期间,在应用上生产环境之时,一定要将spring.h2.console.enabled=true配置去掉。
29.4.1 改变H2控制台的路径
默认情况下H2控制台只在/h2-console路径下有效,你可以通过属性spring.h2.console.path进行自定义H2控制台的路径。
29.4.2 安全化H2控制台
当Spring Security位于类路径下,并且基本认证功能处于激活状态时,H2控制台将会自动是使用基本认证功能。下面这些属性可被用来自定义安全配置:
security.user.role
security.basic.authorize-mode
security.basic.enabled
29.5 使用jOOQ
jOOQ(Java Object Oriented Querying:Java面向对象查询)是一个流行的代码生成器(根据数据源生成java代码),并通过其API创建类型安全的SQL查询,其商业版和开源版均被SpringBoot所支持。
29.5.1 代码生成
为了使用jOOQ的类型安全的查询,你需要根据数据源结构生成Java类。你可以依据 jOOQ user manual(http://www.jooq.org/doc/3.6/manual-single-page/#jooq-in-7-steps-step3)的介绍。如果你正在使用jooq-codegen-maven插件(并且同是在使用spring-boot-starter-parent),你可以省略拆件的<version>标签,你也可以使用SpringBoot来定义版本变量(例如:h2.version)来声明插件的数据源依赖,下面是一个实例:
1 <plugin> 2 <groupId>org.jooq</groupId> 3 <artifactId>jooq-codegen-maven</artifactId> 4 <executions> 5 ... 6 </executions> 7 <dependencies> 8 <dependency> 9 <groupId>com.h2database</groupId> 10 <artifactId>h2</artifactId> 11 <version>${h2.version}</version> 12 </dependency> 13 </dependencies> 14 <configuration> 15 <jdbc> 16 <driver>org.h2.Driver</driver> 17 <url>jdbc:h2:~/yourdatabase</url> 18 </jdbc> 19 <generator> 20 ... 21 </generator> 22 </configuration> 23 </plugin>
29.5.2 使用DSLContext
jOOQ所提供的API是通过org.jooq.DSLContext接口开启的。SpringBoot将会自定义一个DSLContext(DSL上下文)Bean和你的应用的数据源连接起来,为了使用DSLContext,你需要@Autowired:
1 @Component 2 public class JooqExample implements CommandLineRunner { 3 4 private final DSLContext create; 5 6 @Autowired 7 public JooqExample(DSLContext dslContext) { 8 this.create = dslContext; 9 } 10 11 }
注意:jOOQ手册倾向于使用名为create的变量来持有DSLContext,因此我们可以使用如下代码完成同样的功能:
1 public List<GregorianCalendar> authorsBornAfter1980() { 2 return this.create.selectFrom(AUTHOR) 3 .where(AUTHOR.DATE_OF_BIRTH.greaterThan(new GregorianCalendar(1980, 0, 1))) 4 .fetch(AUTHOR.DATE_OF_BIRTH); 5 }
然后你可以使用DSLContext来构建你的查询。
29.5.3 jOOQ SQL语言
SpringBoot可以决定是否使用jOOQ SQL语言,除非spring.jooq.sql-dialect属性被设置,如果该语言无法被执行,则该属性一定被设置为DEFAULT了。
注意:SpringBoot仅仅支持开源版本的jOOQ SQL语言。
29.5.4 自定义jOOQ
通过自定义@Bean(这些Bean将会在jOOQ的Configuration被创建时使用),你可以获得更多的高级定制功能。你可以使用如下jOOQ类型来定义Bean:
ConnectionProvider
TransactionProvider
RecordMapperProvider
RecordListenerProvider
ExecuteListenerProvider
VisitListenerProvider
如果你想要完美的控制jOOQ的各项配置,你需要自定义org.jooq.Configuration配置类(使用@Bean注解)。