SpringData专题(八)-Repository子接口相关

1.Repository子接口相关概述

看下面一张图,大概能了解Repository接口的继承体系
在这里插入图片描述
下面阐述下常用的Repository的子接口
CrudRepository:继承Repository接口,新增了一组CRUD相关的方法
PagingAndSortingRepository:继承CrudRepository接口,新增了一组分页排序的相关方法
JpaRepository:继承PagingAndSortRepository接口,新增了一组JPA规范的方法

有个不属于Repository继承体系的接口,也比较常用,它就是JpaSpecificationExecutor
JpaSpecificationExecutor:不属于Repository继承体系,有一组JPA Criteria查询相关方法

2.CrudRepository接口介绍

这个接口的代码,代码如下,它定义好了一组CRUD的方法,共11个。

@NoRepositoryBean
public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {
    <S extends T> S save(S var1);

    <S extends T> Iterable<S> save(Iterable<S> var1);

    T findOne(ID var1);

    boolean exists(ID var1);

    Iterable<T> findAll();

    Iterable<T> findAll(Iterable<ID> var1);

    long count();

    void delete(ID var1);

    void delete(T var1);

    void delete(Iterable<? extends T> var1);

    void deleteAll();
}

在这里插入图片描述

2.1 CrudRepository接口测试
  • 创建实体类 user,测试CrudRepository接口中常用的方法
@Entity
@Table(name = "jpa_user")
public class User {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Integer id;

	@Column
	private String name;

	@Column
	private Integer age;

	@Column
	private Boolean gender;

	@Column
	private String email;

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	public Boolean getGender() {
		return gender;
	}

	public void setGender(Boolean gender) {
		this.gender = gender;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	@Override
	public String toString() {
		return "User [id=" + id + ", name=" + name + ", age=" + age + ", gender=" + gender + ", email=" + email + "]";
	}
}
  • Dao层接口定义如下,直接继承CrudRepository接口即可
public interface UserDao extends CrudRepository<User, Integer> {
	
}
  • 测试
/**
 * @author bruceliu
 * @create 2019-07-31 10:48
 * @description
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestUser {

    @Autowired
    UserDao ud;

    /**
     * 单个新增
     */
    @Test
    public void testSave(){
        User u=new User();
        u.setName("小泽玛丽");
        u.setGender(true);
        u.setAge(40);
        u.setEmail("xiaoze@163.com");

        ud.save(u);
    }

    /**
     * 批量新增
     */
    @Test
    public void testSaveSome(){
        List<User> list = new ArrayList<User>();
        for (int i = 'A'; i <= 'Z'; i++) {
            User u = new User();
            u.setName((char) i + "" + (char) i);
            u.setGender(true);
            u.setAge(i + 1);
            u.setEmail(u.getName() + "@163.com");
            list.add(u);
        }
        //测试批量新增
        Iterable<User> users = ud.save(list);
        for (User user : users) {
            System.out.println(user);
        }
    }

    /**
     * 测试更新
     */
    @Test
    public void testCrudRepositoryUpdate(){
        User user = ud.findOne(2);
        System.out.println(user);
        user.setName("张三");
        user.setGender(false);

        User u = ud.save(user); // 有id时是更新
        System.out.println("保存成功!"+u);
    }

    /**
     * 查询所有
     */
    @Test
    public void testFindAll(){
        Iterable<User> users = ud.findAll();
        for (User user : users) {
            System.out.println(user);
        }
    }

    /**
     * 查询 根据ID查询
     */
    @Test
    public void testFindAll1(){
        List<Integer> ids=new ArrayList<Integer>();
        ids.add(1);
        ids.add(2);
        ids.add(5);
        ids.add(8);
        Iterable<User> users = ud.findAll(ids);
        for (User user : users) {
            System.out.println(user);
        }
    }

    /**
     * 批量删除
     */
    @Test
    public void testDeleteSome(){

        List<Integer> ids=new ArrayList<Integer>();
        ids.add(3);
        ids.add(4);
        ids.add(6);
        ids.add(9);

        Iterable<User> users = ud.findAll(ids); //先把要查询的数据库查询为一个集合!

        //再把对象集合放进去
        ud.delete(users);
    }


}

3.PagingAndSortingRepository接口介绍

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

@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
    Iterable<T> findAll(Sort var1);

    Page<T> findAll(Pageable var1);
}

在这里插入图片描述

3.1 PagingAndSortingRepository接口测试

下面通过一个案例讲解这个接口中方法的使用

  • 首先Dao层改一下,把继承的接口改为PagingAndSortingRepository
