Spring Data JPA介绍
Spring Data JPA是Spring基于ORM框架、JPA规范的基础上封装的一套JPA应用框架,它提供了增删改查等常用功能,使开发者可以用较少的代码实现数据操作,同时还易于扩展。考虑到部分读者可能对Spring Data JPA并不熟悉,在正式讲解Spring Boot整合JPA之前,先针对Spring Data JPA的基本使用进行简单介绍,具体内容如下。
1.编写ORM实体类
Spring Data JPA框架是针对具有ORM关系的数据操作,所以在使用Spring Data JPA时,首先需要编写一个实体类与数据表进行映射,并且配置好映射关系,示例代码如下。
@Entity(name = "t_comment")
public class Discuss {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "a_id")
private Integer aId;
// 省略getXX()和setXX()方法
}
上述代码中,通过各种注解声明了一个Spring Data JPA实体类Discuss,同时配置好了与数据表t_comment的映射关系。这里,对上述示例中使用的注解进行简要说明,具体如下。
● @Entity:标注在类上,表示与数据表具有映射关系的实体类,同时默认对应表名为类名的首字母小写形式,否则必须使用name属性指定具体映射的表名。
● @Id:标注在类属性或者getXX()方法上,表示某一个属性对应表中的主键。
● @GeneratedValue:与@Id注解标注在同一位置,用于表示属性对应主键的生成策略,可省略。Spring Data JPA支持的主键生成策略包括有TABLE(使用一个特定的数据库表格来保存主键)、SEQUENCE(不支持主键自增长的数据库主键生成策略)、IDENTITY(主键自增)和AUTO(JPA自主选择前面3种合适的策略,是默认选项)。
● @Column:标注在属性上,配合name属性表示类属性对应的表字段名。如果类属性与表字段名相同,则可以省略。
2.编写Repository接口
针对不同的表数据操作编写各自对应的Repository接口,并根据需要编写对应的数据操作方法,示例代码如下。
public interface DiscussRepository extends JpaRepository<Discuss,Integer> {
public List<Discuss> findByAuthorNotNull();
@Query("SELECT c FROM t_comment c WHERE c.aId = ?1")
public List<Discuss> getDiscussPaged(Integer aid,Pageable pageable);
@Query(value = "SELECT * FROM t_comment WHERE a_Id = ?1",nativeQuery = true)
public List<Discuss> getDiscussPaged2(Integer aid,Pageable pageable);
@Transactional
@Modifying
@Query("UPDATE t_comment c SET c.author = ?1 WHERE c.id = ?2")
public int updateDiscuss(String author,Integer id);
@Transactional
@Modifying
@Query("DELETE t_comment c WHERE c.id = ?1")
public int deleteDiscuss(Integer id);
}
上述代码中,通过几种常见的形式编写了对Discuss类对应的t_comment表数据进行的删、改、查操作。这里,同样对上述示例中使用的数据操作方式进行简要说明,具体如下。
● findByAuthorNotNull()方法:该方法是一个基本的查询方法,上方没有任何注解,属于JPA支持的方法名关键字查询方式;同时通过定义的方法名可以猜测出,该方法的作用是查询author非空的Discuss评论信息。
● getDiscussPaged()方法:该方法上方通过@Query注解引入了一个SQL语句,同时通过方法参数可以猜测出,该方法的作用是通过文章id分页查询出Discuss评论信息。
● getDiscussPaged()2方法:该方法与getDiscussPaged()方法的参数和作用完全一样,区别是该方法上方的@Query注解将nativeQuery属性设置为了true(默认false),用来编写原生SQL语句。
● updateDiscuss()方法和deleteDiscuss()方法:这两个方法同样使用@Query注解配置了对应的SQL语句,这两个方法分别对应数据的更新和删除操作;另外,在数据更新、删除操作的方法上还必须使用@Modifying和@Transactional注解。其中,@Modifying注解表示数据的变更操作,@Transactional注解用于对数据修改的事务管理。
下面,针对在编写Spring Data JPA的Repository接口方法时需要注意的问题,进行重点说明。
(1)使用Spring Data JPA自定义Repository接口,必须继承XXRepository<T, ID>接口,其中的T代表要操作的实体类,ID代表实体类主键数据类型。在上述示例中,选择继承了JpaRepository接口,它的继承结构如图1所示。
图1 JpaRepository接口继承结构
下面,对图1中JpaRepository接口继承结构中涉及到的接口进行说明,具体如下。
● Repository是Spring Data JPA提供的用于自定义Repository接口的顶级父接口,该接口中没有声明任何方法;
● CrudRepository接口是Repository的继承接口之一,包含了一些基本的CRUD方法;
● PagingAndSortingRepository接口继承CrudRepository接口的同时,提供了分页和排序两个方法;
● QueryByExampleExecutor接口是进行条件封装查询的顶级父接口,允许通过Example实例执行复杂条件查询。
JpaRepository接口同时继承了PagingAndSortingRepository接口和QueryByExampleExecutor接口,并额外提供了一些数据操作方法。自定义Repository接口文件时,通常会直接选择继承JpaRepository接口。
(2)在使用Spring Data JPA进行数据操作时,可以有多种实现方式,主要方式如下。
● 如果自定义接口继承了JpaRepository接口,则默认包含了一些常用的CRUD方法。
● 自定义Repository接口中,可以使用@Query注解配合SQL语句进行数据的查、改、删操作。
● 自定义Repository接口中,可以直接使用方法名关键字进行查询操作。
其中,Spring Data JPA中支持的方法名关键字及对应的SQL片段说明,如表1所示。
表1 JPA中支持的方法名关键字
关键字 | 方法名示例 | 对应的SQL片段 |
---|---|---|
And | findByLastnameAndFirstname | … where x.lastname = ?1 and x.firstname = ?2 |
Or | findByLastnameOrFirstname | … where x.lastname = ?1 or x.firstname = ?2 |
Is,Equals | findByFirstname,findByFirstnameIs,findByFirstnameEquals | … where x.firstname = ?1 |
Between | findByStartDateBetween | … where x.startDate between ?1 and ?2 |
LessThan | findByAgeLessThan | … where x.age < ?1 |
LessThanEqual | findByAgeLessThanEqual | … where x.age <= ?1 |
GreaterThan | findByAgeGreaterThan | … where x.age > ?1 |
GreaterThanEqual | findByAgeGreaterThanEqual | … where x.age >= ?1 |
After | findByStartDateAfter | … where x.startDate > ?1 |
Before | findByStartDateBefore | … where x.startDate < ?1 |
IsNull | findByAgeIsNull | … where x.age is null |
IsNotNull,NotNull | findByAge(Is)NotNull | … where x.age not null |
Like | findByFirstnameLike | … where x.firstname like ?1 |
NotLike | findByFirstnameNotLike | … where x.firstname not like ?1 |
StartingWith | findByFirstnameStartingWith | … where x.firstname like ?1 (绑定参数 %) |
EndingWith | findByFirstnameEndingWith | … where x.firstname like ?1 (绑定参数 %) |
Containing | findByFirstnameContaining | … where x.firstname like ?1 (绑定参数 %) |
OrderBy | findByAgeOrderByLastnameDesc | … where x.age = ?1 order by x.lastname desc |
Not | findByLastnameNot | … where x.lastname <> ?1 |
In | findByAgeIn(Collection<Age> ages) | … where x.age in ?1 |
NotIn | findByAgeNotIn(Collection<Age> ages) | … where x.age not in ?1 |
True | findByActiveTrue() | … where x.active = true |
False | findByActiveFalse() | … where x.active = false |
IgnoreCase | findByFirstnameIgnoreCase | … where UPPER(x.firstame) = UPPER(?1) |
(3)在自定义的Repository接口中,针对数据的变更操作(修改、删除),无论是否使用了@Query注解,都必须在方法上方添加@Transactional注解进行事务管理,否则程序执行就会出现InvalidDataAccessApiUsageException异常。如果在调用Repository接口方法的业务层Service类上已经添加了@Transactional注解进行事务管理,那么Repository接口文件中就可以省略@Transactional注解。
(4)在自定义的Repository接口中,使用@Query注解方式执行数据变更操作(修改、删除),除了要使用@Query注解,还必须添加@Modifying注解表示数据变更。
(5)JPA还支持使用Example实例进行复杂条件查询。例如,针对JpaRepository接口中已存在的findAll(Example<S> var1)方法进行查询,示例代码如下。
// 1、使用Example精确匹配查询条件
Discuss discuss =new Discuss();
discuss.setAuthor("张三");
Example<Discuss> example = Example.of(discuss);
List<Discuss> list = repository.findAll(example);
// 2、使用ExampleMatcher模糊匹配查询条件
Discuss discuss =new Discuss();
discuss.setAuthor("张");
ExampleMatcher matcher = ExampleMatcher.matching()
.withMatcher("author",startsWith());
Example<Discuss> example = Example.of(discuss, matcher);
List<Discuss> list = repository.findAll(example);
上述代码针对Spring Data JPA的一些基本使用进行了说明,实际上,Spring Data JPA还包更多语法和使用方式,有兴趣的读者可以查看官方文档学习。