参考1:Spring-Data-JPA criteria 查询
参考2: 【原创】纯干货,Spring-data-jpa详解,全方位介绍。
spring data jpa介绍
1、什么是JPA
JPA(Java Persistence API)是Sun官方提出的Java持久化规范。它为Java开发人员提供了一种对象/关联映射工具来管理Java应用中的关系数据。
Spring Data JPA 是 Spring 基于 ORM 框架(例如:Hibernate,mybatis)、JPA 规范的基础上封装的一套JPA应用框架,可使开发者用极简的代码即可实现对数据的访问和操作。它提供了包括增删改查等在内的常用功能,且易于扩展!学习并使用 Spring Data JPA 可以极大提高开发效率!
spring data jpa让我们解脱了DAO层的操作,基本上所有CRUD都可以依赖于它来实现
2、简单查询
基本查询也分为两种,一种是spring data默认已经实现,一种是根据查询的方法来自动解析成SQL。
spring data jpa 默认预先生成了一些基本的CURD的方法,例如:增、删、改等等
1、第一种自带的:
public interface ItemRepository extends JpaRepository<Item, Integer>, JpaSpecificationExecutor<Item> {
//空的,可以什么都不用写
}
@Test
public void test1() throws Exception {
Item item = new Item();
itemRepository.save(item);
List<Item> itemList = itemRepository.findAll();
Item one = itemRepository.findOne(1);
itemRepository.delete(item);
long count = itemRepository.count();
}
2、自定义简单查询
public interface ItemRepository extends JpaRepository<Item, Integer>, JpaSpecificationExecutor<Item> {
// 根据查询的方法名字来自动解析成SQL
Item findByItemName(String itemName);
List<Item> findByItemNameLike(String itemName);
Long deleteByItemId(Integer id);
List<Item> findByItemNameLikeOrderByItemNameDesc(String itemName);
}
具体的关键字,使用方法和生产成SQL如下表所示
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 | 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) |
3、分页查询
// 可以直接用,不需要在 ItemRepository 中写这个方法
Page<Item> findALL(Pageable pageable);
@Test
public void test1() throws Exception {
int page=1,size=10;
// 根据id降序排序
Sort sort = new Sort(Sort.Direction.DESC, "id");
Pageable pageable = new PageRequest(page, size, sort);
Page<Item> pageResult = itemRepository.findALL(pageable);
List<Item> itemList = pageResult.getContent();
}
4、自定义SQL查询
JPQL查询
1、简要
要从 Java 代码内发出 JPQL 查询,您需要利用 EntityManager API 和 Query API 的相应方法,执行以下一般步骤:
- 使用注入或通过 EntityManagerFactory 实例获取一个 EntityManager 实例。
- 通过调用相应 EntityManager 的方法(如 createQuery),创建一个 Query 实例。
- 如果有查询参数,使用相应 Query 的 setParameter 方法进行设置。
- 如果需要,使用 setMaxResults 和/或 setFirstResult Query 的方法设置要检索的实例的最大数量和/或指定检索的起始实例位置。
- 如果需要,使用 setHint Query 的方法设置供应商特定的提示。
- 如果需要,使用 setFlushMode Query 的方法设置查询执行的刷新模式,覆盖实体管理器的刷新模式。
- 使用相应 Query 的方法 getSingleResult 或 getResultList 执行查询。
- 如果进行更新或删除操作,您必须使用 executeUpdate 方法,它返回已更新或删除的实体实例的数量。
@PersistenceContext
private EntityManager em; // 1、注入实体管理器
@SuppressWarnings("unchecked")
public Page<User> search(User user) {
String dataSql = "select t from User t where 1 = 1";
String countSql = "select count(t) from User t where 1 = 1";
if(null != user && !StringUtils.isEmpty(user.getName())) {
dataSql += " and t.name = ?1";
countSql += " and t.name = ?1";
}
// 2、创建一个 Query 实例
Query dataQuery = em.createQuery(dataSql);
Query countQuery = em.createQuery(countSql);
// 3、设置查询参数
if(null != user && !StringUtils.isEmpty(user.getName())) {
dataQuery.setParameter(1, user.getName());
countQuery.setParameter(1, user.getName());
}
// 7、执行查询总数
long count = (long) countQuery.getSingleResult();
// 7、执行查询
List<User> data = dataQuery.getResultList();
// 返回带分页的Page对象
Page<User> result = new PageImpl(data, pageable, count);
return result;
}
// count
Query queryTotal = entityManager.createQuery(countHql);
Long count = (Long)queryTotal.getSingleResult();
// list
Query query = entityManager.createQuery(listHql);
// 分页信息
query.setFirstResult(pageable.getPageNumber() * pageable.getPageSize());
query.setMaxResults(pageable.getPageSize());
// 执行查询
List resultMap = query.getResultList();
Page<EquipTransfer> result = new PageImpl(resultMap, pageable, count);
return result;
2、JPQL 概念:
select u from User u
select u.name u.income from User u
貌似和sql没啥区别对吧, 好到这里先做个简单实例,我们现说下概念:
1、运算符
-
算术运算符:+ - * /
-
关系运算符: = > < >= <=
-
逻辑运算符:between,like,in,is null,is empty,member of ,not,and ,or
- is null :用来判断普通属性,
- is empty :用来判断集合属性,
- member of :查出所有喜欢体育的User:
select u from User u where :interest member of u.interests
(冒号加一个名字是JPQL的一种传参方式见下面的JPQL动态查询)这里的**:interests是传入的体育这个兴趣实体对象**,没错就是Java对象!
2、函数
1、字符串函数:
2、时间函数
-
ABS绝对值
-
SQRT平方根
-
MOD取模
-
SIZE集合数量(只用在where 中)
-
SQL
-- 查出有2个兴趣的人。 select u.name from User u where size(u.interests)=2
-
3、JPQL分页
和Hibernate差不多,用Query的setFirstResult和setMaxResult设置开始位置和个数
4、JPQL分组查询
group by … having
聚合函数:avg(distinct),sum(distinct),count(distinct),min(distinct),max(distinct)
5、JPQL连接查询
左连接:
select a.id,a.name,b.id,b.name from A a left join a.b b
关联查询:
select u from User u (left) join fetch u.interests i
3、JPQL动态查询
- 基于位置参数 : 用?接数字表示参数位置,如?1,?2,设置参数就query.setparameter(1,name);
- 基于名称绑定: 用:id 冒号加上一个参数名字,query.setparameter(“id”,name);
创建查询:
Query query = em.createQuery("select new com.demo.bean.Person(p.id, p.name) from Person p order by p.id desc");
4、JPQL本地化查询
-- 即直接使用sql查询
Query query = entityManager.createNativeQuery("sql 语句");
在SQL的查询方法上面使用
@Query
注解,如涉及到删除和修改在需要加上@Modifying
.也可以根据需要添加@Transactional
对事物的支持
public interface ItemRepository extends JpaRepository<Item, Integer>, JpaSpecificationExecutor<Item> {
//自定分页查询 一条查询数据,一条查询数据量
@Query(value = "select i from Item i",
countQuery = "select count(i.itemId) from Item i")
Page<Item> findall(Pageable pageable);
//nativeQuery = true 本地查询 就是使用原生SQL查询
@Query(value = "select * from item where item_id = ?1", nativeQuery = true)
Item findAllItemById(int id);
// 删除操作
@Transactional
@Modifying
@Query("delete from Item i where i.itemId = :itemId")
void deleteInBulkByItemId(@Param(value = "itemId") Integer itemId);
// #{#entityName}就是指定的@Entity,这里就是Item
@Query("select i from #{#entityName} i where i.itemId = ?1")
Item findById(Integer id);
}
5、命名查询
在实体类上使用@NameQueries注解
在自己实现的DAO的Repository接口里面定义一个同名的方法
然后就可以使用了,Spring会先找是否有同名的NamedQuery,如果有,那么就不会按照接口定义的方法来解析。
//命名查询
@NamedQueries({
@NamedQuery(name = "Item.findItemByitemPrice",
query = "select i from Item i where i.itemPrice between ?1 and ?2"),
@NamedQuery(name = "Item.findItemByitemStock",
query = "select i from Item i where i.itemStock between ?1 and ?2"),
})
@Entity
@Data
public class Item implements Serializable {
@Id
@GeneratedValue
@Column(name = "item_id")
private int itemId;
private String itemName;
private Integer itemPrice;
private Integer itemStock;
}
/**
* 这里是在domain实体类里@NamedQuery写对应的HQL
* @NamedQuery(name = "Item.findItemByitemPrice",
baseQuery = "select i from Item i where i.itemPrice between ?1 and ?2"),
* @param price1
* @param price2
* @return
*/
List<Item> findItemByitemPrice(Integer price1, Integer price2);
List<Item> findItemByitemStock(Integer stock1, Integer stock2);
那么spring data jpa是怎么通过这些规范来进行组装成查询语句呢?
Spring Data JPA框架在进行方法名解析时,会先把方法名多余的前缀截取掉,比如 find、findBy、read、readBy、get、getBy,然后对剩下部分进行解析。
假如创建如下的查询:findByUserDepUuid(),框架在解析该方法时,首先剔除 findBy,然后对剩下的属性进行解析:
- 先判断 userDepUuid (根据 POJO 规范,首字母变为小写)是否为查询实体的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,继续第二步;
- 从右往左截取第一个大写字母开头的字符串此处为Uuid),然后检查剩下的字符串是否为查询实体的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,则重复第二步,继续从右往左截取;最后假设user为查询实体的一个属性;
- 接着处理剩下部分(DepUuid),先判断 user 所对应的类型是否有depUuid属性,如果有,则表示该方法最终是根据
Doc.user.depUuid
的取值进行查询;否则继续按照步骤 2 的规则从右往左截取,最终表示根据Doc.user.dep.uuid
的值进行查询。 - 可能会存在一种特殊情况,比如 Doc包含一个 user 的属性,也有一个 userDep 属性,此时会存在混淆。可以明确在属性之间加上 “_” 以显式表达意图,比如
findByUser_DepUuid()
或者findByUserDep_uuid()
5、高级动态查询
Spring Data JPA虽然大大的简化了持久层的开发,但是在实际开发中,很多地方都需要高级动态查询**
1、Criteria API
Criteria 查询是以元模型的概念为基础的,元模型是为具体持久化单元的受管实体定义的,这些实体可以是实体类,嵌入类或者映射的父类。
CriteriaQuery接口:代表一个specific的顶层查询对象,它包含着查询的各个部分,比如:select 、from、where、group by、order by等
注意:CriteriaQuery对象只对实体类型或嵌入式类型的Criteria查询起作用
Root接口:代表Criteria查询的根对象,Criteria查询的查询根定义了实体类型,能为将来导航获得想要的结果,它与SQL查询中的FROM子句类似
1:Root实例是类型化的,且定义了查询的FROM子句中能够出现的类型。
2:查询根实例能通过传入一个实体类型给 AbstractQuery.from方法获得。
3:Criteria查询,可以有多个查询根。
4:AbstractQuery是CriteriaQuery 接口的父类,它提供得到查询根的方法。CriteriaBuilder接口:用来构建CritiaQuery的构建器对象
Predicate:一个简单或复杂的谓词类型,其实就相当于条件或者是条件组合
如果编译器能够对查询执行语法正确性检查,那么对于 Java 对象而言该查询就是类型安全的。Java™Persistence API (JPA) 的 2.0 版本引入了 Criteria API,这个 API 首次将类型安全查询引入到 Java 应用程序中,并为在运行时动态地构造查询提供一种机制。
2、JPA元模型
在JPA中,标准查询是以元模型的概念为基础的.元模型是为具体持久化单元的受管实体定义的.这些实体可以是实体类,嵌入类或者映射的父类.提供受管实体元信息的类就是元模型类.
使用元模型类最大的优势是凭借其实例化可以在编译时访问实体的持久属性.该特性使得criteria 查询更加类型安全.
如下,Item
实体类对应的元模型Item_
@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(Item.class)
public abstract class Item_ {
public static volatile SingularAttribute<Item, Integer> itemId;
public static volatile SingularAttribute<Item, String> itemName;
public static volatile SingularAttribute<Item, Integer> itemStock;
public static volatile SingularAttribute<Item, Integer> itemPrice;
}
这样的元模型不用手动创建,在Maven中添加插件,编译之后@Entity注解的类就会自动生成对应的元模型:
<!--hibernate JPA 自动生成元模型-->
<!-- 相关依赖 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-jpamodelgen</artifactId>
<version>5.2.10.Final</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.1.0.Final</version>
</dependency>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<compilerArguments>
<processor>org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor</processor>
</compilerArguments>
</configuration>
</plugin>
3、使用criteria 自定义查询
@Service
public class ItemServiceImpl implements ItemService {
@Resource
private EntityManager entityManager;
@Override
public List<Item> findByConditions(String name, Integer price, Integer stock) {
//创建CriteriaBuilder安全查询工厂
//CriteriaBuilder是一个工厂对象,安全查询的开始.用于构建JPA安全查询.
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
//创建CriteriaQuery安全查询主语句
//CriteriaQuery对象必须在实体类型或嵌入式类型上的Criteria 查询上起作用。
CriteriaQuery<Item> query = criteriaBuilder.createQuery(Item.class);
//Root 定义查询的From子句中能出现的类型
Root<Item> itemRoot = query.from(Item.class);
//Predicate 过滤条件 构建where字句可能的各种条件
//这里用List存放多种查询条件,实现动态查询
List<Predicate> predicatesList = new ArrayList<>();
//name模糊查询 ,like语句
if (name != null) {
predicatesList.add(
criteriaBuilder.and(
criteriaBuilder.like(
itemRoot.get(Item_.itemName), "%" + name + "%")));
}
// itemPrice 小于等于 <= 语句
if (price != null) {
predicatesList.add(
criteriaBuilder.and(
criteriaBuilder.le(
itemRoot.get(Item_.itemPrice), price)));
}
//itemStock 大于等于 >= 语句
if (stock != null) {
predicatesList.add(
criteriaBuilder.and(
criteriaBuilder.ge(
itemRoot.get(Item_.itemStock), stock)));
}
// 类型安全写法
if (true) {
Path<Date> createTimePath = root.get("createTime");
predicates.add(criteriaBuilder.greaterThanOrEqualTo(createTimePath.as(Date.class), new Date()));
}
//where()拼接查询条件
query.where(predicatesList.toArray(new Predicate[predicatesList.size()]));
// Group By 语句
query.groupBy(
root.get("processOrg"),
root.get("confirmOrg")
);
TypedQuery<Item> typedQuery = entityManager.createQuery(query);
// 分页信息
typedQuery.setFirstResult(pageNumber() * pageSize());
typedQuery.setMaxResults(pageSize());
// 执行查询
List<Item> resultList = typedQuery.getResultList();
return resultList;
}
}
criteriaBuilder中各方法对应的语句:
equle : filed = value
gt / greaterThan : filed > value
lt / lessThan : filed < value
ge / greaterThanOrEqualTo : filed >= value
le / lessThanOrEqualTo: filed <= value
notEqule : filed != value
like : filed like value
notLike : filed not like value
如果每个动态查询的地方都这么写,那就感觉太麻烦了.
那实际上,在使用Spring Data JPA的时候,只要我们的Repo层接口继承JpaSpecificationExecutor接口就可以使用Specification进行动态查询了,我们先看下JpaSpecificationExecutor接口:
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);
}
在这里有个很重要的接口Specification
:
public interface Specification<T> {
Predicate toPredicate(Root<T> var1, CriteriaQuery<?> var2, CriteriaBuilder var3);
}
这个接口只有一个方法,返回动态查询的数据结构,用于构造各种动态查询的SQL
4、Specification接口示例:
public Page<Item> findByConditions(String name, Integer price, Integer stock, Pageable pageable) {
Page<Item> page = itemRepository.findAll((root, criteriaQuery, criteriaBuilder) -> {
List<Predicate> predicatesList = new ArrayList<>();
//name模糊查询 ,like语句
if (name != null) {
predicatesList.add(
criteriaBuilder.and(
criteriaBuilder.like(
root.get(Item_.itemName), "%" + name + "%")));
}
// itemPrice 小于等于 <= 语句
if (price != null) {
predicatesList.add(
criteriaBuilder.and(
criteriaBuilder.le(
root.get(Item_.itemPrice), price)));
}
//itemStock 大于等于 >= 语句
if (stock != null) {
predicatesList.add(
criteriaBuilder.and(
criteriaBuilder.ge(
root.get(Item_.itemStock), stock)));
}
return criteriaBuilder.and(
predicatesList.toArray(new Predicate[predicatesList.size()]));
}, pageable);
return page;
}
另种方式:
Page<StockMaintainTask> page = stockMaintainTaskRepository.findAll(new Specification<StockMaintainTask>() {
@Override
public Predicate toPredicate(Root<StockMaintainTask> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
query.multiselect(root.get("confirmOrg"),cb.count(root.get("confirmOrg")));
query.groupBy(root.get("confirmOrg"));
return query.getRestriction();
}
},pageable);
还可以:
@Service
@Transactional
public class MySpeciServiceImpl implements MySpeciService {
@Autowired private SpeciRepository speciRepository;
@Override public Page<TestVO> findByPageAndParams(final TestVO param, int pageNumber,int pageSize) {
Pageable pageable=new PageRequest(pageNumber, pageSize); //分页信息
Specification<TestVO> spec = new Specification<TestVO>() { //查询条件构造
@Override public Predicate toPredicate(Root<TestVO> root, CriteriaQuery<?> query,CriteriaBuilder cb) {
Path<String> name = root.get("name");
Path<Integer> age = root.get("age");
Predicate p1 = cb.like(name, "%"+param.getName()+"%");
Predicate p2 = cb.lt(age, param.getAge());
Predicate p = cb.and(p1, p2);
return p;
}
};
return speciRepository.findAll(spec, pageable);
}
}
在这里因为
findAll(Specification<T> var1, Pageable var2)
方法中参数Specification<T>
是一个匿名内部类那这里就可以直接用lambda表达式直接简化代码.
这样写,就比用CriteriaBuilder安全查询工厂简单多了.
调用:
Page<Item> itemPageList = findByConditions("车", 300, null, new PageRequest(1, 10));
利用JPA的Specification<T>
接口和元模型就实现动态查询了.
那其实这样每一个需要动态查询的地方都需要写一个这样类似的findByConditions
方法,感觉也很麻烦了.当然是越简化越好.
5、Group BY分页查询
要实现下面这种查询:
select count(*) as num, name from user where name = 'liu' group by name
JpaSpecificationExecutor
接口可以来进行group by 查询,但是有新增总数字段,就满足不了
需自己使用元接口来自定义查询
//创建CriteriaBuilder安全查询工厂
//CriteriaBuilder是一个工厂对象,安全查询的开始.用于构建JPA安全查询.
CriteriaBuilder criteriaBuilder = entityManagerFactory.getCriteriaBuilder();
//创建CriteriaQuery安全查询主语句
//CriteriaQuery对象必须在实体类型或嵌入式类型上的Criteria 查询上起作用。
CriteriaQuery<MaintenanceTaskStatistics> query = criteriaBuilder.createQuery(MaintenanceTaskStatistics.class);
//Root 定义查询的From子句中能出现的类型
Root<StockMaintainTask> root = query.from(StockMaintainTask.class);
//Predicate 过滤条件 构建where字句可能的各种条件
//这里用List存放多种查询条件,实现动态查询
List<Predicate> predicatesList = new ArrayList<>();
query.multiselect(
// 查询出来的表字段
// select count(*) as num, processOrg, confirmOrg 三个字段
criteriaBuilder.count(root.get("id")),
root.get("processOrg"),
root.get("confirmOrg")
).where(
criteriaBuilder.equal(root.get("status"), StockMaintainTaskStatus.DONE)
).groupBy(
root.get("processOrg"),
root.get("confirmOrg")
);
List<MaintenanceTaskStatistics> resultList = entityManagerFactory.createEntityManager().createQuery(query).getResultList();
return resultList;
需要注意的是,
// 这个是 要查询字段的 实体,
// 类似于 select count(*) as num, name
// 改实体中要有 Long num, 和 String name 字段
CriteriaQuery<MaintenanceTaskStatistics> query = criteriaBuilder.createQuery(MaintenanceTaskStatistics.class);
// StockMaintainTask 是要查询的表的实体
// 类似于 from user where name = 'liu' group by name
// 相当于User 实体类/或者叫 User表
Root<StockMaintainTask> root = query.from(StockMaintainTask.class);
// MaintenanceTaskStatistics.java 部分代码
private static final long serialVersionUID = 1L;
private Long num;
private Organization processOrg;
private Organization confirmOrg;
public MaintenanceTaskStatistics() {
super();
}
6、Example查询
1、简单查询
Person person = new Person();
person.setFirstname("Dave");
Example<Person> example = Example.of(person);
// 执行查询
List<Person> persons = personRepository.findAll(example);
2、参数匹配
Person person = new Person();
person.setFirstname("Dave");
ExampleMatcher matcher = ExampleMatcher.matching()
.withIgnorePaths("lastname")
.withIncludeNullValues()
.withStringMatcherEnding();
Example<Person> example = Example.of(person, matcher);
// 执行查询
List<Person> persons = personRepository.findAll(example);
Matching | Logical result |
---|---|
DEFAULT (case-sensitive) | firstname = ?0 |
DEFAULT (case-insensitive) | LOWER(firstname) = LOWER(?0) |
EXACT (case-sensitive) | firstname = ?0 |
EXACT (case-insensitive) | LOWER(firstname) = LOWER(?0) |
STARTING (case-sensitive) | firstname like ?0 + '%' |
STARTING (case-insensitive) | LOWER(firstname) like LOWER(?0) + '%' |
ENDING (case-sensitive) | firstname like '%' + ?0 |
ENDING (case-insensitive) | LOWER(firstname) like '%' + LOWER(?0) |
CONTAINING (case-sensitive) | firstname like '%' + ?0 + '%' |
CONTAINING (case-insensitive) | LOWER(firstname) like '%' + LOWER(?0) + '%' |
3、配置参数
Person person = new Person();
person.setFirstname("Dave");
// 条件
ExampleMatcher matcher = ExampleMatcher.matching()
.withMatcher("firstname", endsWith())
.withMatcher("lastname", startsWith().ignoreCase());
}
Example<Person> example = Example.of(person, matcher);
// 执行查询
List<Person> persons = personRepository.findAll(example);
4、lambdas形式
Person person = new Person();
person.setFirstname("Dave");
// 条件
// lambdas形式
ExampleMatcher matcher = ExampleMatcher.matching()
.withMatcher("firstname", match -> match.endsWith())
.withMatcher("firstname", match -> match.startsWith());
}
Example<Person> example = Example.of(person, matcher);
// 执行查询
List<Person> persons = personRepository.findAll(example);
可在 PersonRepository 里边直接传入对象来查询
public interface PersonRepository extends JpaRepository<Person, String> { … }
public class PersonService {
@Autowired PersonRepository personRepository;
public List<Person> findPeople(Person probe) {
return personRepository.findAll(Example.of(probe));
}
}