微服务框架【第5期】--SpringBoot整合SpringDataJpa进阶
微服务框架【第5期】--SpringBoot整合SpringDataJpa进阶
导读:
大家好,我是老田。今天我们继续学习SpringDataJpa进阶内容。主要涉及SpringDataJpa的分页查询和排序,动态多条件查询。
1.JPA分页和排序
在SpringDataJpa的Dao类上我们一般会继承JpaRepository接口,而JpaRepository接口继承了PagingAndSortingRepository接口。PagingAndSortingRepository接口中定义实现了一组用于分页排序的相关方法。
分页
Pageable
是一个由 SpringDataJpa 定义的接口,它拥有一个实现 PageRequest。让我们看看如何创建一个 PageRequest。
Pageable pageable = PageRequest.of(0, 10);
Page<Employee> page = repository.findAll(pageable);
// 也可以合并
Page<Employee> page = repository.findAll(PageRequest.of(0, 10));
排序
Spring Data JPA 提供一个Sort
对象提供排序机制。让我们看一下排序的方式。
repository.findAll(Sort.by("fistName"));
repository.findAll(Sort.by("fistName").ascending().and(Sort.by("lastName").descending());
同时排序和分页
Pageable pageable = PageRequest.of(0, 20, Sort.by("firstName"));
Pageable pageable = PageRequest.of(0, 20, Sort.by("fistName").ascending().and(Sort.by("lastName").descending());
2.按例查询
Example:按例查询(QBE)是一种用户界面友好的查询技术。它允许动态创建查询,并且不需要编写包含字段名称的查询。实际上,按示例查询不需要使用特定的数据库的查询语言来编写查询语句。
Example api的组成
Probe: 含有对应字段的实例对象。
ExampleMatcher:ExampleMatcher携带有关如何匹配特定字段的详细信息,相当于匹配条件。
Example:由Probe和ExampleMatcher组成,用于查询。
Example的限制
属性不支持嵌套或者分组约束,比如这样的查询 firstname = ?0 or (firstname = ?1 and lastname = ?2)
灵活匹配只支持字符串类型,其他类型只支持精确匹配
最简单的按例查询
用example可以最快速的完成支持所有参数的筛选功能,像这样的代码:
Example<User> example = Example.of(user);
Example会将为null的字段自动过滤掉,不会作为筛选条件,但是使用这种方式会遇到一个问题,它没有办法实现 id > startId && id < endId 这样的操作。
自定匹配器规则
ExampleMatcher实例查询三要素
实体对象:在ORM框架中与Table对应的域对象,一个对象代表数据库表中的一条记录,如上例中User对象,对应user表。在构建查询条件时,一个实体对象代表的是查询条件中的“数值”部分。如:要查询姓“X”的客户,实体对象只需要存储条件值“X”。
匹配器:ExampleMatcher对象,它是匹配“实体对象”的,表示了如何使用“实体对象”中的“值”进行查询,它代表的是“查询方式”,解释了如何去查的问题。如:要查询姓“X”的客户,即姓名以“X”开头的客户,该对象就表示了“以某某开头的”这个查询方式,如上例中:withMatcher(“userName”, GenericPropertyMatchers.startsWith())
实例:即Example对象,代表的是完整的查询条件。由实体对象(查询条件值)和匹配器(查询方式)共同创建。最终根据实例来findAll即可。
@Test
public void contextLoads() {
User user = new User();
user.setUsername("李");
user.setAddress("北");
user.setPassword("123456");
ExampleMatcher matcher = ExampleMatcher.matching()
.withMatcher("username", ExampleMatcher.GenericPropertyMatchers.startsWith())//模糊查询匹配开头,即{username}%
.withMatcher("address" ,ExampleMatcher.GenericPropertyMatchers.contains())//全部模糊查询,即%{address}%
.withIgnorePaths("password");//忽略字段,即不管password是什么值都不加入查询条件
Example<User> example = Example.of(user ,matcher);
List<User> list = userRepository.findAll(example);
System.out.println(list);
}
Matching生成的语句说明
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.Criteria API
Criteria 查询是以元模型的概念为基础的,元模型是为具体持久化单元的受管实体定义的,这些实体可以是实体类,嵌入类或者映射的父类。
CriteriaQuery接口:代表一个specific的顶层查询对象,它包含着查询的各个部分,比如:select 、from、where、group by、order by等。注意:CriteriaQuery对象只对实体类型或嵌入式类型的Criteria查询起作用
Root接口:代表Criteria查询的根对象,Criteria查询的查询根定义了实体类型,能为将来导航获得想要的结果,它与SQL查询中的FROM子句类似
Root实例是类型化的,且定义了查询的FROM子句中能够出现的类型
查询根实例能通过传入一个实体类型给 AbstractQuery.from方法获得。
Criteria查询,可以有多个查询根。
AbstractQuery是CriteriaQuery 接口的父类,它提供得到查询根的方法。
CriteriaBuilder接口:用来构建CritiaQuery的构建器对象
Predicate:一个简单或复杂的谓词类型,其实就相当于条件或者是条件组合
示例代码
@Service
public class ItemServiceImpl implements ItemService {
//利用entitymanager构建Criteria查询
//其实和sql一样,就是换成了面向对象的方式。这种方式的好处就是可以防止sql拼写错误。当然这种方式也不是特别直观
@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)));
}
//where()拼接查询条件
query.where(predicatesList.toArray(new Predicate[predicatesList.size()]));
TypedQuery<Item> typedQuery = entityManager.createQuery(query);
List<Item> resultList = typedQuery.getResultList();
return resultList;
}
}
如果每个动态查询的地方都这么写,那就。感觉太麻烦了.
4.JpaSpecificationExecutor
SpringData JPA 为了实现 "Domain Driven Design" 中的规范概念,提供了一系列的 Specification 接口,其中最常用的便是 :JpaSpecificationExecutor。
使用 SpringData JPA 构建复杂查询(join操作,聚集操作等等)都是依赖于 JpaSpecificationExecutor 构建的 Specification 。
public interface Specification<T> {
Predicate toPredicate(Root<T> var1, CriteriaQuery<?> var2, CriteriaBuilder var3);
}
这个方法的参数和返回值都是JPA标准里面定义的对象。
在Dao层继承JpaSpecificationExecutor,这个接口是不需要再去实现的,到了Repository层就行,再对此进行扩充(比Mybatis简单多了)
//JpaRepository定义了简单的增删改查 简单分页
//JpaSpecificationExecutor 定义了分页 和多条件动态查询(重点是使Dao层支持CQL)
public interface UserDao extends JpaRepository<User,Integer>, JpaSpecificationExecutor {
}
示例代码
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserDaoTest {
@Autowired
private UserDao ud;
@Test
public void userDaoTestDemo(){
//多条件动态查询 比如说有一个表单 里边有一个搜索条件列表 包含用户名 性别 info
User user = new User();
user.setUsername("donghao");
user.setSex(0);
user.setInfo("董老了一岁");
//多条件查询中 JPA提供了好几种方式
//1.最简单的
//Example<User> example = Example.of(user);//有值的属性 会充当条件 没有值的属性忽略
//System.out.println(ud.findAll(example));
//但是有问题 第一个案例做完以后发现全部都是等值
//另外 能不能写 大于 小于...... 不能
//2.ExampleMatcher自定义匹配规则,但是 对非字符串只能怪精确匹配 = 不能使用 > < 等等....
//ExampleMatcher exampleMatcher = ExampleMatcher.matching().withMatcher("username",ExampleMatcher.GenericPropertyMatchers.contains());
//Example<User> example = Example.of(user,exampleMatcher);
//System.out.println(ud.findAll(example));
//3.就是Hibernate中 CQL Criteria是一个完全面向对象的查询 针对的就是类和对象及属性
Specification specification = new Specification() {
@Override
public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder criteriaBuilder) {
//构建规则有两种方式
//一种是利用CriteriaQuery
//一种是利用CriteriaBuilder
//创建一个过滤规则集合
List<Predicate> pres = new ArrayList<Predicate>();
if(null!=user.getUsername()&&!"".equals(user.getUsername())){
Predicate p1= criteriaBuilder.like(root.get("username").as(String.class),"%"+user.getUsername());
pres.add(p1);
}
if(null!=user.getSex()){
Predicate p2= criteriaBuilder.gt(root.get("sex").as(Integer.class),user.getSex());
pres.add(p2);
}
Predicate[] p = new Predicate[pres.size()];
return criteriaBuilder.and(pres.toArray(p));
}
};
Pageable pageable = PageRequest.of(1,1);
System.out.println(ud.findAll(specification,pageable));
}
}
博观而约取,厚积而薄发!
--END--