SpringDataJpa之动态查询 Example与Specification

动态查询之Example

官方文档介绍

The Query by Example API consists of three parts:

  • Probe: The actual example of a domain object with populated fields.
  • ExampleMatcher: The ExampleMatcher carries details on how to match particular fields. It can be reused across multiple Examples.
  • Example: An Example consists of the probe and the ExampleMatcher. It is used to create the query.

Query by Example is well suited for several use cases:

  • Querying your data store with a set of static or dynamic constraints.
  • Frequent refactoring of the domain objects without worrying about breaking existing queries.
  • Working independently from the underlying data store API.

Query by Example also has several limitations:

  • No support for nested or grouped property constraints, such as firstname = ?0 or (firstname = ?1 and lastname = ?2).
  • Only supports starts/contains/ends/regex matching for strings and exact matching for other property types.
    SpringDataJpa之动态查询 Example与Specification_第1张图片

使用

        UUser user = new UUser();
		user.setUserName("e");
		user.setStatus(1);
		ExampleMatcher matcher = ExampleMatcher.matching()
				.withMatcher("username", ExampleMatcher.GenericPropertyMatchers.contains())//模糊查询匹配开头,即{userNamee}%
				;
		Example userExample = Example.of(user, matcher);
		userRepository.findAll(userExample);

上述设置将模糊匹配 userName包含‘e’,且status等于1 的全部对象
上例中设置匹配器规则,也可使用默认的匹配器规则,默认是属性不为空的就根据属性值进行查询

匹配器规则

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) + ‘%’ 模糊查询(忽略大小写)

根据用户实际需求来自定义匹配规则,来生成动态sql

ExampleMatcher matcher = ExampleMatcher.matching()
            .withMatcher("username", ExampleMatcher.GenericPropertyMatchers.startsWith())//模糊查询匹配开头,即{username}%
            .withMatcher("address" ,ExampleMatcher.GenericPropertyMatchers.contains())//全部模糊查询,即%{address}%
            .withIgnorePaths("password");//忽略字段,即不管password是什么值都不加入查询条件

一般的情况使用Example 就能满足需求了,如果遇到一些多重判断,大于等于的情况,Example就捉襟见囧了

Specification

extends JpaSpecificationExecutor

public interface CustomerRepository extends CrudRepository, JpaSpecificationExecutor {
 …
}

调用findAll方法

List findAll(Specification spec);

实现接口,设置sql拼接规则

public interface Specification {
  Predicate toPredicate(Root root, CriteriaQuery query,
            CriteriaBuilder builder);
}
Specification specification = new Specification() {
			@Override
			public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
				List predicates = new ArrayList();
				if(!StringUtils.isEmpty(userQuery.getUserName())) {
					predicates.add(criteriaBuilder.like(root.get("userName"), "%" + userQuery.getUserName() + "%"));
				}
				if(null != userQuery.getStatus()){
					predicates.add(criteriaBuilder.equal(root.get("status"), userQuery.getStatus()));
				}
				if(null != userQuery.getId()){
					predicates.add(criteriaBuilder.greaterThan(root.get("id"), userQuery.getId()));
				}
				return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
				}
			};

支持不为空时,模糊匹配,精确匹配,小于

   /**
     * 复杂动态多条件查询
     * @param username
     * @param password
     * @param id
     * @return
     */
    public List listDynamic(final String username,final String password,final Integer id){
        Specification sf = new Specification() {
            List list = new ArrayList<>();
            @Override
            public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
/*              Predicate p1 = cb.like(root.get("name").as(String.class), "%"+um.getName()+"%");
                Predicate p2 = cb.equal(root.get("uuid").as(Integer.class), um.getUuid());
                Predicate p3 = cb.gt(root.get("age").as(Integer.class), um.getAge());
                //把Predicate应用到CriteriaQuery中去,因为还可以给CriteriaQuery添加其他的功能,比如排序、分组啥的
                query.where(cb.and(p3,cb.or(p1,p2)));//where p3 and (p1 or p2)
                //添加排序的功能
                query.orderBy(cb.desc(root.get("uuid").as(Integer.class)));
                return query.getRestriction();*/
                List list = new ArrayList<>();
                if(!StringUtils.isEmpty(username)){
                    list.add(criteriaBuilder.like(root.get("username").as(String.class), "%" + username + "%"));
                }
                if(!StringUtils.isEmpty(password)){
                    list.add(criteriaBuilder.isNotNull(root.get("password").as(String.class)));
                }
                if(id!=null){
                    list.add(criteriaBuilder.greaterThanOrEqualTo(root.get("id").as(Integer.class),id));
                }
                Predicate[] pd = new Predicate[list.size()];
                criteriaQuery.where(list.toArray(pd));
                criteriaQuery.orderBy(criteriaBuilder.desc(root.get("id").as(Integer.class)));
                return criteriaQuery.getRestriction();
            }
        } ;
        return userInfoRepository.findAll(sf);
    }
    
    public Page pageDynamic(final String username,final String password,final Integer id1,
                                      final Integer id2,final Integer pageNo,final Integer pageSize){
        return userInfoRepository.findAll(new Specification() {
            @Override
            public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
                Predicate p1=null;Predicate p2=null; Predicate p3=null;
                if(StringUtils.isNotEmpty(username)){
                    p1 = criteriaBuilder.equal(root.get("username").as(String.class),username);
                }
                if(StringUtils.isNotEmpty(password)){
                    p2 = criteriaBuilder.equal(root.get("password").as(String.class), password);
                }
                if(id1!=null&&id2!=null){
                    p3 = criteriaBuilder.between(root.get("id").as(Integer.class), id1, id2);
                }
                criteriaQuery.where(criteriaBuilder.and(p1,criteriaBuilder.or(p2,p3)));
                return criteriaQuery.getRestriction();
            }
        },new PageRequest(pageNo,pageSize,new Sort(Sort.Direction.DESC,"id")));
    }
}

还支持排序、and、or逻辑的实现
参考

更为复杂的情况

jpa的entity定义规则中属性与表中字段一一对应,查询时要查询出所有的对象属性才能映射成对象,不能只能使用map对象去接收了,多表之间的join查询,而且返回字段不全的时候,最好还是使用原生sql进行操作,这块的操作是只能这么实现

 
import org.springframework.stereotype.Repository;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;
 
@Repository
public class EntityManagerDAO {
 
    @PersistenceContext
    private EntityManager entityManager;
 
 
       
     /**
     * 人员列表排序
     * @return
     */
    @Transactional
    public List listUser(){
        String sql = "select a.create_time createTime," +
                "a.mobilephone phoneNum," +
                "a.email email,a.uid uid," +
                "a.enabled enabled," +
                "c.id_number idNumber," +
                " (case b.`status` when 1 then 1 else 0 end) status " +
                "from tbl_sys_user a " +
                "LEFT JOIN user_high_qic b on a.uid=b.u_id" +
                "LEFT JOIN user_qic c on a.uid=c.uid " +
                "ORDER BY status desc";
 
        SQLQuery sqlQuery = entityManager.createNativeQuery(sql).unwrap(SQLQuery.class);
        Query query = 
     sqlQuery.setResultTransformer(Transformers.aliasToBean(BackstageUserListDTO.class));
        List list = query.list();
        entityManager.clear();
        return list;
    }
}

参考链接

你可能感兴趣的:(Java)