【发布时间】:2013-12-24 00:07:59
【问题描述】:
在 PostgreSQL 中,string_agg(column, separator) 允许聚合一些字符串。我尝试将它与 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));
}
}
然后:
Expression<String> exprStr = ...
CriteriaBuilder cb = ...
cb.construct(MyClass.class,
myClass.get(MyClass_.name),
myClass.get(MyClass_.surname),
new StringAgg(cb, exprStr, "/"));
问题,我得到一个 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]
调试器显示cb.construct() (new StringAgg(cb, exprStr, "/")) 中的最后一个Selection 被忽略。因此,搜索到的构造函数是 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)"));
}
}
在 persistence.xml
中使用它<properties>
<property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver"/>
<property name="hibernate.dialect" value="path.to.PGDialect"/>
然后使用 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" )));
为了缓解它,我创建了一个辅助方法:
public static Expression<String> strAgg(CriteriaBuilder cb, Expression<String> expression, String delimiter) {
return cb.function( "string_agg", String.class, expression, cb.literal(delimiter));
}
所以代码变成了:
Expression<String> exprStr = ...
CriteriaBuilder cb = ...
cb.construct(MyClass.class,
myClass.get(MyClass_.name),
myClass.get(MyClass_.surname),
strAgg(cb, myColPath, "delimiter"));
【问题讨论】:
-
FUNCTION 是 JPA (2.1) 标准函数。也许使用那个
-
为什么不使用类的普通构造函数,相当于JPQL 中的
SELECT new com.me.Entity(path1, path2)...? (PS:+1 因为我学到了一些新东西)
标签: java postgresql jpa