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();                    //删除所有
}
        
        下面还是通过一个案例来介绍下这个接口里面方法的使用,项目的搭建就不介绍了,还是使用上一篇笔记搭建好的项目。
    • 为了和上一篇笔记的代码区分开,这里新建一个实体类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方法
}

三、PagingAndSortingRepository接口介绍

        先通过观察源码可知,这个接口继承CrudRepository接口,它新增了一组分页和排序的方法。源码如下

@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
	Iterable<T> findAll(Sort sort);        // 不带分页的排序
	Page<T> findAll(Pageable pageable);    // 带分页的排序
}
       
         下面通过一个案例讲解这个接口中方法的使用
    • 首先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());
	}
}

    • 运行测试方法,生成的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”查找并排序
}