Luckylau's Blog

SpringBoot之Spring-Data-JPA

什么是Spring-Data-JPA?

JPA(Java Persistence API,Java持久化API),定义了对象-关系映射(ORM)以及实体对象持久化的标准接口。

Spring Data是一个用于简化数据库访问,并支持云服务的开源框架。其主要目标是使得对数据的访问变得方便快捷,并支持map-reduce框和云计算数据服务。 Spring Data 包含多个子项目,Spring-data-jpa是其中一个用于简化JPA开发的框架。

Spring Data JPA的核心概念:

Repository:最顶层的接口,是一个空的接口,目的是为了统一所有Repository的类型,且能让组件扫描的时候自动识别。
CrudRepository :是Repository的子接口,提供CRUD的功能
PagingAndSortingRepository:是CrudRepository的子接口,添加分页和排序的功能
JpaRepository:是PagingAndSortingRepository的子接口,增加了一些实用的功能,比如:批量操作等。
JpaSpecificationExecutor:用来做负责查询的接口
Specification:是Spring Data JPA提供的一个查询规范,要做复杂的查询,只需围绕这个规范来设置查询条件即可。

配置Spring-Data-JPA的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.2.RELEASE</version>
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<-! 连接mysql数据库 ->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>

SpringBoot的DataSource属性的说明

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
spring.datasource.username=root
spring.datasource.password=root123
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.platform=mysql
#指定初始化数据源,是否用data.sql来初始化,默认: true
spring.datasource.initialize=false
#指定Data (DML)脚本
spring.datasource.data=classpath:sql/data.sql
#指定Schema (DDL)脚本
spring.datasource.schema=classpath:sql/schema-mysql.sql
#指定SQL scripts编码
spring.datasource.sqlScriptEncoding=UTF-8
#初始化出错是否继续进行
spring.datasource.continueOnError=true
#是否在启动时初始化schema,默认为false
spring.jpa.generate-ddl=false
#指定DDL mode (none, validate, update, create, create-drop).
#当使用内嵌数据库时,默认是create-drop,否则为none
spring.jpa.hibernate.ddl-auto=update
#是否开启sql的log,默认为: false
spring.jpa.show-sql=false
#指定命名策略
spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.ImprovedNamingStrategy
#方言
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
#
spring.data.jpa.repositories.enabled=true

Spring-Data-JPA的常用接口说明

结合项目:MessagePushandEmailSend

实体类

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
import java.io.Serializable;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.hibernate.annotations.GenericGenerator;
@Entity
@Table(name = "user_info")
public class UserInfoE implements Serializable{
/**
*
*/
private static final long serialVersionUID = -498870986608804909L;
@Id
@GenericGenerator(name ="user_info-uuid",strategy ="uuid")
@GeneratedValue(generator ="user_info-uuid")
private String id;
// SMTP服务器地址
@Column(nullable = true, length = 128)
private String smtpServer;
// 发件人邮箱地址
@Column(nullable = true, length = 128)
private String sender;
// 收件人邮箱地址
@Column(nullable = true, length = 128)
private String receiverAddr;
// 登录SMTP服务器的用户名
@Column(nullable = true, length = 128)
private String username;
// 登录SMTP服务器的密码
@Column(nullable = true, length = 128)
private String password;
@OneToMany(fetch=FetchType.EAGER,cascade=CascadeType.ALL,orphanRemoval=true)
@JoinColumn(name="id")
private List<MessageInfoE> messageinfo;
}

接口

Repository

Repository 接口是 Spring Data 的一个核心接口,它不提供任何方法,开发者需要在自己定义的接口中声明需要的方法。

1
2
3
4
public interface UserDao extends Repository<AccountInfo, Long> { …… }
//等价
@RepositoryDefinition(domainClass = AccountInfo.class, idClass = Long.class)
public interface UserDao { …… }

CrudRepository

使用 CrudRepository 的副作用是它可能暴露了你不希望暴露给业务层的方法。比如某些接口你只希望提供增加的操作而不希望提供删除的方法。针对这种情况,开发者只能退回到 Repository 接口,然后到 CrudRepository 中把希望保留的方法声明复制到继承Repository 的自定义的接口中即可。

