使用构造函数表达式和 JPQL 的复合 DTO 投影

Posted

技术标签:

【中文标题】使用构造函数表达式和 JPQL 的复合 DTO 投影【英文标题】:Composite DTO projections using a Constructor Expression and JPQL 【发布时间】:2018-11-06 06:51:03 【问题描述】:

我正在尝试通过提供完全限定名称从 db 中选择特定列到复合类 DTO。

 @Data
    public class Temp 
    String dName;
    Temp2 value;

    public Temp( String dName, Temp2 value) 
        this.dName = dName;
        this.value = value;
    
    @Data
    public static class Temp2 
        Integer day;
        public Temp2(Integer day) 
            this.day = day;
        
    
 

查询:SELECT new com.pojo.Temp (t1.displayName, new com.pojo.Temp.Temp2 (t3.day)) FROM table1 t1 JOIN table2 t2 ON t1.bId = t2.id AND LEFT JOIN table3 t3 ON t1.g_id = t2.id

Error: `[2018-11-06 12:02:54] [main] ERROR o.h.hql.internal.ast.ErrorCounter.reportError -  [  ] line 1:64: unexpected token: ,
[2018-11-06 12:02:54] [main] ERROR o.h.hql.internal.ast.ErrorCounter.reportError -  [  ] line 1:64: unexpected token: ,
antlr.NoViableAltException: unexpected token: ,
    at org.hibernate.hql.internal.antlr.HqlBaseParser.primaryExpression(HqlBaseParser.java:1009)
    at org.hibernate.hql.internal.antlr.HqlBaseParser.atom(HqlBaseParser.java:3549)
    at org.hibernate.hql.internal.antlr.HqlBaseParser.unaryExpression(HqlBaseParser.java:3401)
    at org.hibernate.hql.internal.antlr.HqlBaseParser.multiplyExpression(HqlBaseParser.java:3273)
    at org.hibernate.hql.internal.antlr.HqlBaseParser.additiveExpression(HqlBaseParser.java:2930)
    at org.hibernate.hql.internal.antlr.HqlBaseParser.concatenation(HqlBaseParser.java:615)
    at org.hibernate.hql.internal.antlr.HqlBaseParser.relationalExpression(HqlBaseParser.java:2697)
    at org.hibernate.hql.internal.antlr.HqlBaseParser.equalityExpression(HqlBaseParser.java:2558)
    at org.hibernate.hql.internal.antlr.HqlBaseParser.negatedExpression(HqlBaseParser.java:2522)
    at org.hibernate.hql.internal.antlr.HqlBaseParser.logicalAndExpression(HqlBaseParser.java:2438)
    at org.hibernate.hql.internal.antlr.HqlBaseParser.logicalOrExpression(HqlBaseParser.java:2403)
    at org.hibernate.hql.internal.antlr.HqlBaseParser.expression(HqlBaseParser.java:2116)
    at org.hibernate.hql.internal.antlr.HqlBaseParser.aliasedExpression(HqlBaseParser.java:2357)
    at org.hibernate.hql.internal.antlr.HqlBaseParser.selectedPropertiesList(HqlBaseParser.java:1390)
    at org.hibernate.hql.internal.antlr.HqlBaseParser.newExpression(HqlBaseParser.java:1434)
    at org.hibernate.hql.internal.antlr.HqlBaseParser.selectClause(HqlBaseParser.java:1306)
    at org.hibernate.hql.internal.antlr.HqlBaseParser.selectFrom(HqlBaseParser.java:1040)
    at org.hibernate.hql.internal.antlr.HqlBaseParser.queryRule(HqlBaseParser.java:748)
    at org.hibernate.hql.internal.antlr.HqlBaseParser.selectStatement(HqlBaseParser.java:319)
    at org.hibernate.hql.internal.antlr.HqlBaseParser.statement(HqlBaseParser.java:198)
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.parse(QueryTranslatorImpl.java:284)
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:186)
    at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:141)
    at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:115)
    at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:77)
    at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:153)
    at org.hibernate.internal.AbstractSharedSessionContract.getQueryPlan(AbstractSharedSessionContract.java:553)
    at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:662)
    at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:103)
    `

我无法找到使用 jpql 表达式将数据输入复合类 dto 的任何相关答案。因为我是新人,所以我可能会遗漏一些东西。

我们将不胜感激。

【问题讨论】:

【参考方案1】:

像这样的嵌套构造函数表达式是不可能的 AFAIK。我知道有一种方法可以做到这一点,但这是一个肮脏而丑陋的工作。我将所有参数放在一个构造函数中,然后在构造函数中实例化其他类对象。

例子:

    public CommentDTO(Long id, String body, LocalDateTime datePosted,
                  LocalDateTime lastModifiedDate, Long userId,
                  String login, String avatarUrl, boolean hireable) 
    this.id = id;
    this.body = body;
    this.datePosted = datePosted;
    this.lastModifiedDate = lastModifiedDate;
    this.author = new UserDTO(userId, login, avatarUrl, hireable);

所以 JPQL 查询必须包含所有这些参数。

同样,不建议这样做,因为它变得 100% 不可维护。但这是我发现使用构造函数表达式的唯一方法。

我刚刚重构为使用基于界面的投影。如果您使用的是 Spring Data JPA,那么为嵌套投影设置它非常容易。

【讨论】:

感谢@abrandell。我还避免使用带有太多参数的构造函数。我很想知道是否可以在 jpql 中嵌套构造函数。因为以这种方式嵌套构造函数似乎是合乎逻辑的。我不明白不支持这一点的原因是什么。 具有嵌套实体的接口投影会触发多个查询,这可能会导致performance issues。

以上是关于使用构造函数表达式和 JPQL 的复合 DTO 投影的主要内容,如果未能解决你的问题,请参考以下文章

JPQL 构造函数表达式中的 JPQL 和子查询

JPQL 构造函数表达式 - org.hibernate.hql.ast.QuerySyntaxException:表未映射

复合键的 JPQL 异常

JPQL 多对多 Dto 选择失败

java 使用JPQL投影进行查询(需要使用构造函数)

在 JPA 2 中使用构造函数表达式时排序