SpringData Jpa(3)SpringDataJpa进阶,CURD

一、JPA提供的接口

  • Repository: 最顶层的接口,是一个空接口,目的是为了统一所有的Repository的类型,且能让组件扫描时自动识别
  • CrudRepository: Repository的子接口,提供CRUD 的功能。
  • PagingAndSortingRepository:CrudRepository的子接口, 添加分页排序。
  • JpaRepository: PagingAndSortingRepository的子接口,增加批量操作等。
  • JpaSpecificationExecutor: 用来做复杂查询的接口。
  • JpaRepositoryImplementation:继承了JpaSpecificationExecutor和JpaRepository,做所有的CRUD,如果想要使用JPQL和Criteria两种查询,建议继承此接口

SpringData Jpa(3)SpringDataJpa进阶,CURD_第1张图片

  • Repository

    最顶层的接口,是一个空的接口,目的是为了统一所有Repository的类型,且能让组件扫描的时候自动识别。

  • CrudRepository

    是Repository的子接口,提供基础的CRUD的功能。

@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {
	<S extends T> S save(S entity); //保存
	<S extends T> Iterable<S> saveAll(Iterable<S> entities);//批量保存
	Optional<T> findById(ID id); //按ID查询
	boolean existsById(ID id);//查询ID是否存在
	Iterable<T> findAll();//查询所有
	Iterable<T> findAllById(Iterable<ID> ids);//查询所有ID相同的
	long count();//计算条数
	void deleteById(ID id);//按ID删除
	void delete(T entity);//按实体类条件删除
	void deleteAll(Iterable<? extends T> entities);//按实体类 删除全部数据
	void deleteAll();//删除所有数据
}
  • PagingAndSortingRepository

    是CrudRepository的子接口,添加分页和排序的功能

@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
	Iterable<T> findAll(Sort sort);//排序
	Page<T> findAll(Pageable pageable);//分页
}
  • JpaRepository(常用)(重要,一般直接继承此接口)
    是PagingAndSortingRepository的子接口,增加了一些实用的功能,比如:批量操作等。
@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
	List<T> findAll();
	List<T> findAll(Sort sort);
	List<T> findAllById(Iterable<ID> ids);
	<S extends T> List<S> saveAll(Iterable<S> entities);
	void flush();
	<S extends T> S saveAndFlush(S entity);
	void deleteInBatch(Iterable<T> entities);
	void deleteAllInBatch();
	T getOne(ID id);
	@Override
	<S extends T> List<S> findAll(Example<S> example);
	@Override
	<S extends T> List<S> findAll(Example<S> example, Sort sort);
}
  • JpaSpecificationExecutor(常用):用来做负责查询的接口,使用到criteria查询需要继承此接口
public interface JpaSpecificationExecutor<T> {
	Optional<T> findOne(@Nullable Specification<T> spec);
	List<T> findAll(@Nullable Specification<T> spec);
	Page<T> findAll(@Nullable Specification<T> spec, Pageable pageable);
	List<T> findAll(@Nullable Specification<T> spec, Sort sort);
	long count(@Nullable Specification<T> spec);
}
  • JpaRepositoryImplementation(最佳版本):继承了JpaSpecificationExecutor和JpaRepository,做所有的CRUD,如果想要使用JPQL和Criteria两种查询,建议继承此接口

    • SimpleJpaRepository:是上面接口的实现类,负责规范化查询语句,里面有EntityManager

      • QuerydslJpaRepository:上面的子类,负责处理@Query的语句,里面有EntityManager

二、JPA自带的方法

  • 直接继承接口JpaRepository即可
public interface UserDao extends JpaRepository<对应的Entity类, 该类的主键类型>{...}
  • 接口里自带的所有增删改查方法
