JPA Hibernate 调用 Postgres 函数 Void 返回 MappingException:
Posted
技术标签:
【中文标题】JPA Hibernate 调用 Postgres 函数 Void 返回 MappingException:【英文标题】:JPA Hibernate Call Postgres Function Void Return MappingException: 【发布时间】:2012-09-15 11:53:09 【问题描述】:在尝试使用 JPA 创建本机查询调用 postgres 函数时,我遇到了一个问题:org.hibernate.MappingException: No Dialect mapping for JDBC type: 1111。
我在启动单例中创建了一个 EJB 计时器,以每 6 小时运行一次 Postgres 函数。该函数返回 void 并检查过期记录、删除它们并更新一些状态。它不接受任何参数,并且返回 void。
如果我使用 PgAdmin 查询工具 (select function();) 调用 postgres 函数并返回 void,则它可以完美运行。
当我在 Glassfish 3.1.1 上部署应用程序时,出现异常并且无法部署。
这是(缩短的)堆栈跟踪:
WARNING: A system exception occurred during an invocation on EJB UserQueryBean method public void com.mysoftwareco.entity.utility.UserQueryBean.runRequestCleanup()
javax.ejb.TransactionRolledbackLocalException: Exception thrown from bean
...STACK TRACE BLAH BLAH BLAH ...
Caused by: javax.persistence.PersistenceException: org.hibernate.MappingException: No Dialect mapping for JDBC type: 1111
代码如下:
首先是 JPA 的东西:
public void runRequestCleanup()
String queryString = "SELECT a_function_that_hibernate_chokes_on()";
Query query = em.createNativeQuery(queryString);
Object result = query.getSingleResult();
这是单例调用它:
@Startup
@Singleton
public class RequestCleanupTimer
@Resource
TimerService timerService;
@EJB
UserQueryBean queryBean;
@PostConstruct
@Schedule(hour = "*/6")
void runCleanupTimer()
queryBean.runRequestCleanup();
以及功能:
CREATE OR REPLACE FUNCTION a_function_that_hibernate_chokes_on()
RETURNS void AS
$BODY$
DECLARE
var_field_id myTable.field_id%TYPE;
BEGIN
FOR var_field_id IN
select field_id from myTable
where status = 'some status'
and disposition = 'some disposition'
and valid_through < now()
LOOP
BEGIN
-- Do Stuff
END;
END LOOP;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
【问题讨论】:
【参考方案1】:这可能是一个 hack,但它对我有用并且非常简单。只需将查询更改为:
SELECT count(*) FROM your_function();
现在它返回一个正确的整数,hibernate 很高兴。
【讨论】:
好主意。我看到了另一个答案,我认为你的答案是最好的。 你也可以SELECT 1 FROM...
【参考方案2】:
我已经受够了 JPA 的麻烦,试图让它运行一个存储过程。
我最终使用了带有预处理语句的 JDBC。我花了几个小时试图将方形钉插入圆孔后,在 15 分钟内完成了。我调用了我的持久性单元用来获取连接的同一个 jndi 数据源,创建了一个准备好的语句并在完成后关闭它。
因此,如果您需要从(现在主要是)JPA 应用程序运行存储过程(或 Postgres 函数),这对我有用:
@Stateless
@LocalBean
public class UserQueryBean implements Serializable
@Resource(mappedName="jdbc/DatabasePool")
private javax.sql.DataSource ds;
...
public void runRequestCleanup()
String queryString = "SELECT cleanup_function_that_hibernateJPA_choked_on()";
Connection conn = null;
PreparedStatement statement = null;
try
conn = ds.getConnection();
statement = conn.prepareCall(queryString);
statement.executeQuery();
catch (SQLException ex)
Logger.getLogger(UserQueryBean.class.getName()).log(Level.SEVERE, null, ex);
finally
try
statement.close();
conn.close();
catch (SQLException ex)
Logger.getLogger(UserQueryBean.class.getName()).log(Level.SEVERE, null, ex);
// bit of logging code here
...
似乎有一个可怕的疏忽,忽略了 JPA 在服务器上运行函数或存储过程的简单能力;尤其是除了 void 或受影响的行数之外什么都不返回的。如果这是故意的......没有评论。
编辑:添加关闭连接。
【讨论】:
【参考方案3】:对于本期的未来访问者,演员表也可以。也发布在this thread上
public void runRequestCleanup()
String queryString = "SELECT cast(a_function_that_hibernate_chokes_on() as text)";
Query query = em.createNativeQuery(queryString);
query.getSingleResult();
【讨论】:
【参考方案4】:似乎是postgres存储过程返回void时出现问题。尝试更改返回类型以返回一些虚拟值,可能是字符串。这在我的情况下有效。
【讨论】:
我正在尝试调用一个返回 void 的 postgres 管理函数。 pg_advisory_xact_lock 就是我所说的。我该如何改变呢?【参考方案5】:这是不久前发布的,但我遇到了类似的问题。如前所述,Hibernate 看起来对 void 过敏,并且每次都会尝试将您锁定为使用返回类型。从我的角度来看,这是一种很好的做法,因为您通常应该总是有一个返回值,至少可以指定它是否成功:抛出异常通常是滥用。
然而,Hibernate 确实提供了一种绕过它的限制的方法:org.hibernate.jdbc.Work
您可以轻松地在小班中重现所需要的内容:
class VoidProcedureWork implements Work
String query;
private VoidProcedureWork(String sql)
query = sql;
public static boolean execute(Session session, String sql)
try
// We assume that we will succeed or receive an exception.
session.doWork(new VoidProcedureWork(sql));
return true;
catch (Throwable e)
// log exception
return false;
/**
* @see org.hibernate.jdbc.Work#execute(java.sql.Connection)
*/
@Override
public void execute(Connection connection) throws SQLException
Statement statement = null;
try
statement = connection.createStatement();
statement.execute(query);
finally
if (statement != null)
statement.close();
现在您可以随时使用
调用它VoidProcedureWork.execute(hibernateSession, sqlQuery);
你会注意到关于这个类的两件事: 1)我不关闭连接。我离开它是因为我不知道谁打开了连接,如何以及为什么。事务中是否使用相同的连接?如果我关闭它之后有人使用它,它会崩溃吗?等等。我没有编写 Hibernate 代码,也没有使用 connection.open()。因此,我不会关闭它,并假设无论打开它都会关闭它。 2)它比OOP更程序化。我知道,但我很懒:使用 VoidProcedureWork.execute(session, sql) 比使用 new VoidProcedureWork(sql).execute(session) 更容易(而且更清晰 imo)。或者更糟糕的是:每次我想使用那个小技巧时重现执行代码(session.doWork(new VoidProcedureWork(sql)) 和异常处理)。
【讨论】:
【参考方案6】:老兄!就像引用函数名一样简单。像这样:
public void runRequestCleanup()
String queryString = "SELECT \"a_function_that_hibernate_chokes_on\"()";
Query query = em.createNativeQuery(queryString);
Object result = query.getSingleResult();
【讨论】:
【参考方案7】:将查询转换为:
SELECT cast(sqlFunction() as text)
例如:"SELECT cast(pushlogsToMainTable() as text)"
它对我有用。
【讨论】:
与@srex 在 2015 年的同一问题中的answer 相比,这没有提供额外的帮助以上是关于JPA Hibernate 调用 Postgres 函数 Void 返回 MappingException:的主要内容,如果未能解决你的问题,请参考以下文章
Hibernate - JPA 在不同的情况下生成具有相同名称的重复表
如何在 JPA 中使用 Postgres JSONB 数据类型?
Postgres - Spring Data JPA - 自动生成数据库和表
无法构建 Hibernate SessionFactory - spring data/jpa/hibernate 逆向工程