Spring Boot JPA--Query

在SpringBoot中,当我们实现JpaRepository接口,就可以对实例进行基本的CRUD操作。同时SpringBoot也提供了以下几种方式来满足一些复杂的查询需求。

  • @Query注解
  • Named Queries
  • Specification and JPA Criteria API

JpaRepository

我们先来看下JpaRepository的实现

public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> 
  • T表示实体
  • ID 表示实体的主键

    JpaRepository的继承层次如下

    可以看到JpaRepository实现了基本的CRUD操作,同时由于继承了PagingAndSortingRepository接口,也拥有了分页排序的功能。

下面再来详细说明下上述几种查询方式,我们先定义一个Entity

@Entity
@Getter
@Setter
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private String name;
    private String address;
    private Date birthday;
}

@Query

JPQL与Native Query

@Query支持两种类型的SQL,一种是JPQL,一种是native query,即数据库原生的SQL

@Query中的nativeQuery参数用来标识这两种查询方式,为true表示使用native query,默认为false

两种其中一个区别就是,JPQL将Entity(标有@Entity的类)的名称作为表名,而native query的表名就是数据库中的表名。

排序

对于JPA原生提供的方法,可以直接通过Sort来进行排序

@Repository
public interface UserDao extends JpaRepository<User, Integer>{
}

// 按照name进行倒叙排序
userDao.findAll(new Sort(Sort.Direction.DESC, "name"))

但是native query不支持Sort,只能在SQL中使用order by

@Query(value = "select * from user order by name", nativeQuery = true)
List<User> getUsersOrderByName();

分页

对于JPQL查询,可以直接使用Pageable进行分页

@Query("select u from User order by id")
Page<User> findUsersWithPageable(Pageable pageable);

对于native query,可以通过参数countQuery来实现分页的功能

@Query(value = "select * from user order by id",
            countQuery = "select count(*) from user order by id",nativeQuery = true)
Page<User> findWithPage(Pageable pageable);

参数传递

我们可以通过两种方式将方法中的参数传递到查询中

  1. 通过参数顺序
  2. 通过参数名称

通过参数顺序

不论是JPQL还是native query,Spring都会按照方法声明中参数出现的顺序传递给查询

@Query("select u from User u where u.name = ?1 and u.address = ?2")
List<User> findUserByNameAndAddress(String name, String address);

@Query("select u from user u where u.name = ?1 and u.address = ?2",nativeQuery=true)
List<User> findUserByNameAndAddress(String name, String address);

通过参数名称

另外我们还可以@Param来传递参数,JPQL与native query的使用姿势一致

@Query("select u from User u where u.name = :name and u.address = :address")
List<User> findUserByNameAndAddressParam(@Param("name") String name, 
                                             @Param("address") String address);

@Query("select u from user u where u.name = :name and u.address = :address",nativeQuery=true)
List<User> findUserByNameAndAddressParam(@Param("name") String name, 
                                             @Param("address") String address);                                             

Named Querys

Spring JPA支持根据方法名称自动生成查询语句

// 等同于执行SQL:select * from user where name like "%?1%" and address like "%?2%"
List<User> findUserByNameLikeAndAddressLike(String name, String address);

是不是很神奇,Spring JPA是怎么实现这个功能的我会在另外一篇博客中进行展开

Specification and JPA Criteria API

@Query和Named Querys两种方式比较方便简单,但是对于一些动态查询或者更加灵活的查询就不是很适用了,此时就需要Criteria API

JPA Criteria API

@Service
public class UserQueryService {
    @Resource
    private EntityManager entityManager;

    public List<User> findByName(String name){
        CriteriaBuilder builder = entityManager.getCriteriaBuilder();
        //1. 创建对应entity的query
        CriteriaQuery<User> query = builder.createQuery(User.class);
        //2. 查询对应的entity
        Root<User> root = query.from(User.class);

         //3. 查询条件
        Predicate predicate = builder.equal(root.get("name"), name);
        query.where(predicate);
        //4. 执行查询语句
        return entityManager.createQuery(query.select(root)).getResultList();
    }
}

Specification

Specification在Criteria API上在封装了一层,让SQL语句模块化,并且可读性更强

1.首先需要实现JpaSpecificationExecutor接口

public interface UserDao extends JpaRepository<User, Integer>, JpaSpecificationExecutor {}

2.创建不同的查询条件

public class UserSpecifications {
    // 根据name查询
    public static Specification<User> userHasName(String name){
        return (Specification<User>) (root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("name"),name);
    }

    // address不为null
    public static Specification<User> userAddressNotNull(){
        return ((root, query, criteriaBuilder) -> criteriaBuilder.isNotNull(root.get("address")));
    }

    // 根据birthday查询
    public static Specification<User> userBirthday(){
        LocalDate today = LocalDate.now();
        return (root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("birthday"),today);
    }
}

3.执行语句

userDao.findAll(userHasName("").and(userAddressNotNull()).and(userBirthday())

通过示例可以看到,Specification可以组合不同的Specification进行查询

##参考文献


Reprint please specify: wbl Spring Boot JPA--Query

Previous
Spring Boot JPA--命名策略 Spring Boot JPA--命名策略
废话不多说,先来看下例子,首先定义一个Entity @Entity @Getter @Setter public class User { @Id @GeneratedValue(strategy = GenerationT
2020-04-11
Next
Spring Boot JPA--DataSource Spring Boot JPA--DataSource
周所周知,SpringBoot可以非常方便的实现数据库的连接访问。只要配置数据库的相关属性,定义对应的Entity以及Repository就可以实现对数据库的增删改查。 properties spring.datasource.url=jd
2020-03-31