<S extends T> S save(S entity);
<S extends T> Iterable<S> saveAll(Iterable<S> entities);
Optional<T> findById(ID id);
boolean existsById(ID id);
Iterable<T> findAll();
Iterable<T> findAllById(Iterable<ID> ids);
long count();
void deleteById(ID id);
void delete(T entity);
void deleteAll(Iterable<? extends T> entities);
void deleteAll();
Iterable<T> findAll(Sort sort);
Page<T> findAll(Pageable pageable);
List<T> findAll();
List<T> findAll(Sort sort);
List<T> findAllById(Iterable<ID> ids);
<S extends T> List<S> saveAll(Iterable<S> entities);
void flush();
<S extends T> S saveAndFlush(S entity);
void deleteInBatch(Iterable<T> entities);
void deleteAllInBatch();
T getOne(ID id);
<S extends T> List<S> findAll(Example<S> example);
<S extends T> List<S> findAll(Example<S> example, Sort sort);

三、标准查询(重要)

  • 继承接口JpaRepository<对应的Entity类, 该类的主键类型>
public interface UserRepository extends JpaRepository<User, Long>{...}
  • 基础的增删上面继承接口后已经全部实现了,需要自定义的部分只有查询
  • 查询方法需要按照语法规范书写,规范下面表格已详细给出,这里举一个例子
public interface UserRepository extends JpaRepository<User, Long>{
    //按照用户名模糊查询,username是User类的一个字段
    findByUsernameLike(String username);
}

标准化查询对应的JPQL语法

Keyword Sample JPQL snippet
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(parameter bound with appended %)
EndingWith findByFirstnameEndingWith … where x.firstname like ?1(parameter bound with prepended %)
Containing findByFirstnameContaining … where x.firstname like ?1(parameter bound wrapped in%)
OrderBy findByAgeOrderByLastnameDesc … where x.age = ?1 order by x.lastname desc
Not findByLastnameNot … where x.lastname <> ?1
In findByAgeIn(Collection ages) … where x.age in ?1
NotIn findByAgeNotIn(Collection age) … 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)

四、JPQL查询

  • 继承接口JpaRepository<对应的Entity类, 该类的主键类型>
  • 编写方法和对象查询语言,注意不是传统的Sql语句
  • 在方法名上使用注解@Query,两种写法
  1. @Query(“原生的SQL语句”)
  2. @Query(value=“原生的SQL语句”)
  • 占位符

方式一:使用 ?1、?2 … 占位符从一开始

@Query("SELECT p FROM Person p WHERE name LIKE %?1% AND sex=?2")
Person findByNameLikeAndSexIs(String name,Integer sex);//注意要按顺序s

方式二:Sql语句使用 :param ,并且在方法形参使用注解 @Param(“param”) 注意注解中的值要与Sql中的 :param 一致

@Query("SELECT p FROM Person p WHERE name LIKE %:name% AND sex=:sex")
Person findByNameLikeAndSexIs(@Param("sex")Integer sexInt,@Param("name")String nameStr);//可以不按顺序
  • 示例
public interface UserRepository extends JpaRepository<User, Long> {
    
    //第一种传参方式;另外,在JPQL语法中,User只的不是表而是User实体类
    @Query("select u from User u where u.emailAddress = ?1")
    User findByEmailAddress(String emailAddress);

    //第二种传参方式
    @Query("select u from User u where u.firstname like %:firstname")
    List<User> findByFirstnameEndsWith(@Param("firstname")String firstname);

    //使用了原生Sql语句查询
    @Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?1", nativeQuery = true)
    User findByEmailAddress(String emailAddress);

    //原生语句的分页条件查询,Page需要两个对象,因此由两条语句:一个负责结果,一个负责总数
    @Query(value = "SELECT * FROM USERS WHERE LASTNAME = ?1",countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1",nativeQuery = true)
    Page<User> findByLastname(String lastname, Pageable pageable);
    
    //更新语句,一定要用@Modifying声明,在service层一定要使用事务@Transactional,要不会报错
    @Modifying
    @Query("update User u set u.firstname = ?1 where u.lastname = ?2")
    int setFixedFirstnameFor(String firstname, String lastname);
}

五、原生SQL查询

  • 在@Query注解中添加属性 nativeQuery=true 即可,然后书写原生的Sql即可
@Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?1", nativeQuery = true)
User findByEmailAddress(String emailAddress);
  • 其余需要注意的参考上面的 JPQL查询

六、自定义增删改

Spring-data-jpa 的@modifying注解

