我有一个带有此签名的 Postgres 函数:
CREATE OR REPLACE FUNCTION public.ums_rate_permissions_for_collection(user_roles_arr text[], profile_id bigint, collection_id bigint)
我在 Hibernate 5.6 中通过 NamedNativeQuery 访问此函数,但在将 String[] 作为参数传递给此方法以用作 text[] 时遇到问题。
本机查询:
@NamedNativeQuery(
name = "getUserRolesPermissionsForCollection",
query = "SELECT * FROM ums_rate_permissions_for_collection(ARRAY[:profileList], :profileId, :collectionId)"
)
DAO:
public UmsRolesNavigationPermissionVO getRolePermissionsForCollection(final List<String> userRoles, final Long profileId, final Long collectionId) {
String[] param = userRoles.toArray(new String[0]);
NativeQuery query = this.getSession()
.getNamedNativeQuery("getUserRolesPermissionsForCollection")
.setParameterList("profileList", param)
.setParameter("profileId", profileId)
.setParameter("collectionId", collectionId);
List result = query.list();
当我在 postgres 控制台中运行此查询时,它工作正常:
SELECT * FROM ums_rate_permissions_for_collection(ARRAY['Sales User', 'Admin'], 141, 330)
而且,如果我的参数中只有一个元素,则一切正常。但是一旦数组中有 2 个或更多值,我就会收到这些错误:
Caused by: org.hibernate.exception.SQLGrammarException: could not extract ResultSet
at deployment.tg-clienta.war//org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:103)
at deployment.tg-clienta.war//org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:37)
at deployment.tg-clienta.war//org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:113)
at deployment.tg-clienta.war//org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:99)
at deployment.tg-clienta.war//org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:67)
at deployment.tg-clienta.war//org.hibernate.loader.Loader.getResultSet(Loader.java:2322)
at deployment.tg-clienta.war//org.hibernate.loader.Loader.executeQueryStatement(Loader.java:2075)
at deployment.tg-clienta.war//org.hibernate.loader.Loader.executeQueryStatement(Loader.java:2037)
at deployment.tg-clienta.war//org.hibernate.loader.Loader.doQuery(Loader.java:956)
at deployment.tg-clienta.war//org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:357)
at deployment.tg-clienta.war//org.hibernate.loader.Loader.doList(Loader.java:2868)
at deployment.tg-clienta.war//org.hibernate.loader.Loader.doList(Loader.java:2850)
at deployment.tg-clienta.war//org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2682)
at deployment.tg-clienta.war//org.hibernate.loader.Loader.list(Loader.java:2677)
at deployment.tg-clienta.war//org.hibernate.loader.custom.CustomLoader.list(CustomLoader.java:338)
at deployment.tg-clienta.war//org.hibernate.internal.SessionImpl.listCustomQuery(SessionImpl.java:2186)
at deployment.tg-clienta.war//org.hibernate.internal.AbstractSharedSessionContract.list(AbstractSharedSessionContract.java:1204)
at deployment.tg-clienta.war//org.hibernate.query.internal.NativeQueryImpl.doList(NativeQueryImpl.java:177)
at deployment.tg-clienta.war//org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1617)
... 27 more
Caused by: org.postgresql.util.PSQLException: ERROR: function ums_rate_permissions_for_collection(record[], bigint, bigint) does not exist
Hinweis: No function matches the given name and argument types. You might need to add explicit type casts.
Position: 15
at [email protected]//org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2553)
at [email protected]//org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2285)
at [email protected]//org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:323)
at [email protected]//org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:481)
at [email protected]//org.postgresql.jdbc.PgStatement.execute(PgStatement.java:401)
at [email protected]//org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:164)
at [email protected]//org.postgresql.jdbc.PgPreparedStatement.executeQuery(PgPreparedStatement.java:114)
at [email protected]//org.jboss.jca.adapters.jdbc.WrappedPreparedStatement.executeQuery(WrappedPreparedStatement.java:504)
at deployment.tg-clienta.war//org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:57)
... 41 more
看起来 Hibernate 正在将其转换为某种记录数组而不是文本数组。我尝试了几件事,例如,将所有单独的字符串包装在 '' 内或将我的值转换为 postgres 文本数组表示形式:
private String convertToPostgresArray(List<String> userRoles) {
StringBuilder sb = new StringBuilder();
sb.append("'{");
for (int i = 0; i < userRoles.size(); i++) {
if (i > 0) {
sb.append(",");
}
sb.append(userRoles.get(i));
}
sb.append("}'");
return sb.toString();
}
后一种方法似乎至少将真实的 text[] 传递给 Postgres,如错误堆栈所示:
Caused by: org.postgresql.util.PSQLException: ERROR: syntax error at or near "AND"
Wobei: PL/pgSQL function ums_rate_permissions_for_collection(text[],bigint,bigint) line 73 at RETURN QUERY
at [email protected]//org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2553)
at [email protected]//org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2285)
at [email protected]//org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:323)
at [email protected]//org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:481)
at [email protected]//org.postgresql.jdbc.PgStatement.execute(PgStatement.java:401)
at [email protected]//org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:164)
at [email protected]//org.postgresql.jdbc.PgPreparedStatement.executeQuery(PgPreparedStatement.java:114)
at [email protected]//org.jboss.jca.adapters.jdbc.WrappedPreparedStatement.executeQuery(WrappedPreparedStatement.java:504)
at deployment.tg-clienta.war//org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:57)
... 39 more
但没有什么真正有帮助。有什么想法吗?
我的同事给了我解决这个问题的秘诀。解决方案是使用自定义类型并使用
.setParameter(String name, Object value, Type type)
方法添加它。
String[] param = userRoles.toArray(new String[0]);
NativeQuery query = this.getSession()
.getNamedNativeQuery("getUserRolesPermissionsForCollection")
.setParameter("profileList", param, new CustomType(new CustomStringArrayType()))
.setParameter("profileId", profileId)
.setParameter("collectionId", collectionId);
这个
CustomStringArrayType
看起来像这样:
public class CustomStringArrayType implements UserType {
@Override
public String[] nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner)
throws HibernateException, SQLException {
if (names != null && names.length > 0 && rs != null && rs.getArray(names[0]) != null) {
return (String[]) rs.getArray(names[0]).getArray();
}
return null;
}
}