13

我在尝试使用 JPA 创建本机查询调用 postgres 函数时遇到一个问题:org.hibernate.MappingException: No Dialect mapping for JDBC type: 1111 。

我在启动单例中创建了一个 EJB 计时器,以每 6 小时运行一次 Postgres 函数。该函数返回 void 并检查过期记录、删除它们并更新一些状态。它不接受任何参数,并且返回 void。

  • 如果我使用 PgAdmin 查询工具 (select function();) 调用 postgres 函数并返回 void,则 postgres 函数运行完美。

  • 当我在 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;
4

7 回答 7

29

这可能是一个 hack,但它对我有用并且非常简单。只需将查询更改为:

SELECT count(*) FROM your_function();

现在它返回一个正确的整数,hibernate 很高兴。

于 2013-08-26T08:02:10.413 回答
5

我已经对 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 或受影响的行数之外什么都不返回的。如果这是故意的……没有评论。

编辑:添加关闭连接。

于 2012-09-24T19:59:36.003 回答
3

对于这个问题的未来访问者,演员也可以工作。也发布在这个线程

public void runRequestCleanup() {
    String queryString = "SELECT cast(a_function_that_hibernate_chokes_on() as text)";
    Query query = em.createNativeQuery(queryString);
    query.getSingleResult();
}
于 2015-08-24T20:22:16.803 回答
2

当 postgres 存储过程返回 void 时,似乎会出现问题。尝试更改返回类型以返回一些虚拟值,可能是字符串。这在我的情况下有效。

于 2016-02-02T17:28:06.257 回答
1

这是前一段时间发布的,但我遇到了类似的问题。如前所述,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)) 和异常处理)时都重现执行代码。

于 2013-01-03T19:13:55.603 回答
0

老兄!就像引用函数名一样简单。像这样:

public void runRequestCleanup() {
    String queryString = "SELECT \"a_function_that_hibernate_chokes_on\"()";
    Query query = em.createNativeQuery(queryString);
    Object result = query.getSingleResult();
}
于 2014-06-11T20:49:26.750 回答
-1

将查询转换为:

SELECT cast(sqlFunction() as text)

例如:"SELECT cast(pushlogsToMainTable() as text)"

它对我有用。

于 2020-05-20T20:50:49.490 回答