  • 在进行增删改的时候,一定要使用注解 @Modifying 进行标注,作用:提示 JPA 该操作是修改操作
  • 如果报错,添加事务:在方法上添加注解@Transactional
//更新语句,一定要用@Modifying声明,在service层一定要使用事务@Transactional,要不会报错
@Modifying
@Query("update User u set u.firstname = ?1 where u.lastname = ?2")
int setFixedFirstnameFor(String firstname, String lastname);

  • 有可能会有缓存存在而无法更新的情况:
//1.当进行 find 操作时,JPA 在 EntityManager 中缓存了 find 生成的对象,当再次 find 时会直接返回该对象。于是可能会出现下面这种情况 用 @Query 定义一个修改状态的方法
public interface EntityRepository extends JpaRepository<Entity, Integer> {
    @Modifying
    @Query("update Entity set status = 'IGNORED' where id = ?1")
    int updateStatus(int id);
}
//2.先读取一个对象,再修改对象状态,再次读取对象
Optional<Entity> entityBefore = repository.findById(1);
repository.updateStatus(1);
Optional<Entity> entityAfter = repository.findById(1);
//3.结果会发现 entityBefore 和 entityAfter 中的 Entity 对象 id 是相同的,中间对状态的修改并没有体现出来!当然,其原因也很明确,@Query 跟 find 和 save 系列方法是两套不同的体系,@Query 引起的数据库变更 EntityManager 并不能发现,更进一步说,使用其它工具或者其它框架修改数据库中的数据,也不能及时反应到 JPA 的 find 系列方法上来。

//4.当然,只要有缓存机制就一定不可避免存在此类问题,这仅是个取舍问题而不要认为是 BUG。如果要解决 find 得到的值不是数据库中最新值的问题可以有几种方式,避免使用 @Query 是一种方式,在需要时显式清理 EntityManager 的缓存也是一种方式。Spring Data JPA 提供了另外一种方式则是 @Modifying(clearAutomatically = true),@Modifying 的 clearAutomatically 属性为 true 时,执行完 modifying query 之后就会清理缓存,从而在下次 find 时就可以读取到数据库中的最新值。

//5.自动清理之后还会带来一个新的问题,clear 操作清理的缓存中,还包括提交后未 flush 的数据,例如调用 save 而不是 saveAndFlush 就有可能不会立即将修改内容更新到数据库中,在 save 之后 flush 之前调用 @Modifying(clearAutomatically = true) 修饰的方法就有可能导致修改丢失。如果再要解决这个问题,还可以再加上另外一个属性 @Modifying(clearAutomatically = true, flushAutomatically = true),@Modifying 的 flushAutomatically 属性为 true 时,执行 modifying query 之前会先调用 flush 操作,从而避免数据丢失问题。

//6.在实际运行中,clear 和 flush 操作都可能需要消耗一定的时间,要根据系统实际情况可以选择使用其中的一个或两个属性,以保证系统的正确性。
  1. 当进行 find 操作时,JPA 在 EntityManager 中缓存了 find 生成的对象,当再次 find 时会直接返回该对象。于是可能会出现下面这种情况 用 @Query 定义一个修改状态的方法

  2. 先读取一个对象,再修改对象状态,再次读取对象

  3. 结果会发现 entityBefore 和 entityAfter 中的 Entity 对象 id 是相同的,中间对状态的修改并没有体现出来!当然,其原因也很明确,@Query 跟 find 和 save 系列方法是两套不同的体系,@Query 引起的数据库变更 EntityManager 并不能发现,更进一步说,使用其它工具或者其它框架修改数据库中的数据,也不能及时反应到 JPA 的 find 系列方法上来。

  4. 当然,只要有缓存机制就一定不可避免存在此类问题,这仅是个取舍问题而不要认为是 BUG。如果要解决 find 得到的值不是数据库中最新值的问题可以有几种方式,避免使用 @Query 是一种方式,在需要时显式清理 EntityManager 的缓存也是一种方式。Spring Data JPA 提供了另外一种方式则是 @Modifying(clearAutomatically = true),@Modifying 的 clearAutomatically 属性为 true 时,执行完 modifying query 之后就会清理缓存,从而在下次 find 时就可以读取到数据库中的最新值。

