JPA中的PostgreSQL函数string_agg

Arn*_*lle 6 java postgresql jpa

在PostgreSQL中,string_agg(列,分隔符)允许聚合一些字符串.我尝试将它与JPA一起使用,但它不是JPA标准函数.

注意:这等于CriteriaBuilder#concat().

所以,我试图告诉JPA这个函数是否存在,如下所示:

public class StringAgg extends ParameterizedFunctionExpression<String> implements Serializable {

  public static final String NAME = "string_agg";

  @Override
  public boolean isAggregation() {
    return true;
  }

  @Override
  protected boolean isStandardJpaFunction() {
    return false;
  }

  public StringAgg(CriteriaBuilderImpl criteriaBuilder, Expression<String> expression, String separator) {
    super(criteriaBuilder, String.class, NAME, expression, new LiteralExpression(criteriaBuilder, separator));
  }
}
Run Code Online (Sandbox Code Playgroud)

然后 :

Expression<String> exprStr = ...
CriteriaBuilder cb = ...
cb.construct(MyClass.class, 
             myClass.get(MyClass_.name),
             myClass.get(MyClass_.surname),
             new StringAgg(cb, exprStr, "/"));
Run Code Online (Sandbox Code Playgroud)

问题,我得到一个NullPointerException!

java.lang.NullPointerException: null
at org.hibernate.internal.util.ReflectHelper.getConstructor(ReflectHelper.java:355) ~[hibernate-core-4.3.0.Beta3.jar:4.3.0.Beta3]
at org.hibernate.hql.internal.ast.tree.ConstructorNode.resolveConstructor(ConstructorNode.java:179) ~[hibernate-core-4.3.0.Beta3.jar:4.3.0.Beta3]
at org.hibernate.hql.internal.ast.tree.ConstructorNode.prepare(ConstructorNode.java:152) ~[hibernate-core-4.3.0.Beta3.jar:4.3.0.Beta3]
at org.hibernate.hql.internal.ast.HqlSqlWalker.processConstructor(HqlSqlWalker.java:1028) ~[hibernate-core-4.3.0.Beta3.jar:4.3.0.Beta3]
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectExpr(HqlSqlBaseWalker.java:2279) ~[hibernate-core-4.3.0.Beta3.jar:4.3.0.Beta3]
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectExprList(HqlSqlBaseWalker.java:2145) ~[hibernate-core-4.3.0.Beta3.jar:4.3.0.Beta3]
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectClause(HqlSqlBaseWalker.java:1451) ~[hibernate-core-4.3.0.Beta3.jar:4.3.0.Beta3]
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:571) ~[hibernate-core-4.3.0.Beta3.jar:4.3.0.Beta3]
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:299) ~[hibernate-core-4.3.0.Beta3.jar:4.3.0.Beta3]
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:247) ~[hibernate-core-4.3.0.Beta3.jar:4.3.0.Beta3]
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:261) ~[hibernate-core-4.3.0.Beta3.jar:4.3.0.Beta3]
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:189) ~[hibernate-core-4.3.0.Beta3.jar:4.3.0.Beta3]
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:141) ~[hibernate-core-4.3.0.Beta3.jar:4.3.0.Beta3]
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:119) ~[hibernate-core-4.3.0.Beta3.jar:4.3.0.Beta3]
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:87) ~[hibernate-core-4.3.0.Beta3.jar:4.3.0.Beta3]
at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:190) ~[hibernate-core-4.3.0.Beta3.jar:4.3.0.Beta3]
at org.hibernate.internal.AbstractSessionImpl.getHQLQueryPlan(AbstractSessionImpl.java:288) ~[hibernate-core-4.3.0.Beta3.jar:4.3.0.Beta3]
at org.hibernate.internal.AbstractSessionImpl.createQuery(AbstractSessionImpl.java:223) ~[hibernate-core-4.3.0.Beta3.jar:4.3.0.Beta3]
Run Code Online (Sandbox Code Playgroud)

调试器显示忽略最后Selection一个cb.construct()(new StringAgg(cb, exprStr, "/")).因此,搜索的构造函数MyClass(String,String)代替MyClass(String, String, String).

StringAgg的实现有什么问题吗?有人已经尝试在JPA中使用string_agg吗?

解决方案(感谢vzamanillo)

扩展方言:

public class PGDialect extends PostgreSQLDialect{

  public PGDialect() {
    super();
    registerFunction("string_agg", new SQLFunctionTemplate( StandardBasicTypes.STRING, "string_agg(?1, ?2)"));
  }
}
Run Code Online (Sandbox Code Playgroud)

persistence.xml中使用它

<properties>
  <property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver"/>
  <property name="hibernate.dialect" value="path.to.PGDialect"/>
Run Code Online (Sandbox Code Playgroud)

然后使用CriteriaBuilder #function():

Expression<String> exprStr = ...
CriteriaBuilder cb = ...
cb.construct(MyClass.class, 
             myClass.get(MyClass_.name),
             myClass.get(MyClass_.surname),
             cb.function( "string_agg", myColPath, cb.literal("delimiter" )));
Run Code Online (Sandbox Code Playgroud)

为了简化它,我创建了一个帮助方法:

public static Expression<String> strAgg(CriteriaBuilder cb, Expression<String> expression, String delimiter) {
  return cb.function( "string_agg", String.class, expression, cb.literal(delimiter));
}
Run Code Online (Sandbox Code Playgroud)

所以代码变成:

Expression<String> exprStr = ...
CriteriaBuilder cb = ...
cb.construct(MyClass.class, 
             myClass.get(MyClass_.name),
             myClass.get(MyClass_.surname),
             strAgg(cb, myColPath, "delimiter"));
Run Code Online (Sandbox Code Playgroud)

vza*_*llo 7

也许这有助于你,

您可以在JPA Criteria Query中调用数据库函数.

CriteriaBuilder接口具有" 功能 "方法.

<T> Expression<T> function(String name,
                         Class<T> type,
                         Expression<?>... args)

Create an expression for the execution of a database function.

Parameters:
    name - function name
    type - expected result type
    args - function arguments
Returns:
    expression
Run Code Online (Sandbox Code Playgroud)

然后,您可以尝试创建CriteriaBuilder帮助程序类以获取可在标准查询中照常使用的普通条件表达式

public abstract class CriteriaBuilderHelper {

    private static final String PG_STRING_AGG  = "string_agg";

    /**
    * @param cb the CriteriaBuilder to use
    * @param toJoin the string to join
    * @param delimiter the string to use
    * @return Expression<String>
    */
    public static Expression functionStringAgg(CriteriaBuilder cb, String toJoin, String delimiter) {
        return cb.function(PG_STRING_AGG, 
            String.class,
            cb.literal(toJoin),
            cb.literal(delimiter))
        );
    }
}
Run Code Online (Sandbox Code Playgroud)

或者您可以使用自定义方言注册新功能

public class PGDialect extends PostgreSQLDialect{

    public PGDialect() {
        super();
        registerFunction("string_agg", new SQLFunctionTemplate( StandardBasicTypes.STRING, "string_agg(?1, ?2)"));
    }
}
Run Code Online (Sandbox Code Playgroud)

并在CriteriaBuilder中将其用作普通函数

Expression<String> functionStringAgg = cb.function( "string_agg", String.class, 
                                cb.parameter(String.class, "toJoin" ), 
                                cb.parameter(String.class, "delimiter"));
Run Code Online (Sandbox Code Playgroud)

毕竟不要忘记将参数值设置为您的CriteriaQuery

setParameter( "toJoin", toJoin);
setParameter( "delimiter", delimiter);
Run Code Online (Sandbox Code Playgroud)