PagingAndSortingRepository

它继承自 CrudRepository 接口,在 CrudRepository 基础上新增了两个与分页有关的方法。但是,我们很少会将自定义的持久层接口直接继承自 PagingAndSortingRepository,而是在继承 Repository 或 CrudRepository 的基础上,在自己声明的方法参数列表最后增加一个 Pageable 或 Sort 类型的参数,用于指定分页或排序信息即可,这比直接使用 PagingAndSortingRepository 提供了更大的灵活性。

JpaRepository

继承自 PagingAndSortingRepository 的针对 JPA 技术提供的接口,它在父接口的基础上,提供了其他一些方法,比如 flush(),saveAndFlush(),deleteInBatch() 等。如果有这样的需求,则可以继承该接口。

Spring Data JPA本身会提供3中方式创建查询:

通过解析方法名创建查询

Keyword Sample JPQL snippet
IsNotNull findByAgeNotNull … where x.age not null
Like findByNameLike … where x.name like ?1
NotLike findByNameNotLike … where x.name not like ?1
StartingWith findByNameStartingWith … where x.name like ?1(parameter bound with appended %)
EndingWith findByNameEndingWith … where x.name like ?1(parameter bound with prepended %)
Containing findByNameContaining … where x.name like ?1(parameter bound wrapped in %)
OrderBy findByAgeOrderByName … where x.age = ?1 order by x.name desc
Not findByNameNot … where x.name <> ?1
In findByAgeIn … where x.age in ?1
NotIn findByAgeNotIn … where x.age not in ?1
True findByActiveTrue … where x.avtive = true
Flase findByActiveFalse … where x.active = false
And findByNameAndAge … where x.name = ?1 and x.age = ?2
Or findByNameOrAge … where x.name = ?1 or x.age = ?2
Between findBtAgeBetween … where x.age between ?1 and ?2
LessThan findByAgeLessThan … where x.age < ?1
GreaterThan findByAgeGreaterThan … where x.age > ?1
After/Before
IsNull findByAgeIsNull … where x.age is null

第一步,解析时,会先把方法名多余的前缀截取掉,比如find、findBy、read、readBy、get、getBy,然后对剩下部分进行解析。

第二步,例如findByUserDepUuid()先判断userDepUuid (根据POJO 规范,首字母变为小写)是否为查询实体的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,继续第二步;

从右往左截取第一个大写字母开头的字符串此处为Uuid),然后检查剩下的字符串是否为查询实体的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,则重复第二步,继续从右往左截取;最后假设user为查询实体的一个属性;

接着处理剩下部分(DepUuid),先判断user 所对应的类型是否有depUuid属性,如果有,则表示该方法最终是根据“ Doc.user.depUuid” 的取值进行查询;否则继续按照步骤二 的规则从右往左截取,最终表示根据“Doc.user.dep.uuid” 的值进行查询。

可能会存在一种特殊情况,比如Doc包含一个user 的属性,也有一个userDep 属性,此时会存在混淆。可以明确在属性之间加上”_” 以显式表达意图,比如”findByUser_DepUuid()” 或者”findByUserDep_uuid()”。

使用 @Query 创建查询

通过调用 JPA 命名查询语句创建查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Entity
@NamedQuery(name = "User.findByEmailAddress",
query = "select u from User u where u.emailAddress = ?1")
public class User {
}//单一查询
@Entity
@NamedQueries({
@NamedQuery(name = "User.findByEmailAddress",
query = "select u from User u where u.emailAddress = ?1"),
@NamedQuery(name = "User.findByName",
query = "select u from User u where u.name = ?1"),
})
public class User {
}//多个命名查询
public interface UserRepository extends JpaRepository<User, Long> {
User findByEmailAddress(String emailAddress);
User findByName(String name);
}

参考:

https://segmentfault.com/a/1190000004316491

Luckylau wechat
如果对您有价值,看官可以打赏的!