  5. 自动清理之后还会带来一个新的问题,clear 操作清理的缓存中,还包括提交后未 flush 的数据,例如调用 save 而不是 saveAndFlush 就有可能不会立即将修改内容更新到数据库中,在 save 之后 flush 之前调用 @Modifying(clearAutomatically = true) 修饰的方法就有可能导致修改丢失。如果再要解决这个问题,还可以再加上另外一个属性 @Modifying(clearAutomatically = true, flushAutomatically = true),@Modifying 的 flushAutomatically 属性为 true 时,执行 modifying query 之前会先调用 flush 操作,从而避免数据丢失问题。

  6. 在实际运行中,clear 和 flush 操作都可能需要消耗一定的时间,要根据系统实际情况可以选择使用其中的一个或两个属性,以保证系统的正确性。

七、动态查询

7.1.接口关系

  1. AbstractQuery接口:可以获取root,构建查询条件
public interface AbstractQuery<T> extends CommonAbstractCriteria {
	//最关键的是from方法,可以得到语句的条件部分根对象
    <X> Root<X> from(Class<X> entityClass);
    <X> Root<X> from(EntityType<X> entity);
    AbstractQuery<T> where(Expression<Boolean> restriction);
    AbstractQuery<T> where(Predicate... restrictions);
    AbstractQuery<T> groupBy(Expression<?>... grouping);
    AbstractQuery<T> groupBy(List<Expression<?>> grouping);
    AbstractQuery<T> having(Expression<Boolean> restriction);
    AbstractQuery<T> having(Predicate... restrictions);
    AbstractQuery<T> distinct(boolean distinct);
    Set<Root<?>> getRoots();
    Selection<T> getSelection();
    List<Expression<?>> getGroupList();
    Predicate getGroupRestriction();
    boolean isDistinct();
    Class<T> getResultType();
}

  1. CriteriaQuery接口:继承了AbstractQuery接口,用于构建查询条件
public interface CriteriaQuery<T> extends AbstractQuery<T> {
    CriteriaQuery<T> select(Selection<? extends T> selection);
    CriteriaQuery<T> multiselect(Selection<?>... selections);
    CriteriaQuery<T> multiselect(List<Selection<?>> selectionList);
    CriteriaQuery<T> where(Expression<Boolean> restriction);//应用查询条件实例的方法
    CriteriaQuery<T> where(Predicate... restrictions);//应用查询条件实例的方法
    CriteriaQuery<T> groupBy(Expression<?>... grouping);
    CriteriaQuery<T> groupBy(List<Expression<?>> grouping);
    CriteriaQuery<T> having(Expression<Boolean> restriction);
    CriteriaQuery<T> having(Predicate... restrictions);
    CriteriaQuery<T> orderBy(Order... o);
    CriteriaQuery<T> orderBy(List<Order> o);
    CriteriaQuery<T> distinct(boolean distinct);
    List<Order> getOrderList();
    Set<ParameterExpression<?>> getParameters();
}
  1. CriteriaBuilder接口,能创建CriteriaQuery对象,还能创建查询条件(Expression表达式),定义了几乎所有的sql逻辑判断相关的表达式创建方法,功能非常全,因此一般都是用此接口中的方法。实现类是由hibernate提供
//这里列一下Expression的继承树,仅列出常见的
* Expression (定义了:isNull、isNotNull、in、as。。。)
	* Predicate(定义了:not,还有枚举对象{AND,OR}。。。)
	* Subquery(子查询接口,里面有select、where、groupBy、having。。。)
    * Path(JPA定义的,定义了一些获取方法,有和实体相关的,有和表达式相关的)
    	* From(JPA定义的,定义了许多join相关的方法)
    		* Join(连接查询,定义了join的条件on。。。)
    		* Root(就一个获取实体的方法)

  1. Specification接口:处理复杂查询,如多条件分页查询 Specification接口使用和意义

    里面定义了基本的连接语句动态添加的方法,Specifications是该接口的实现类