public interface UserDao extends PagingAndSortingRepository<User, Integer> {

	
}
  • 测试

    /**
     * 查询所有 可以排序
     */
    @Test
    public void testfindAll(){

        Sort.Order order1=new Sort.Order(Sort.Direction.DESC, "id");  // 按id降序
        Sort.Order order2=new Sort.Order(Sort.Direction.ASC, "age"); // 按age升序
        Sort sort = new Sort(order1, order2); //Sort封装了排序规则
        Iterable<User> users = ud.findAll(sort);
        for (User user : users) {
            System.out.println(user);
        }
        
    }


/**
	 * 测试PagingAndSortingRepositoryd的分页且排序方法
	 */
	@Test
	public void testPagingAndSortingRepository() {
		/* 需求:查询第3页的数据,每页5条 */
		int pageIndex=2;
		int pageSize = 5;
		
		// Pageable 接口通常使用的其 PageRequest 实现类. 其中封装了需要分页的信息
		// 排序相关的. Sort 封装了排序的信息
		// Order 是具体针对于某一个属性进行升序还是降序.
        Sort.Order order1=new Sort.Order(Sort.Direction.DESC, "id");  // 按id降序
        Sort.Order order2=new Sort.Order(Sort.Direction.ASC, "age"); // 按age升序
        Sort sort = new Sort(order1, order2); //Sort封装了排序规则
        
		Pageable pageable = new PageRequest(pageIndex-1, pageSize, 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("当前页面的记录数: " + result.getNumberOfElements());
		System.out.println("当前的user对象的结果如下:");
		List<User> users = result.getContent();
		for (User user : users) {
			System.out.println(user);
		}
	}

4.JpaRepository接口介绍

这个接口继承了PagingAndSortingRepository接口,所以开发中一般都会继承它,它功能多一点。源码如下

@NoRepositoryBean
public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {
    List<T> findAll();

    List<T> findAll(Sort var1);

    List<T> findAll(Iterable<ID> var1);

    <S extends T> List<S> save(Iterable<S> var1);

    void flush();

    <S extends T> S saveAndFlush(S var1);

    void deleteInBatch(Iterable<T> var1);

    void deleteAllInBatch();

    T getOne(ID var1);
}
4.1 JpaRepository测试
  • 首先Dao层改一下,把继承的接口改为PagingAndSortingRepository
public interface UserDao extends JpaRepository<User, Integer> {

	
}
  • 测试
/**
 * @author bruceliu
 * @create 2019-07-31 14:18
 * @description
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestUser3 {

    @Autowired
    UserDao userDao;

    /**
     * 批量删除 性能高,一条SQL执行结束!类似于MyBatis中的foreach!!!
     */
    @Test
    public void testDelete1(){

        List<Integer> ids=new ArrayList<Integer>();
        ids.add(26);
        ids.add(27);

        List<User> users = userDao.findAll(ids);

        userDao.deleteInBatch(users);
    }

    /**
     * 要么新增 要么更新
     */
    @Test
    public void testSaveAndFlush(){
        User u=new User();
        u.setName("小波波");
        u.setAge(35);
        u.setGender(true);

        userDao.saveAndFlush(u); //新增
    }

    /**
     * 要么新增 要么更新
     */
    @Test
    public void testSaveAndFlush1(){
        User u=new User();
        u.setName("大波波");
        u.setAge(36);
        u.setGender(true);
        u.setId(30);

        userDao.saveAndFlush(u); //更新 根据ID更新 因为这个ID在数据库存在了
    }

    /**
     * 要么新增 要么更新
     */
    @Test
    public void testSaveAndFlush2(){
        User u=new User();
        u.setName("大波波");
        u.setAge(36);
        u.setGender(true);
        u.setId(300);//ID在数据库中没有,那么就新增!新增之后的编号是数据自增的编号 不是300

        userDao.saveAndFlush(u); //新增  因为ID在数据库中没有 默认就新增
    }
}
  • 总结下saveAndFlush方法:
    如果对象的id在数据库存在,则是更新;如果不存在则是保存
    返回的对象和原来的对象之所以不相等时因为返回的是持久化对象的引用。也就是返回的是一级缓存中对象的引用。

5.JpaSpecificationExecutor接口介绍

这个接口不属于Repository体系,定义了一组JPA Criteria查询的方法,其源码如下所示

public interface JpaSpecificationExecutor<T> {
    T findOne(Specification<T> var1);

    List<T> findAll(Specification<T> var1);

    Page<T> findAll(Specification<T> var1, Pageable var2); //分页可以动态的条件

    List<T> findAll(Specification<T> var1, Sort var2);

    long count(Specification<T> var1);  //带条件查询总数
}

在这里插入图片描述

有时我们在查

询某个实体的时候,给定的条件是不固定的,这时就需要动态构建相应的查询语句,在Spring Data JPA中可以通过JpaSpecificationExecutor接口查询。相比JPQL,其优势是类型安全,更加的面向对象。

