java - 将 TIMESTAMPDIFF 与 JPA 条件查询一起使用,并将 hibernate 作为提供者

标签 java mysql sql hibernate jpa

我有一个包含列设置和发布的数据表,它们都包含时间戳。我的目标是使用 CriteriaQuery 创建一个等效的 SQL 查询。

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

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

EntityManager entityManager = emProvider.get();

CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();

CriteriaQuery<Integer> query = criteriaBuilder.createQuery();

Root<Thingy> thingyRoot = query.from(Thingy.class);

Path<DateTime> setup = root.get("setup");
Path<DateTime> release = root.get("release");

Expression secondLiteral = criteriaBuilder.literal("SECOND");

Expression func = criteriaBuilder.function("TIMESTAMPDIFF", Integer.class, secondLiteral, setup, release);

entityManager.createQuery(query).getResultList();

但是,当我尝试运行这段代码时,它抛出了一个异常;看起来文字没有呈现为常量,而是呈现为参数:

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。

最佳答案

这里是对等价的 JPA Criteria Query 的解释

SELECT * from calls where TIMESTAMPDIFF(SECOND, setup, released) < 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<String> implements Serializable {

  public UnitExpression(CriteriaBuilderImpl criteriaBuilder, Class<String> 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<Calls> thingyRoot = query.from(Calls.class);

    Expression<String> second = new UnitExpression(null, String.class, "SECOND");
    Expression<Integer> timeInSec = cb.function(
        "TIMESTAMPDIFF",
        Integer.class,
        second ,
        root.<Timestamp>get("setup"),
        root.<Timestamp>get("release"));
    List<Predicate> conditions = new ArrayList<>();
    conditions.add(cb.lessThan(timeInSec, 3600));
    cq.where(conditions.toArray(new Predicate[]{}));
    return session.createQuery(cq);

它正在工作。

关于java - 将 TIMESTAMPDIFF 与 JPA 条件查询一起使用,并将 hibernate 作为提供者,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22412234/

相关文章:

java - WebElements 列表中的相对 Xpath

java - 在 Java 中将字符串连接到 "sets"

php - 使一段代码在函数和类中工作

mysql - 优化比较两个MySQL大表中的数据

java - 从数据库中检索到的实体与查询中的大小写相同

java - 如果数组包含 100 个元素,二分查找最多检查多少个元素?

javascript - Enterprise Architect 图框架/mysql 数据库中的图表引用

php - MySQL使用LIKE命令在分号之间查找

mysql - 在两个表 : Why? 的非常简单的情况下,MySQL 5.6.16 中的 "Cannot add foreign key constraint"错误

SQL子查询-有更好的方法吗