    注意方法toPredicate(断言方法),此方法负责处理criteria语句的条件,需要实现此方法

@SuppressWarnings("deprecation")
public interface Specification<T> extends Serializable {
	long serialVersionUID = 1L;
	static <T> Specification<T> not(Specification<T> spec) {
		return Specifications.negated(spec);
	}
	static <T> Specification<T> where(Specification<T> spec) {
		return Specifications.where(spec);
	}
	default Specification<T> and(Specification<T> other) {
		return Specifications.composed(this, other, AND);
	}
	default Specification<T> or(Specification<T> other) {
		return Specifications.composed(this, other, OR);
	}
    //上面几个方法中,Specifications是该接口的实现类,可能适用于语句的连接,方法都过期了,我没用过
	@Nullable
	Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder);
    //需要实现改方法,默认绑定了3个参数,root和criteriaBuilder都十分有用
    /*网上看到有人这么总结(其实就是上面的参考链接~)
        Root:查询哪个表
        CriteriaQuery:查询哪些字段,排序是什么
        CriteriaBuilder:字段之间是什么关系,如何生成一个查询条件,每一个查询条件都是什么方式
        Predicate(Expression):单独每一条查询条件的详细描述*/
}

  1. JpaSpecificationExecutor接口:是复杂查询的主要入口,传入Specification对象

    这个接口里的方法全部是围绕着上面的接口Specification写的

    想使用Specification对象构造复杂查询语句,Dao层需要先继承该接口,然后实现Specification的toPredicate方法。

import org.springframework.data.jpa.domain.Specification;

public interface JpaSpecificationExecutor<T> {
	Optional<T> findOne(@Nullable Specification<T> spec);
	List<T> findAll(@Nullable Specification<T> spec);
	Page<T> findAll(@Nullable Specification<T> spec, Pageable pageable);//分页,推荐使用
	List<T> findAll(@Nullable Specification<T> spec, Sort sort);
	long count(@Nullable Specification<T> spec);
}

  1. JpaRepositoryImplementation接口(最佳partner):查看过JPA提供的接口我们可以发现,继承JpaRepositoryImplementation 将是我们最好的选择!既能使用JpaRepository的自带方法,又能使用specification自己构造查询,最全解决方案~接口定义如下:
@NoRepositoryBean
public interface JpaRepositoryImplementation<T, ID> extends JpaRepository<T, ID>, JpaSpecificationExecutor<T> {...}

7.2.Criteria 查询原理

是一种类型安全和更面向对象的查询

  • 基本对象的构建
  1. 通过EntityManager的getCriteriaBuilder或EntityManagerFactory的getCriteriaBuilder方法可以得到CriteriaBuilder对象(该对象由hibernate提供)
  2. 通过调用CriteriaBuilder的createQuery或createTupleQuery方法可以获得CriteriaQuery的实例
  3. 通过调用CriteriaQuery的from方法(方法定义在AbstractQuery,但是由CriteriaQueryImpl实现)可以获得Root实例
  • 过滤条件
  1. 过滤条件会被应用到SQL语句的FROM子句中。在criteria 查询中,查询条件通过Predicate或Expression实例应用到CriteriaQuery对象上。
  2. 这些条件使用 CriteriaQuery .where 方法应用到CriteriaQuery 对象上
  3. CriteriaBuilder也作为Predicate实例的工厂,通过调用CriteriaBuilder 的条件方法( equal,notEqual, gt, ge,lt, le,between,like等)创建Predicate对象。
  4. 复合的Predicate 语句可以使用CriteriaBuilder的and, or andnot 方法构建

7.3.Criteria查询

使用条件

  • Dao层需要继承JpaSpecificationExecutor接口,但前面我也介绍了,推荐继承JpaRepositoryImplementation接口 ,这里我也继承它!
