在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));
}
}
然后:
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]
调试程序显示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)"));
}
}
在 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"));
答案 0 :(得分:6)
也许这有助于你,
您可以在JPA Criteria Query中调用数据库函数。
CriteriaBuilder接口有一个“function”方法。
<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
然后你可以尝试创建一个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))
);
}
}
或者您可以使用自定义方言注册新功能
public class PGDialect extends PostgreSQLDialect{
public PGDialect() {
super();
registerFunction("string_agg", new SQLFunctionTemplate( StandardBasicTypes.STRING, "string_agg(?1, ?2)"));
}
}
并在CriteriaBuilder中将其用作普通函数
Expression<String> functionStringAgg = cb.function( "string_agg", String.class,
cb.parameter(String.class, "toJoin" ),
cb.parameter(String.class, "delimiter"));
毕竟不要忘记将参数值设置为CriteriaQuery
setParameter( "toJoin", toJoin);
setParameter( "delimiter", delimiter);
答案 1 :(得分:0)
我使用相同的结构来完成它,但是很容易:
修改了持久性文件:
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">es.gmrcanarias.saga.utiles.PGDialect</prop>
<prop key="hibernate.show_sql">true</prop>
...
</props>
</property>
然后注册功能:
import org.hibernate.dialect.PostgreSQL82Dialect;
import org.hibernate.dialect.function.SQLFunctionTemplate;
import org.hibernate.type.StandardBasicTypes;
public class PGDialect extends PostgreSQL82Dialect {
public PGDialect() {
super();
registerFunction("string_agg", new
SQLFunctionTemplate(StandardBasicTypes.STRING, "string_agg(?1, ?2)"));
}
}
然后添加查询:
Join<Razon, Incidenc> subquery;
....
Expression<String> functionStringAgg = criteriaBuilder.function("string_agg",
String.class,
subquery.get(CODIGO),
criteriaBuilder.literal(", "));
...
subqueryList.select(functionStringAgg);
答案 2 :(得分:0)
PGDialect的一个细节: 您可以添加第3个参数以使用ORDER BY
public class PGDialect extends PostgreSQL9Dialect {
public PGDialect() {
super();
this.registerFunction("string_agg", new SQLFunctionTemplate( StandardBasicTypes.STRING, "string_agg(?1, ?2)") );
this.registerFunction("string_agg", new SQLFunctionTemplate( StandardBasicTypes.STRING, "string_agg(?1, ?2 ORDER BY ?3 )") );
}
}
HQL查询用法
"SELECT string_agg(f.name, '; ', f.name) FROM Foo as f "