使用TIMESTAMPDIFF和JPA条件查询并将hibernate作为提供者

我有一个包含列设置和释放的数据表,两者都有时间戳。 我的目标是使用CriteriaQuery在下面创建一个等效的SQL查询。

SQL查询: SELECT TIMESTAMPDIFF(SECOND, setup, released)) as sum_duration FROM calls

CriteriaBuilder#diff()函数显然不起作用,因为它需要参数是Numbers,所以我尝试使用CriteriaBuilder#函数 :

 EntityManager entityManager = emProvider.get(); CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder(); CriteriaQuery query = criteriaBuilder.createQuery(); Root thingyRoot = query.from(Thingy.class); Path setup = root.get("setup"); Path release = root.get("release"); Expression secondLiteral = criteriaBuilder.literal("SECOND"); Expression func = criteriaBuilder.function("TIMESTAMPDIFF", Integer.class, secondLiteral, setup, release); entityManager.createQuery(query).getResultList(); 

但是,当我尝试运行此代码时,它抛出exception; 似乎文字不是作为常量呈现,而是作为参数呈现:

 java.lang.IllegalStateException: No data type for node: org.hibernate.hql.internal.ast.tree.MethodNode \-[METHOD_CALL] MethodNode: '(' +-[METHOD_NAME] IdentNode: 'TIMESTAMPDIFF' {originalText=TIMESTAMPDIFF} \-[EXPR_LIST] SqlNode: 'exprList' +-[NAMED_PARAM] ParameterNode: '?' {name=param0, expectedType=null} +-[DOT] DotNode: 'cdr0_.setup' {propertyName=setup,dereferenceType=ALL,propertyPath=setup,path=generatedAlias0.setup,tableAlias=cdr0_,className=com.vtsl.domain.CDR,classAlias=generatedAlias0} | +-[ALIAS_REF] IdentNode: 'cdr0_.id' {alias=generatedAlias0, className=com.vtsl.domain.CDR, tableAlias=cdr0_} | \-[IDENT] IdentNode: 'setup' {originalText=setup} \-[DOT] DotNode: 'cdr0_.release' {propertyName=release,dereferenceType=ALL,propertyPath=release,path=generatedAlias0.release,tableAlias=cdr0_,className=com.vtsl.domain.CDR,classAlias=generatedAlias0} +-[ALIAS_REF] IdentNode: 'cdr0_.id' {alias=generatedAlias0, className=com.vtsl.domain.CDR, tableAlias=cdr0_} \-[IDENT] IdentNode: 'release' {originalText=release} 

所以我试图匿名覆盖LiteralExpression#render直接返回我提供给该方法的字符串,但是这个例外。

 java.lang.IllegalStateException: No data type for node: org.hibernate.hql.internal.ast.tree.MethodNode \-[METHOD_CALL] MethodNode: '(' +-[METHOD_NAME] IdentNode: 'TIMESTAMPDIFF' {originalText=TIMESTAMPDIFF} \-[EXPR_LIST] SqlNode: 'exprList' +-[IDENT] IdentNode: 'SECOND' {originalText=SECOND} +-[DOT] DotNode: 'cdr0_.setup' {propertyName=setup,dereferenceType=ALL,propertyPath=setup,path=generatedAlias0.setup,tableAlias=cdr0_,className=com.vtsl.domain.CDR,classAlias=generatedAlias0} | +-[ALIAS_REF] IdentNode: 'cdr0_.id' {alias=generatedAlias0, className=com.vtsl.domain.CDR, tableAlias=cdr0_} | \-[IDENT] IdentNode: 'setup' {originalText=setup} \-[DOT] DotNode: 'cdr0_.release' {propertyName=release,dereferenceType=ALL,propertyPath=release,path=generatedAlias0.release,tableAlias=cdr0_,className=com.vtsl.domain.CDR,classAlias=generatedAlias0} +-[ALIAS_REF] IdentNode: 'cdr0_.id' {alias=generatedAlias0, className=com.vtsl.domain.CDR, tableAlias=cdr0_} \-[IDENT] IdentNode: 'release' {originalText=release} 

所以问题是:我如何修复我正在尝试的操作,或者实现最初的目标?

我正在使用Hibernate,我的数据库是MySQL。

我碰到了同样的问题: SECOND将被撇号包围,查询将引发exception。

我通过以下代码解决了这个问题:

 CriteriaBuilder builder = em.getCriteriaBuilder(); CriteriaQuery cq = builder.createQuery( MyEntity.class ); Root root = cq.from( MyEntity.class ); javax.persistence.criteria.Expression timeDiff = builder.function( "TIMEDIFF", java.sql.Time.class, root.get( "endDate" ), root.get( "startDate" ) ); javax.persistence.criteria.Expression timeToSec = builder.function( "TIME_TO_SEC", Integer.class, timeDiff ); //lessThanOrEqualTo 60 minutes cq.where( builder.lessThanOrEqualTo( timeToSec, 3600 ) ); return em.createQuery( cq ).getResultList(); 

这给了我相同的结果。

这是对等效JPA标准查询的解释

SELECT *来自TIMESTAMPDIFF(SECOND,设置,发布)<3600的调用;

首先,您必须创建单位表达式并从BasicFunctionExpression扩展它,以“SECOND”参数为单位并仅覆盖其rendor(RenderingContext renderingContext)方法。

 import java.io.Serializable; import org.hibernate.query.criteria.internal.CriteriaBuilderImpl; import org.hibernate.query.criteria.internal.compile.RenderingContext; import org.hibernate.query.criteria.internal.expression.function.BasicFunctionExpression; public class UnitExpression extends BasicFunctionExpression implements Serializable { public UnitExpression(CriteriaBuilderImpl criteriaBuilder, Class javaType, String functionName) { super(criteriaBuilder, javaType, functionName); } @Override public String render(RenderingContext renderingContext) { return getFunctionName(); } } 

然后在JPA条件查询中使用此单位表达式。

 EntityManager entityManager = emProvider.get(); CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder(); Root thingyRoot = query.from(Calls.class); Expression second = new UnitExpression(null, String.class, "SECOND"); Expression timeInSec = cb.function( "TIMESTAMPDIFF", Integer.class, second , root.get("setup"), root.get("release")); List conditions = new ArrayList<>(); conditions.add(cb.lessThan(timeInSec, 3600)); cq.where(conditions.toArray(new Predicate[]{})); return session.createQuery(cq); 

这是工作。