@Repository
public interface FairyAdminDao extends JpaRepositoryImplementation<FairyAdmin,Integer> {//两个参数,一个是对应的实体类,一个是该实体类主键的类型}

7.4.综合查询示例

//测试Criteria查询
    public void criteriaQueryTest() {

        //1.新建排序
        Sort sort  =  Sort.by(Sort.Direction.DESC,"adminUsername");
        //2.新建分页
        PageRequest pageRequest = PageRequest.of(0, 5, sort);
        //3.实现接口方法specification,添加条件
        Specification<FairyAdmin> specification = new Specification<FairyAdmin>(){
            @Override
            public javax.persistence.criteria.Predicate toPredicate(Root<FairyAdmin> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                //3.1.混合条件查询
                Path<String> path1 = root.get("adminUsername");//注意String,一定要类型匹配
                //Path path2 = root.get("adminPassword");//可以像下面直接写到参数里
                Predicate predicate = cb.and(cb.like(path1, "%yoko%"),cb.like(root.get("adminPassword").as(String.class),"%456%"));

                //3.2.多表查询
                //Join join = root.join("catList", JoinType.INNER);
                Join<FairyAdmin, FairyCat> join = root.join(root.getModel().getList("catList",FairyCat.class), JoinType.INNER);
                Path<String> catName = join.get("catName");
                Predicate predicate2 = cb.and(cb.like(path1, "%yoko%"),cb.like(root.get("adminPassword").as(String.class),"%456%"),cb.like(catName,"%cat1%"));
                //输3.2.1出语句...inner join tab_cat c on adminId=c.admin_adminId where (adminUsername like ?) and (adminPassword like ?) and (c.catName like ?) order by adminUsername desc limit ?,?

                //return predicate2;

                //3.3.使用CriteriaQuery直接设置条件,不再需要返回值
                query.where(predicate2);//这里可以设置任意条查询条件
                return null;//这种方式使用JPA的API设置了查询条件,所以不需要再返回查询条件Predicate给Spring Data Jpa,故最后return null
            }
        };
        //4.执行查询
        List<FairyAdmin> list = fairyAdminRepository.findAll(specification, pageRequest).getContent();
        //list.forEach(System.out::println);
        for (FairyAdmin admin: list) {
            System.out.println(admin);
            System.out.println(admin.getCatList());//设置了级联关系,默认自动查询了
        }
    }

7.5.查询示例

