Spring-data-jpa 学习笔记
Posted zeng1994
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring-data-jpa 学习笔记相关的知识,希望对你有一定的参考价值。
通过上一篇笔记的,我们掌握了SpringData的相关概念及简单的用法。但上一篇笔记主要讲的是Dao层接口直接继承Repository接口,然后再自己定义方法。主要阐述了自定义方法时的一些规则及SpringData是如何来解析这些方法的。实际上,一些常用的方法SpringData已经帮我们定义好了,我们只需要定义Dao层接口时继承Repository的有相关功能子接口就ok了。本文主要讲的是Repository各个子接口有什么功能,了解了子接口的功能后,后续开发dao层就方便了。
这里再强调一下使用SpringData开发Dao层的步骤,这个步骤很关键。下面这段话是从上一篇笔记中copy过来的
Spring Data JPA 进行持久层(即Dao)开发一般分三个步骤:
- 声明持久层的接口,该接口继承 Repository(或Repository的子接口,其中定义了一些常用的增删改查,以及分页相关的方法)。
- 在接口中声明需要的业务方法。Spring Data 将根据给定的策略生成实现代码。
- 在 Spring 配置文件中增加一行声明,让 Spring 为声明的接口创建代理对象。配置了 <jpa:repositories> 后,Spring 初始化容器时将会扫描 base-package 指定的包目录及其子目录,为继承 Repository 或其子接口的接口创建代理对象,并将代理对象注册为 Spring Bean,业务层便可以通过 Spring 自动封装的特性来直接使用该对象。
一、Repository子接口相关概述
先来看下面一张图,大概能了解Repository接口的继承体系
下面阐述下常用的Repository的子接口
- CrudRepository:继承Repository接口,新增了一组CRUD相关的方法
- PagingAndSortingRepository:继承CrudRepository接口,新增了一组分页排序的相关方法
- JpaRepository:继承PagingAndSortRepository接口,新增了一组JPA规范的方法
有个不属于Repository继承体系的接口,也比较常用,它就是JpaSpecificationExecutor
- JpaSpecificationExecutor:不属于Repository继承体系,有一组JPA Criteria查询相关方法
二、CrudRepository接口介绍
首先我们看下这个接口的代码,代码如下,它定义好了一组CRUD的方法,共11个。
@NoRepositoryBean
public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {
<S extends T> S save(S entity); //保存
<S extends T> Iterable<S> save(Iterable<S> entities);//批量保存
T findOne(ID id); //根据id查询一个对象
boolean exists(ID id); //判断对象是否存在
Iterable<T> findAll(); //查询所有的对象
Iterable<T> findAll(Iterable<ID> ids);//根据id列表查询所有的对象
long count(); //计算对象的总个数
void delete(ID id); //根据id删除
void delete(T entity); //删除对象
void delete(Iterable<? extends T> entities);//批量删除
void deleteAll(); //删除所有
}
14
1
2
public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {
3
<S extends T> S save(S entity); //保存
4
<S extends T> Iterable<S> save(Iterable<S> entities);//批量保存
5
T findOne(ID id); //根据id查询一个对象
6
boolean exists(ID id); //判断对象是否存在
7
Iterable<T> findAll(); //查询所有的对象
8
Iterable<T> findAll(Iterable<ID> ids);//根据id列表查询所有的对象
9
long count(); //计算对象的总个数
10
void delete(ID id); //根据id删除
11
void delete(T entity); //删除对象
12
void delete(Iterable<? extends T> entities);//批量删除
13
void deleteAll(); //删除所有
14
}
下面还是通过一个案例来介绍下这个接口里面方法的使用,项目的搭建就不介绍了,还是使用上一篇笔记搭建好的项目。
- 为了和上一篇笔记的代码区分开,这里新建一个实体类User。这个实体类的属性如下图
- Dao层接口定义如下,直接继承CrudRepository接口即可
- 测试类代码如下,首先我测试了批量保存方法,向数据库插入了26条数据。后面又测试了保存方法,发现这个保存方法可以起更新的作用的,类似于JPA中EntityManage的merge方法
/** 测试CrudRepository的批量save方法 */
@Test
public void testCrudRepositorySaveMethod(){
UserDao dao = ctx.getBean(UserDao.class);
List<User> list = new ArrayList<>();
for (int i = \'A\'; i <= \'Z\'; i++) {
User u = new User();
u.setName((char)i + "" + (char)i); // AA,BB这种
u.setGender(true);
u.setAge(i + 1);
u.setEmail(u.getName() + "@163.com");
list.add(u);
}
// 调用dao的批量保存
dao.save(list);
}
/** 测试CrudRepository的save */
@Test
public void testCrudRepositoryUpdate(){
UserDao dao = ctx.getBean(UserDao.class);
// 从数据库查出来
User user = dao.findOne(1);
// 修改名字
user.setName("Aa");
dao.save(user); // 经过测试发现,有id时是更新,但不是绝对的;类似jpa的merge方法
}
27
1
/** 测试CrudRepository的批量save方法 */
2
3
public void testCrudRepositorySaveMethod(){
4
UserDao dao = ctx.getBean(UserDao.class);
5
List<User> list = new ArrayList<>();
6
for (int i = \'A\'; i <= \'Z\'; i++) {
7
User u = new User();
8
u.setName((char)i + "" + (char)i); // AA,BB这种
9
u.setGender(true);
10
u.setAge(i + 1);
11
u.setEmail(u.getName() + "@163.com");
12
list.add(u);
13
}
14
// 调用dao的批量保存
15
dao.save(list);
16
}
17
18
/** 测试CrudRepository的save */
19
20
public void testCrudRepositoryUpdate(){
21
UserDao dao = ctx.getBean(UserDao.class);
22
// 从数据库查出来
23
User user = dao.findOne(1);
24
// 修改名字
25
user.setName("Aa");
26
dao.save(user); // 经过测试发现,有id时是更新,但不是绝对的;类似jpa的merge方法
27
}
三、PagingAndSortingRepository接口介绍
先通过观察源码可知,这个接口继承CrudRepository接口,它新增了一组分页和排序的方法。源码如下
@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
Iterable<T> findAll(Sort sort); // 不带分页的排序
Page<T> findAll(Pageable pageable); // 带分页的排序
}
5
1
2
public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
3
Iterable<T> findAll(Sort sort); // 不带分页的排序
4
Page<T> findAll(Pageable pageable); // 带分页的排序
5
}
下面通过一个案例讲解这个接口中方法的使用
- 首先Dao层改一下,把继承的接口改为PagingAndSortingRepository
- 单元测试的代码如下,这里只测试了带分页和排序的那个方法,不带分页的那个方法就不测试了;这里的重点是参数怎么传。而且springdata的分页时,页码是从0开始的,这点要特别注意。
/** 测试PagingAndSortingRepositoryd的分页且排序方法 */
@Test
public void testPagingAndSortingRepository() {
UserDao userDao = ctx.getBean(UserDao.class);
/* 需求:查询第3页的数据,每页5条 */
int page = 3 - 1; //由于springdata默认的page是从0开始,所以减1
int size = 5;
//Pageable 接口通常使用的其 PageRequest 实现类. 其中封装了需要分页的信息
//排序相关的. Sort 封装了排序的信息
//Order 是具体针对于某一个属性进行升序还是降序.
Order order1 = new Order(Direction.DESC, "id");//按id降序
Order order2 = new Order(Direction.ASC, "age");//按age升序
Sort sort = new Sort(order1,order2);
Pageable pageable = new PageRequest(page, size,sort);
Page<User> result = userDao.findAll(pageable);
System.out.println("总记录数: " + result.getTotalElements());
System.out.println("当前第几页: " + (result.getNumber() + 1));
System.out.println("总页数: " + result.getTotalPages());
System.out.println("当前页面的 List: " + result.getContent());
System.out.println("当前页面的记录数: " + result.getNumberOfElements());
System.out.println("当前的user对象的结果如下:");
for (User user : result.getContent()) {
System.out.println(user.getId() + " == " + user.getAge());
}
}
27
1
/** 测试PagingAndSortingRepositoryd的分页且排序方法 */
2
3
public void testPagingAndSortingRepository() {
4
UserDao userDao = ctx.getBean(UserDao.class);
5
/* 需求:查询第3页的数据,每页5条 */
6
int page = 3 - 1; //由于springdata默认的page是从0开始,所以减1
7
int size = 5;
8
//Pageable 接口通常使用的其 PageRequest 实现类. 其中封装了需要分页的信息
9
//排序相关的. Sort 封装了排序的信息
10
//Order 是具体针对于某一个属性进行升序还是降序.
11
Order order1 = new Order(Direction.DESC, "id");//按id降序
12
Order order2 = new Order(Direction.ASC, "age");//按age升序
13
Sort sort = new Sort(order1,order2);
14
15
Pageable pageable = new PageRequest(page, size,sort);
16
Page<User> result = userDao.findAll(pageable);
17
18
System.out.println("总记录数: " + result.getTotalElements());
19
System.out.println("当前第几页: " + (result.getNumber() + 1));
20
System.out.println("总页数: " + result.getTotalPages());
21
System.out.println("当前页面的 List: " + result.getContent());
22
System.out.println("当前页面的记录数: " + result.getNumberOfElements());
23
System.out.println("当前的user对象的结果如下:");
24
for (User user : result.getContent()) {
25
System.out.println(user.getId() + " == " + user.getAge());
26
}
27
}
- 运行测试方法,生成的sql和结果如下图
四、JpaRepository接口介绍
这个接口继承了PagingAndSortingRepository接口,所以开发中一般都会继承它,它功能多一点。源码如下
@NoRepositoryBean
public interface JpaRepository<T, ID extends Serializable>
extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
List<T> findAll(); //查询方法
List<T> findAll(Sort sort); //查询方法,带排序
List<T> findAll(Iterable<ID> ids); //查询方法,参数为id集合
<S extends T> List<S> save(Iterable<S> entities); //批量保存
void flush(); //刷新
<S extends T> S saveAndFlush(S entity); //保存并刷新,类似merge方法
void deleteInBatch(Iterable<T> entities);
void deleteAllInBatch();
T getOne(ID id);
<S extends T> List<S> findAll(Example<S> example); //根据“example”查找,参考:http://www.cnblogs.com/rulian/p/6533109.html
<S extends T> List<S> findAll(Example<S> example, Sort sort); // 根据“example”查找并排序
}
16
1
2
public interface JpaRepository<T, ID extends Serializable>
3
extends PagingAndSortingRepository<T, ID>, 以上是关于Spring-data-jpa 学习笔记的主要内容,如果未能解决你的问题,请参考以下文章
Spring Boot学习进阶笔记-Spring-data-jpa
[原创]java WEB学习笔记61:Struts2学习之路--通用标签 property,uri,param,set,push,if-else,itertor,sort,date,a标签等(代码片段