【问题标题】:JPA Hibernate Call Postgres Function Void Return MappingException:JPA Hibernate 调用 Postgres 函数 Void 返回 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;

【问题讨论】:

    标签: hibernate function jpa stored-procedures postgresql-9.1


    【解决方案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 相比,这没有提供额外的帮助
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2012-08-16
            • 2020-01-18
            • 2017-12-05
            • 2013-12-06
            • 2016-08-28
            • 2013-05-28
            • 2017-11-09
            相关资源
            最近更新 更多