  • 单条件查询
/**
 *  单条件查询
 * 需求:根据ID查询数据
 */
@GetMapping("/select1")
public void criteriaQueryTest1() {
    //3.实现接口方法specification,添加条件
    Specification<FairyAdmin> specification = new Specification<FairyAdmin>(){
        /**
         * @return Predicate:定义了查询条件
         * @param root  root:根对象。封装了查询条件的对象
         * @param query query:定义了一个基本的查询.一般不
        使用
         * @param cb cb:创建一个查询条件
         */
        @Override
        public javax.persistence.criteria.Predicate toPredicate(Root<FairyAdmin> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
            Predicate pre = cb.equal(root.get("adminId"),4);
            //3.3.使用CriteriaQuery直接设置条件,不再需要返回值
            query.where(pre);//这里可以设置任意条查询条件
            return null;//这种方式使用JPA的API设置了查询条件,所以不需要再返回查询条件Predicate给Spring Data Jpa,故最后return null
        }
    };
    //4.执行查询
    List<FairyAdmin> list = fairyAdminRepository.findAll(specification);
    for (FairyAdmin admin: list) {
        System.out.println(admin);
        System.out.println(admin.getCatList());//设置了级联关系,默认自动查询了
    }
}
  • 多条件查询 方式一
/**
 *  多条件查询 方式一
 * 需求: 使用ID和用户名进行搜索
 */
@GetMapping("/select2")
public void criteriaQueryTest2() {
    //3.实现接口方法specification,添加条件
    Specification<FairyAdmin> specification = new Specification<FairyAdmin>(){
        /**
         * @return Predicate:定义了查询条件
         * @param root root:根对象。封装了查询条件的对象
         * @param query: query:定义了一个基本的查询.一般不
        使用
         * @param cb cb:创建一个查询条件
         */
        @Override
        public javax.persistence.criteria.Predicate toPredicate(Root<FairyAdmin> root, CriteriaQuery<?> query, CriteriaBuilder cb) {

            List<Predicate> list = new ArrayList<>();
            list.add(cb.equal(root.get("adminId"),3));

            list.add(cb.equal(root.get("adminUsername"), "admin2"));
            //此时条件之间是没有任何关系的。
            Predicate[] arr = new Predicate[list.size()];
            return cb.and(list.toArray(arr));
        }
    };
    //4.执行查询
    List<FairyAdmin> list = fairyAdminRepository.findAll(specification);
    for (FairyAdmin admin: list) {
        System.out.println(admin   +"|-------------|");
        System.out.println(admin.getCatList() +"|-------------|");//设置了级联关系,默认自动查询了
    }
}
  • 多条件查询 方式二
/**
*  多条件查询 方式二
* 需求: 使用ID和用户名进行搜索
*/
@GetMapping("/select3")
public void criteriaQueryTest3() {
	 //3.实现接口方法specification,添加条件
	 Specification<FairyAdmin> specification = new Specification<FairyAdmin>(){
	     /**
	      * @return Predicate:定义了查询条件
	      * @param root root:根对象。封装了查询条件的对象
	      * @param query query:定义了一个基本的查询.一般不
	     使用
	      * @param cb cb:创建一个查询条件
	      */
	     @Override
	     public javax.persistence.criteria.Predicate toPredicate(Root<FairyAdmin> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
	         return cb.or(cb.equal(root.get("adminId"),3),cb.equal(root.get("adminUsername"), "admin2"));
	     }
	 };
	 //4.执行查询
	 List<FairyAdmin> list = fairyAdminRepository.findAll(specification);
	 for (FairyAdmin admin: list) {
	     System.out.println(admin   +"|-------------|");
	     System.out.println(admin.getCatList() +"|-------------|");//设置了级联关系,默认自动查询了
	 }
}
  • 分页
/**
*
* 需求:查询admin用户,并且做分页处理
*/
@GetMapping("/select4")
public void criteriaQueryTest4() {
   //条件
   Specification<FairyAdmin> spec = new Specification<FairyAdmin>() {
       @Override
       public Predicate toPredicate(Root<FairyAdmin> root,
                                    CriteriaQuery<?> query, CriteriaBuilder cb) {
           return cb.like(root.get("adminUsername").as(String.class), "%ad%");
       }
   };
   //分页
   Pageable pageable = PageRequest.of(0, 10);
   Page<FairyAdmin> page = this.fairyAdminRepository.findAll(spec, pageable);
   System.out.println("总条数:"+page.getTotalElements());
   System.out.println("总页数:"+page.getTotalPages());
   List<FairyAdmin> list = page.getContent();
   for (FairyAdmin admin : list) {
       System.out.println(admin +"|-------------|");
       System.out.println(admin.getCatList() +"|-------------|");
   }
}
  • 排序
/**
*
* 需求:查询数据库中ad姓的用户,并且根据用户 id 做倒序排序
*/
@GetMapping("/select5")
public void criteriaQueryTest5() {
   //条件
   Specification<FairyAdmin> spec = new Specification<FairyAdmin>() {
       @Override
       public Predicate toPredicate(Root<FairyAdmin> root,
                                    CriteriaQuery<?> query, CriteriaBuilder cb) {
           return cb.like(root.get("adminUsername").as(String.class),
                   "%ad%");
       }
   };
   //排序
   Sort sort = Sort.by(Sort.Direction.DESC,"adminId");
   List<FairyAdmin> list = this.fairyAdminRepository.findAll(spec, sort);
   for (FairyAdmin admin : list) {
       System.out.println(admin + "|-------------");
   }
}
  • 分页加排序
/**
 *
 * 需求:查询数据库中ad姓的用户,做分页处理,并且根据用户 id 做倒序排序
 */
@GetMapping("/select6")
public void criteriaQueryTest6() {
    //排序等定义
    Sort sort = Sort.by(Sort.Direction.DESC,"adminId");
    //分页的定义
    Pageable pageable = PageRequest.of(0, 10,sort);

    //查询条件
    Specification<FairyAdmin> spec = new Specification<FairyAdmin>() {
        @Override
        public Predicate toPredicate(Root<FairyAdmin> root,
                                     CriteriaQuery<?> query, CriteriaBuilder cb) {
            return cb.like(root.get("adminUsername").as(String.class),
                    "%ad%");
        }
    };
    Page<FairyAdmin> page = this.fairyAdminRepository.findAll(spec, pageable);
    System.out.println("总条数:"+page.getTotalElements());
    System.out.println("总页数:"+page.getTotalPages());
    List<FairyAdmin> list = page.getContent();
    for (FairyAdmin admin : list) {
        System.out.println(admin);
    }
}

八、排序和分页

