`
lee1177
  • 浏览: 117745 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

spring data jpa的动态查询封装

阅读更多

最近使用spring data jpa做了两个项目,对于动态查询的不友好做了个类似hibernate的封装,记录也分享下

首先定义一个所有条件的容器,继承Specification

/**
 * 定义一个查询条件容器
 * @author lee
 *
 * @param <T>
 */
public class Criteria<T> implements Specification<T>{
	private List<Criterion> criterions = new ArrayList<Criterion>();

	public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query,
			CriteriaBuilder builder) {
		if (!criterions.isEmpty()) {
			List<Predicate> predicates = new ArrayList<Predicate>();
			for(Criterion c : criterions){
				predicates.add(c.toPredicate(root, query,builder));
			}
			// 将所有条件用 and 联合起来
			if (predicates.size() > 0) {
				return builder.and(predicates.toArray(new Predicate[predicates.size()]));
			}
		}
		return builder.conjunction();
	}
	/**
	 * 增加简单条件表达式
	 * @Methods Name add
	 * @Create In 2012-2-8 By lee
	 * @param expression0 void
	 */
	public void add(Criterion criterion){
		if(criterion!=null){
			criterions.add(criterion);
		}
	}
}

 然后是各种条件组装类,我首先做了一个接口来包装各种条件

/**
 * 条件接口
 * 用户提供条件表达式接口
 * @Class Name Criterion
 * @Author lee
 * @Create In 2012-2-8
 */
public interface Criterion {
	public enum Operator {
		EQ, NE, LIKE, GT, LT, GTE, LTE, AND, OR
	}
	public Predicate toPredicate(Root<?> root, CriteriaQuery<?> query,
			CriteriaBuilder builder);
}

 然后是针对不同类型条件处理的实现

一个是简单比较类型的处理

 

/**
 * 简单条件表达式
 * @author lee
 *
 */
public class SimpleExpression implements Criterion{
	
	private String fieldName;		//属性名
	private Object value;			//对应值
	private Operator operator;		//计算符

	protected SimpleExpression(String fieldName, Object value, Operator operator) {
		this.fieldName = fieldName;
		this.value = value;
		this.operator = operator;
	}

	public String getFieldName() {
		return fieldName;
	}
	public Object getValue() {
		return value;
	}
	public Operator getOperator() {
		return operator;
	}
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public Predicate toPredicate(Root<?> root, CriteriaQuery<?> query,
			CriteriaBuilder builder) {
		Path expression = null;
		if(fieldName.contains(".")){
			String[] names = StringUtils.split(fieldName, ".");
			expression = root.get(names[0]);
			for (int i = 1; i < names.length; i++) {
				expression = expression.get(names[i]);
			}
		}else{
			expression = root.get(fieldName);
		}
		
		switch (operator) {
		case EQ:
			return builder.equal(expression, value);
		case NE:
			return builder.notEqual(expression, value);
		case LIKE:
			return builder.like((Expression<String>) expression, "%" + value + "%");
		case LT:
			return builder.lessThan(expression, (Comparable) value);
		case GT:
			return builder.greaterThan(expression, (Comparable) value);
		case LTE:
			return builder.lessThanOrEqualTo(expression, (Comparable) value);
		case GTE:
			return builder.greaterThanOrEqualTo(expression, (Comparable) value);
		default:
			return null;
		}
	}
	
}

 一个逻辑条件计算实现

/**
 * 逻辑条件表达式 用于复杂条件时使用,如但属性多对应值的OR查询等
 * @author lee
 *
 */
public class LogicalExpression implements Criterion {
	private Criterion[] criterion; 	// 逻辑表达式中包含的表达式
	private Operator operator;		//计算符

	public LogicalExpression(Criterion[] criterions, Operator operator) {
		this.criterion = criterions;
		this.operator = operator;
	}

	public Predicate toPredicate(Root<?> root, CriteriaQuery<?> query,
			CriteriaBuilder builder) {
		List<Predicate> predicates = new ArrayList<Predicate>();
		for(int i=0;i<this.criterion.length;i++){
			predicates.add(this.criterion[i].toPredicate(root, query, builder));
		}
		switch (operator) {
		case OR:
			return builder.or(predicates.toArray(new Predicate[predicates.size()]));
		default:
			return null;
		}
	}

}

 添加一个组装工厂类

/**
 * 条件构造器
 * 用于创建条件表达式
 * @Class Name Restrictions
 * @Author lee
 */
public class Restrictions {

	/**
	 * 等于
	 * @param fieldName
	 * @param value
	 * @param ignoreNull
	 * @return
	 */
	public static SimpleExpression eq(String fieldName, Object value, boolean ignoreNull) {
		if(StringUtils.isEmpty(value))return null;
		return new SimpleExpression (fieldName, value, Operator.EQ);
	}
	
	/**
	 * 不等于
	 * @param fieldName
	 * @param value
	 * @param ignoreNull
	 * @return
	 */
	public static SimpleExpression ne(String fieldName, Object value, boolean ignoreNull) {
		if(StringUtils.isEmpty(value))return null;
		return new SimpleExpression (fieldName, value, Operator.NE);
	}

	/**
	 * 模糊匹配
	 * @param fieldName
	 * @param value
	 * @param ignoreNull
	 * @return
	 */
	public static SimpleExpression like(String fieldName, String value, boolean ignoreNull) {
		if(StringUtils.isEmpty(value))return null;
		return new SimpleExpression (fieldName, value, Operator.LIKE);
	}

	/**
	 * 
	 * @param fieldName
	 * @param value
	 * @param matchMode
	 * @param ignoreNull
	 * @return
	 */
	public static SimpleExpression like(String fieldName, String value,
			MatchMode matchMode, boolean ignoreNull) {
		if(StringUtils.isEmpty(value))return null;
		return null;
	}

	/**
	 * 大于
	 * @param fieldName
	 * @param value
	 * @param ignoreNull
	 * @return
	 */
	public static SimpleExpression gt(String fieldName, Object value, boolean ignoreNull) {
		if(StringUtils.isEmpty(value))return null;
		return new SimpleExpression (fieldName, value, Operator.GT);
	}

	/**
	 * 小于
	 * @param fieldName
	 * @param value
	 * @param ignoreNull
	 * @return
	 */
	public static SimpleExpression lt(String fieldName, Object value, boolean ignoreNull) {
		if(StringUtils.isEmpty(value))return null;
		return new SimpleExpression (fieldName, value, Operator.LT);
	}

	/**
	 * 大于等于
	 * @param fieldName
	 * @param value
	 * @param ignoreNull
	 * @return
	 */
	public static SimpleExpression lte(String fieldName, Object value, boolean ignoreNull) {
		if(StringUtils.isEmpty(value))return null;
		return new SimpleExpression (fieldName, value, Operator.GTE);
	}

	/**
	 * 小于等于
	 * @param fieldName
	 * @param value
	 * @param ignoreNull
	 * @return
	 */
	public static SimpleExpression gte(String fieldName, Object value, boolean ignoreNull) {
		if(StringUtils.isEmpty(value))return null;
		return new SimpleExpression (fieldName, value, Operator.LTE);
	}

	/**
	 * 并且
	 * @param criterions
	 * @return
	 */
	public static LogicalExpression and(Criterion... criterions){
		return new LogicalExpression(criterions, Operator.AND);
	}
	/**
	 * 或者
	 * @param criterions
	 * @return
	 */
	public static LogicalExpression or(Criterion... criterions){
		return new LogicalExpression(criterions, Operator.OR);
	}
	/**
	 * 包含于
	 * @param fieldName
	 * @param value
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	public static LogicalExpression in(String fieldName, Collection value, boolean ignoreNull) {
		if(ignoreNull&&(value==null||value.isEmpty())){
			return null;
		}
		SimpleExpression[] ses = new SimpleExpression[value.size()];
		int i=0;
		for(Object obj : value){
			ses[i]=new SimpleExpression(fieldName,obj,Operator.EQ);
			i++;
		}
		return new LogicalExpression(ses,Operator.OR);
	}
}

 使用方法如下

Criteria<Event> c = new Criteria<Event>();
c.add(Restrictions.like("code", searchParam.getCode(), true));
        c.add(Restrictions.eq("level", searchParam.getLevel(), false));
        c.add(Restrictions.eq("mainStatus", searchParam.getMainStatus(), true));
        c.add(Restrictions.eq("flowStatus", searchParam.getFlowStatus(), true));
        c.add(Restrictions.eq("createUser.userName", searchParam.getCreateUser(), true));
        c.add(Restrictions.lte("submitTime", searchParam.getStartSubmitTime(), true));
        c.add(Restrictions.gte("submitTime", searchParam.getEndSubmitTime(), true));
        c.add(Restrictions.eq("needFollow", searchParam.getIsfollow(), true));
        c.add(Restrictions.ne("flowStatus", CaseConstants.CASE_STATUS_DRAFT, true));
        c.add(Restrictions.in("solveTeam.code",teamCodes, true));
eventDao.findAll(c);

 其中eventDao为继承JpaSpecificationExecutor的接口类

分享到:
评论
5 楼 coyoc 2017-10-09  
楼主封装得不错,不过部分方法是不是没实现完整,不像生产在用的吧
4 楼 lee1177 2016-09-22  
hyf_0528 写道
我单元测试了下,如果solveTeam是Event实体类多对多的属性的话,会报错?

org.springframework.dao.InvalidDataAccessApiUsageException: Illegal attempt to dereference path source [null]; nested exception is java.lang.IllegalArgumentException: Illegal attempt to dereference path source [null]
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:384)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:157)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:417)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:147)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.jpa.repository.support.LockModeRepositoryPostProcessor$LockModePopulatingMethodIntercceptor.invoke(LockModeRepositoryPostProcessor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy34.findAll(Unknown Source)

@ManyToMany?出于对性能及复杂度的考虑一直没用,之前这个封装针对@ManyToOne情况比较多。一般ManyToMany的情况都是通过中间管理表映射实体Entity来处理的。
3 楼 hyf_0528 2016-04-29  
我单元测试了下,如果solveTeam是Event实体类多对多的属性的话,会报错?

org.springframework.dao.InvalidDataAccessApiUsageException: Illegal attempt to dereference path source [null]; nested exception is java.lang.IllegalArgumentException: Illegal attempt to dereference path source [null]
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:384)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:157)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:417)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:147)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.jpa.repository.support.LockModeRepositoryPostProcessor$LockModePopulatingMethodIntercceptor.invoke(LockModeRepositoryPostProcessor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy34.findAll(Unknown Source)
2 楼 yao88611852 2016-01-21  
封装的很好,值得学习!感谢分享!  
1 楼 qewrqq 2014-06-16  
这个方法支持 集合吗? 一对多的查询 http://zhidao.baidu.com/question/135029551948887645.html?quesup2&oldq=1&sort=6 帮我看行吗?

相关推荐

    SpringDataJpa的jar包.rar

    Spring Data JPA 是Spring基于ORM框架、JPA规范封装的...学习并使用SpringDataJPA可以极大提高开发效率! 除了CRUD外,还包括如分页、排序等一些常用的功能。下面的示例代码即可完成数据保存的操作,而无需具体实现类.

    Spring Data JPA 106精讲

    Spring Data 是 Spring 的一个子...2:详解Spring Data JPA封装的各种查询方式 3:详解Spring Data JPA常用接口 4:详解Spring Data JPA各种查询方式 5:详解Spring Data JPA在web方面的用法 具体内容,可以参考大纲:

    spring4.x+springdatajpa+springmvc+maven搭建

    springdatajpa让数据访问更轻便,直接封装数据访问的代码,简化代码编写.不需要写sql语句,也不需要配置维护关系的xml文件,直接调用底层方法就能实现数据库的增删改查,功能堪称完美

    Spring Data JPA 实现多表关联查询的示例代码

    多表查询在spring data jpa中有两种实现方式,第一种是利用hibernate的级联查询来实现,第二种是创建一个结果集的接口来接收连表查询后的结果,这里介绍第二种方式,小编觉得挺不错的,现在分享给大家,也给大家做个...

    Spring-data-jpa:spring-data-jpa的封装及源代码解析

    Spring-data-jpa:spring-data-jpa的封装及源代码解析

    Spring + Spring MVC + SpringData JPA + mysql +jsp实现一个简易的个人博客系统

    • 服务层,封装复杂业务逻辑 • UserService类、StatusService类 • 控制层,Controller,URL路径映射 • UserController类、FriendshipController类、StatusController类、 CommentController类、...

    基于JPA及ASM9实现自动接口开发

    实现JPA基本数据库操作功能封装 实现基于ASM9,动态生成entity、repository、service、serviceImpl、controller相关.class 可根据库表,一键生成新增、修改删除、查询等接口 实现部分基于mybatis-plus,动态代码生成...

    Spring boot中使用Spring-data-jpa方便快捷的访问数据库(推荐)

    Spring Data JPA 是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套JPA应用框架,可使开发者用极简的代码即可实现对数据的访问和操作。这篇文章主要介绍了Spring-boot中使用Spring-data-jpa方便快捷的访问数据库...

    spring-data-projects:测试SpringData整合其他工具的项目

    SpringData提供了针对数据库(包括SQL和NOSQL)的整合方案,对Hibernate JPA、Jedis等工具的api进行高级的封装,为我们提供简单方便地操作接口。 Spring Data JPA Spring Data提供了针对数据库(包括SQL和NOSQL)的...

    SpringData.zip

    Spring Data 项目的目的是为了简化构建基于 Spring 框架应用的数据访问计数,包括非关系数据库、Map-Reduce 框架、云数据服务等等;另外也包含对关系数据库的访问支持。Spring Data 包含多个子项目:Commons - 提供...

    SpringBoot使用Spring-data-jpa简化数据访问层

    对数据库的操作无非就“增删改查”。就最为普遍的单表操作而言,除了表和字段不同外,语句都是类似的,开发人员需要写...Spring-data-jpa的出现正可以让这样一个已经很“薄”的数据访问层变成只是一层接口的编写方式。

    JPA学习大全.docx

    Spring Data JPA 是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套JPA应用框架,可使开发者用极简的代码即可实现对数据库的访问和操作。它提供了包括增删改查等在内的常用功能,且易于扩展!学习并使用 Spring ...

    Spring Boot JPA如何把ORM统一起来

    Spring Data JPA 是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套JPA应用框架,可使开发者用极简的代码即可实现对数据的访问和操作,本文给大家详细介绍了Spring Boot JPA如何把ORM统一起来,感兴趣的朋友一起...

    spring data API

    Spring Data 项目的目的是为了简化构建基于 Spring 框架应用的数据访问计数,包括非关系数据库、Map-Reduce 框架、云数据服务等等;另外也包含对关系数据库的访问支持。 Spring Data 包含多个子项目: Commons - ...

    SPPanAdmin.zip_Bootstrap Table_SPPanAdmin_Hadmin_SpringBoot JPA_

    3、系统中对springdata的查询条件Specification做了简单的封装,更加方便查询条件的灵活使用。 4、前端技术:使用Hadmin系统模版,数据表格使用bootstrap table插件,弹窗使用layer插件,日期选择使用laydate插件。...

    huyidao---123-bootdo-jpa-master.zip

    BootDo-JPA是在SpringBoot基础上搭建的一个Java基础开发平台,SpringDataJPA为数据访问层,ApacheShiro为权限授权层,Ehcahe对常用数据进行缓存。 BootDo-JPA主要定位于后台管理系统学习交流,已内置后台管理系统的...

    基于springboot的后台管理系统基本框架.zip

    系统中对springdata的查询条件Specification做了简单的封装,更加方便查询条件的灵活使用。 前端技术:使用Hadmin系统模版,数据表格使用bootstrap table插件,弹窗使用layer插件,日期选择使用laydate插件。表单...

    基于SpringBoot+shiro+ztree的权限管理后台源码+数据库文件.zip

    3、系统中对springdata的查询条件Specification做了简单的封装,更加方便查询条件的灵活使用。 4、前端技术:使用Hadmin系统模版,数据表格使用bootstrap table插件,弹窗使用layer插件,日期选择使用laydate插件。...

    通用JPA查询

    封装hibernate查询方式(JPA)

    解决timer循环引用的问题

    iOS中的timer的循环引用问题,对timer进行了一次封装

Global site tag (gtag.js) - Google Analytics