dao层做了的改动,继承JpaRepository的同时也继承JpaSpecificationExecutor,具体见下

/**
 * @author bruceliu
 * @create 2019-07-31 10:47
 * @description  测试CrudRepository 中11方法
 */
public interface UserDao extends JpaRepository<User, Integer>,JpaSpecificationExecutor<User> {


}
  • 测试代码如下,这个测试的是 Page findAll(Specification spec, Pageable pageable) 这个方法
/**
 * @author bruceliu
 * @create 2019-07-31 14:18
 * @description
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestUser4 {

    @Autowired
    UserDao userDao;

    /**
     * 带条件查询数据
     * 类似于MyBatis的动态SQL ,可以动态的拼接条件
     */
    @Test
    public void testCount(){

        //接口 匿名内部类 专门负责封装查询条件的
        Specification specification= new Specification() {
            // root 要查询实体类
            // criteriaQuery 条件
            // CriteriaBuilder 对象. 用于创建 Criteria 相关对象的工厂. 当然可以从中获取到
            // Predicate 类型, 代表一个查询条件
            @Override
            public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder cb) {
                Predicate p1 = cb.notEqual(root.get("id"), 15);     //条件1   id!=15
                Predicate p2 = cb.like(root.get("email"), "%163.com");   // 条件2 email like '%163.com'
                return cb.or(p1,p2);
            }
        };

        long count = userDao.count(specification);
        System.out.println(count);
    }


    /**
     * 查询所有 可以排序 可以分页
     */
    @Test
    public void testfindPage(){

        /* 需求:查询第3页的数据,每页5条 */
        int pageIndex=3;
        int pageSize =5;
        Integer totalCount = Integer.parseInt(userDao.count()+"");//总数
        int pageCount=(totalCount%pageSize==0)?(totalCount/pageSize):(totalCount/pageSize+1); //总页数

        System.out.println("当前页码是:"+pageIndex);
        System.out.println("总页码是:"+pageCount);
        System.out.println("每页大小是:"+pageSize);
        System.out.println("总数是:"+totalCount);

        Sort.Order order1=new Sort.Order(Sort.Direction.DESC, "id");  // 按id降序
        Sort.Order order2=new Sort.Order(Sort.Direction.ASC, "age"); // 按age升序

        Sort sort = new Sort(order1, order2); //Sort封装了排序规则

        //分页条件对象
        Pageable pageable = new PageRequest(pageIndex-1, pageSize,sort);

        //接口 匿名内部类 专门负责封装查询条件的
        Specification specification= new Specification() {
            // root 要查询实体类
            // criteriaQuery 条件
            // CriteriaBuilder 对象. 用于创建 Criteria 相关对象的工厂. 当然可以从中获取到
            // Predicate 类型, 代表一个查询条件
            @Override
            public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder cb) {
                Predicate p1 = cb.notEqual(root.get("id"), 15);     //条件1   id!=15
                Predicate p2 = cb.like(root.get("email"), "%163.com");   // 条件2 email like '%163.com'
                return cb.or(p1,p2);
            }
        };

        Page<User> page = userDao.findAll(specification,pageable);//带条件分页

        //每页的数据
        List<User> users = page.getContent();
        for (User user : users) {
            System.out.println(user);
        }

    }
    
      @Test
    public void testQuery1(){

        //接口 匿名内部类 专门负责封装查询条件的
        Specification specification= new Specification() {
            // root 要查询实体类
            // criteriaQuery 条件
            // CriteriaBuilder 对象. 用于创建 Criteria 相关对象的工厂. 当然可以从中获取到
            // Predicate 类型, 代表一个查询条件
            @Override
            public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder cb) {
                Predicate p1 = cb.gt(root.get("id"), 10);     //条件1   id>15
                Predicate p2 = cb.like(root.get("name"), "%大%");   // 条件2 name like '%大%'
                return cb.or(p1,p2);
            }
        };

        List<User> users = userDao.findAll(specification);
        for (User user : users) {
            System.out.println(user);
        }
    }
    
}

对于JpaSpecificationExecutor,这个接口基本是围绕着Specification接口来定义的。我们可以简单的理解为,Specification构造的就是查询条件。

    //构造查询条件
    /**
    *	root	:Root接口,代表查询的根对象,可以通过root获取实体中的属性
    *	query	:代表一个顶层查询对象,用来自定义查询
    *	cb		:用来构建查询,此对象里有很多条件方法
    **/
    public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);

通过JpaSpecificationExecutor接口中的方法,就可以实现带分页的条件查询了。弥补了JpaRepository中的分页方法中不能带条件查询的缺点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值