  • 方式一:直接构造Sort对象排序
 //方式一:多字段排序查询,无法自定义顺序,输出语句...order by adminId desc, adminUsername desc
Sort sortMulti = Sort.by(Sort.Direction.DESC,"adminId","adminUsername");//可多字段
Sort sort = Sort.by(Sort.Direction.DESC,"adminId");//单字段
  • 方式二:构造多个sort对象,并合并
 //方式二:多字段排序查询,可自定义顺序,x输出语句:...order by adminUsername asc, adminId desc
Sort sort2 = Sort.by(Sort.Direction.DESC,"adminId");//第一个参数,排序类型:ASC/DESC,第二个参数:按照排序的字段,可以设置多个
Sort sort1 = Sort.by(Sort.Direction.ASC,"adminUsername");
final Sort mergeSort = sort1.and(sort);
  • 方式三:先构造多个Order对象,再构造Sort对象
//方式三:多字段排序查询,先创建order对象,再构造sort
List<Sort.Order> orders = new ArrayList<Sort.Order>();
Sort.Order order1 = new Sort.Order(Sort.Direction.DESC,"adminId");
Sort.Order order2 = new Sort.Order(Sort.Direction.ASC,"adminUsername");
//可以直接单对象创建
Sort orderSort = Sort.by(order2);//...order by adminUsername asc
//可以使用orders列表创建
orders.add(order1);
orders.add(order2);
Sort orderListSort = Sort.by(orders);//...order by adminId desc, adminUsername desc

--------------------------分页-----------------------------------------

  • 示例

//需要注意的是:page从0开始,不是从1开始!
PageRequest pageRequest = PageRequest.of(0,5, orderListSort);//最后一个参数是排序对象
Page<FairyAdmin> content = fairyAdminRepository.findAll(pageRequest);
if (content.hasContent()) {
    System.out.println("总记录数:"+content.getTotalElements());
    System.out.println("总页数:"+content.getTotalPages());
    System.out.println("当前页:"+(content.getPageable().getPageNumber()+1));
    System.out.println("当前页面大小:"+content.getPageable().getPageSize());
    List<FairyAdmin> list = content.getContent();
    list.forEach(System.out::println);
    System.out.println(content);
}
  • 完整示例
//排序和分页
public void sortAndPageSearchTest() {
    //排序的几种方式

    //方式一:多字段排序查询,无法自定义顺序,输出语句...order by adminId desc, adminUsername desc
    //Sort sort = new Sort(Sort.Direction.DESC,"adminId","adminUsername");

    //方式二:多字段排序查询,可自定义顺序,x输出语句:...order by adminUsername asc, adminId desc
    Sort sort = Sort.by(Sort.Direction.DESC,"adminId");//第一个参数,排序类型:ASC/DESC,第二个参数:按照排序的字段,可以设置多个
    Sort sort1 = Sort.by(Sort.Direction.ASC,"adminUsername");
    final Sort mergeSort = sort1.and(sort);

    //方式三:多字段排序查询,先创建order对象,再构造sort
    List<Sort.Order> orders = new ArrayList<Sort.Order>();
    Sort.Order order1 = new Sort.Order(Sort.Direction.DESC,"adminId");
    Sort.Order order2 = new Sort.Order(Sort.Direction.ASC,"adminUsername");
    //可以直接单对象创建
    Sort orderSort = Sort.by(order2);//...order by adminUsername asc
    //可以使用order列表创建
    orders.add(order1);
    orders.add(order2);
    Sort orderListSort = Sort.by(orders);//...order by adminId desc, adminUsername desc


    //需要注意的是:page从0开始,不是从1开始!
    PageRequest pageRequest = PageRequest.of(0,5, orderListSort);
    Page<FairyAdmin> content = fairyAdminRepository.findAll(pageRequest);
    if (content.hasContent()) {
        System.out.println("总记录数:"+content.getTotalElements());
        System.out.println("总页数:"+content.getTotalPages());
        System.out.println("当前页:"+(content.getPageable().getPageNumber()+1));
        System.out.println("当前页面大小:"+content.getPageable().getPageSize());
        List<FairyAdmin> list = content.getContent();
        list.forEach(System.out::println);
        System.out.println(content);
    }
    System.out.println("==========================================");
}

你可能感兴趣的:(SpringData,Jpa)