Spring Boot 2系列(四): Spring Data Jpa 集成详解和使用
JPA 即 Java Persistence API,是一个基于O/R映射的标准规范。Hibernate 是该规范的实现,Hibernate 是非常流行的对象关系映射框架(ORM
),是SSH
组合开发框架重要组件。
Spring Data JPA 是 Spring Data 的一个子项目,它提供了基于 JPA 的 Repository 减少了大量的数据访问操作的代码。JpaRepository 接口提供了通用的数据访问方法。
始终建议看官方文档:
Spring Boot 2.0.3 > Use Spring Data Repositories
Spring Data JPA - Reference Documentation
JDBC自动配置
Spring Boot 自动配置依赖于 spring-boot-autoconfigure-2.0.1.RELEASE.jar 包,默认支持的自动配置都在org.springframework.boot.autoconfigure路径下。
自动配置
spring-boot-starter-data-jpa 依赖于spring-boot-starter-jdbc, Spring Boot 为 JDBC 做了些自动配置,并自动开启了注解事务的支持。JDBC 自动配置源码在 org.springframework.boot.autoconfigure.jdbc 路径下。
- 自动配置文件:DataSourceAutoConfiguration.class自动配置类会被注册为
Bean
并执行里面的配置。 - 配置属性文件:DataSourceProperties.class,可以通过
spring.datasource
为前辍的属性自动配置数据源。 - 自动开启注解事务文件,DataSourceTransactionManagerAutoConfiguration.class自动配置事务管理,并配置了一个
JdbcTemplate
。
表结构初始化
Spring Boot 提供了初始化数据的功能:放置在类路径(/src/main/resources)下的schema.sql
文件会自动用来初始化表结构;放置在类路径下的data.sql
文件会自动用来填充表数据。
实际项目中几乎不会使用,也不建议使用;实际开发是先根据业务逻辑设计好数据库,再定实体类,再进行业务逻辑代码开发;数据库设计先行能更好更深入的理解业务逻辑和关联关系。
JPA自动配置分析
- Spring Boot 对 JPA 的自动配置在 org.springframework.boot.autoconfigure.orm.jpa 下,从该包下的HibernateJpaAutoConfiguration.class 可以看出,Spring Boot 默认支持的 JPA 实现是Hibernate, HibernateJpaAutoConfiguration 依赖于
JDBC
的自动配置类DataSourceAutoConfiguration。 - 从 JpaProperties.class 属性配置类可以看到,配置 JPA 可以使用 spring.jpa 为前缀的属性在 application.properties 中配置。
- JpaBaseConfiguration.class 类配置了 transactionManager,jpaVendorAdapter,entityManagerFactory 等 Bean。还提供了getPackagesToScan() 方法用于自动扫描注解 @Entity 的实体类。
Spring Data JPA自动配置
- 对 Spring Data JPA 的自动配置放在 org.springframework.boot.autoconfigure.data.jpa 包下。
- 从 JpaRepositoriesAutoConfiguration.class 配置类中可以看到该自动配置依赖于 HibernateJpaAutoConfiguration 配置。该配置类还引入了JpaRepositoriesAutoConfigureRegistrar.class 注册类,该注册启用了 EnableJpaRepositories,所以在 Spring Boot 项目中配置类就无须声明@EnableJpaRepositories。
Spring Boot JPA配置
在项目中添加spring-boot-starter-data-jpa
依赖,会自动添加jdbc
依赖,然后只需定义DataSource
、实体类和数据访问层,并在需要使用数据访问的地方注入数据访问层的 Bean 即可,无须额外的配置。
JpaRepositories类图
Spring Boot JPA使用示例
项目示例
- 新建Spring Boot项目,引入依赖
1
2
3
4
5
6
7
8
9
10
11
12<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency> - 在
application.properties
配置数据源。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25#-----------data source-----------------
80 =
dev =
classpath:log4j2.xml =
#-----------data source-----------------
#spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
#spring.datasource.driverClassName=com.mysql.jdbc.Driver
net.sf.log4jdbc.DriverSpy =
#spring.datasource.url=jdbc:mysql://localhost:3306/mytest?characterEncoding=utf-8&allowMultiQueries=true&autoReconnect=true
jdbc:log4jdbc:mysql://localhost:3306/mytest?characterEncoding=utf-8 =
admin =
123456 =
#spring.datasource.tomcat.max-active=20
#spring.datasource.tomcat.test-while-idle=true
#spring.datasource.tomcat.validation-query=select 1
#spring.datasource.tomcat.default-auto-commit=false
#spring.datasource.tomcat.min-idle=15
#spring.datasource.tomcat.initial-size=15
#spring.jpa.hibernate.ddl-auto=update
true =
org.hibernate.dialect.MySQL5Dialect =
true = - 定义实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
14//和数据库表映射的实体类
public class Actor {
//映射为数据库主键
private Long actorId;
// @Column(name = "realName") //字段名映射
private String firstName;
private String lastName;
private Date lastUpdate;
//...........set/get方法................
} - Controller层代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146import com.springboot.jpa.entity.Actor;
import com.springboot.jpa.service.ActorService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
import java.util.List;
public class ActorController {
private static final Logger logger = LogManager.getLogger(ActorController.class);
private ActorService actorService;
/**
* 添加
* @param actor
* @return
*/
public Actor addActor(Actor actor){
actor.setLastUpdate(new Date());
return actorService.addActor(actor);
}
/**
* 删除
* @param actorId
* @return
*/
public void deleteByActorId(Long actorId){
actorService.deleteByActorId(actorId);
}
/**
* 改
* @param actorId
* @param firstName
* @return
*/
public int updateFirstName(Long actorId, String firstName){
return actorService.updateFirstName(actorId, firstName);
}
/**
* 查所有
* @return
*/
public List<Actor> queryAll(){
return actorService.queryAll();
}
/**
* 主键条件查询
* @param actorId
* @return
*/
public Actor queryByActorId(Long actorId){
return actorService.queryByActorId(actorId);
}
/**
* 非主键条件查询
* @param firstName
* @return
*/
public List<Actor> queryByFirstName(String firstName){
return actorService.queryByFirstName(firstName);
}
/**
* HQL语句查询
* @param lastName
* @return
*/
public List<Actor> queryByLastName(String lastName){
return actorService.queryByLastName(lastName);
}
/**
* 条件查询,自定匹配规则
* @param firstName
* @param lastName
* @return
*/
public List<Actor> queryByFirstNameAndLastName(String firstName, String lastName){
return actorService.queryByFirstNameAndLastName(firstName,lastName);
}
/**
* 排序查询
* @return
*/
public List<Actor> queryByFirstNameWithSortDesc(String firstName){
// Sort sort = new Sort(Sort.Direction.DESC, "firstName");
Sort sort = new Sort(Sort.Direction.ASC, "firstName");
return actorService.queryByFirstNameWithSortDesc(sort);
}
/**
* 分页查询-sql:从第(page * size) + 1条开始查,查 size 条
* @param page 页码
* @param size 每页显示条数
* @return
*/
public Page<Actor> queryByPage(int page, int size){
PageRequest pageRequest = PageRequest.of(page, size);
return actorService.queryByPage(pageRequest);
}
/**
* 统计
* @return
*/
public Long countActor(){
return actorService.countActor();
}
/**
* 条件统计
* @param actor
* @return
*/
public Long countBy(Actor actor){
return actorService.countBy(actor);
}
} - 业务接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33import com.springboot.jpa.entity.Actor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import java.util.List;
public interface ActorService {
Actor addActor(Actor actor);
void deleteByActorId(Long actorId);
List<Actor> queryAll();
Actor queryByActorId(Long actorId);
Long countActor();
Long countBy(Actor actor);
List<Actor> queryByFirstName(String firstName);
int updateFirstName(Long actorId, String firstName);
List<Actor> queryByLastName(String lastName);
List<Actor> queryByFirstNameAndLastName(String firstName, String lastName);
List<Actor> queryByFirstNameWithSortDesc(Sort sort);
Page<Actor> queryByPage(PageRequest pageRequest);
} - 业务接口实现类代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167import com.springboot.jpa.entity.Actor;
import com.springboot.jpa.repository.ActorRepository;
import com.springboot.jpa.service.ActorService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.stereotype.Service;
import java.util.List;
public class ActorServiceImpl implements ActorService {
private ActorRepository actorRepository;
/**
* 增
*
* @param actor
* @return
*/
public Actor addActor(Actor actor) {
return actorRepository.save(actor);
}
/**
* 删: deleteById()
*
* @param actorId
* @return
*/
public void deleteByActorId(Long actorId) {
actorRepository.deleteById(actorId);
}
/**
* 改
*
* @param firstName
* @return
*/
public int updateFirstName(Long actorId, String firstName) {
return actorRepository.updateFirstName(actorId, firstName);
}
/**
* 查: 所有, findAll()
*
* @return
*/
public List<Actor> queryAll() {
return actorRepository.findAll();
}
/**
* 查: 条件-主键, findById(primary key)
*
* @param actorId
* @return
*/
public Actor queryByActorId(Long actorId) {
//springboot 1.5.3.Release
// return actorRepository.findOne(actorId);
//springboot 2.0.0.Release及以上
return actorRepository.findById(actorId).get();
}
/**
* 查: 条件-非主键字段
* 非主键查询都是匹配对象属性查询
*
* @param firstName
* @return
*/
public List<Actor> queryByFirstName(String firstName) {
Example<Actor> example = Example.of(new Actor().setFirstName(firstName));
return actorRepository.findAll(example);
}
/**
* 查: 定制查询条件和匹配规则
* @param firstName
* @param lastName
* @return
*/
public List<Actor> queryByFirstNameAndLastName(String firstName, String lastName) {
Actor actor = new Actor().setFirstName(firstName).setLastName(lastName);
Example<Actor> example = new Example<Actor>() {
//构造参与查询的参数
public Actor getProbe() {
return actor;
}
//设置查询匹配规则
public ExampleMatcher getMatcher() {
return ExampleMatcher.matchingAny();
}
};
// return actorRepository.findAll(example);
return actorRepository.findAll(Example.of(actor, ExampleMatcher.matchingAll()));
}
/**
* 排序查询
* @param sort
* @return
*/
public List<Actor> queryByFirstNameWithSortDesc(Sort sort) {
return actorRepository.findAll(sort);
}
/**
* 分页查询
* @param pageRequest
* @return
*/
public Page<Actor> queryByPage(PageRequest pageRequest) {
return actorRepository.findAll(pageRequest);
}
/**
* 使用HQL查询语句
* @param lastName
* @return
*/
public List<Actor> queryByLastName(String lastName) {
return actorRepository.queryByLastName(lastName);
}
/**
* 统计: 所有, count()
*
* @return
*/
public Long countActor() {
return actorRepository.count();
}
/**
* 统计: 条件,全匹配实体类属性,count(example)
*
* @param actor
* @return
*/
public Long countBy(Actor actor) {
// Actor actor = new Actor().setFirstName(firstName);
// Example<Actor> example = Example.of(actor);
return actorRepository.count(Example.of(actor));
}
} - 定义数据访问接口
继承JpaRepository
接口,指定映射的实体类和主键类型。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37import com.springboot.jpa.entity.Actor;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
public interface ActorRepository extends JpaRepository<Actor, Long> {
/**
* @Query注解的SQL语句是HQL,是对对象的查询
* 更新和删除操作必需开启事务,否则会报:
* nested exception is javax.persistence.TransactionRequiredException
* @param actorId
* @param firstName
* @return
*/
int updateFirstName(Long actorId, String firstName);
/**
* @Query注解传参有两种方式
* 1. 第一种是按参数的序号传参。
* 2. 第二种是绑定参数名传参,方法参数必须使用@Param注解来与HQL入参绑定
* @param lastName
* @return
*/
// @Query(value = "select a from Actor a where a.lastName = ?1")
List<Actor> queryByLastName( String lastName);
}
JPA常用注解
- @Entity:表示这是一个与数据库映射的实体类(作用在类上)。
- @Table:表示该实体类映射到的表名(作用在类上)。
- @Id:表示该属性映射为数据库表的主键(作用在属性上)。
- @GeneratedValue:指示主键生成策略(作用在属性上)。
- GenerationType.TABLE
- GenerationType.SEQUENCE
- GenerationType.IDENTITY
- GenerationType.AUTO
- @Column:表示该属性映射到表的字段名(作用在属性上)。
- @Temporal: Date类型属性映射到表字段的具体日期时间类型(作用在属性上),可设置如下值:
- TemporalType.DATE
- TemporalType.TIME
- TemporalType.TIMESTAMP
- @Inheritance:指定继承(父子)关系的实体类映射到表的策略(作用在类上)。
- InheritanceType.SINGLE_TABLE
- InheritanceType.TABLE_PER_CLASS
- InheritanceType.JOINED
示例代码>github
Spring Boot 2系列(四): Spring Data Jpa 集成详解和使用