2012-09-24 18 views
9

Tengo un problema donde recibo una: org.hibernate.MappingException: No Dialecto mapping para tipo JDBC: 1111 cuando intento llamar a una función postgres usando JPA create native query .JPA Hibernate Llamada Postgres Función Void Return MappingException:

Creé un temporizador EJB en un singleton de inicio para ejecutar una función de Postgres cada 6 horas. La función devuelve vacío y comprueba si hay registros caducados, los elimina y actualiza algunos estados. No requiere argumentos y vuelve vacío.

  • La función postgres funciona perfectamente si yo lo llamo el uso de herramienta de consulta PgAdmin (selección de la función();) y devuelve void.

  • Cuando implemente la aplicación en Glassfish 3.1.1 recibo una excepción y una falla al implementar.

Este es el (abreviado) seguimiento de la pila:

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 

Aquí está el código:

Primero las cosas JPA:

public void runRequestCleanup() { 
    String queryString = "SELECT a_function_that_hibernate_chokes_on()"; 
    Query query = em.createNativeQuery(queryString); 
    Object result = query.getSingleResult(); 
} 

Esto se llama el singleton se :

@Startup 
@Singleton 
public class RequestCleanupTimer { 
    @Resource 
    TimerService timerService; 
    @EJB 
    UserQueryBean queryBean; 

    @PostConstruct 
    @Schedule(hour = "*/6") 
    void runCleanupTimer() { 
     queryBean.runRequestCleanup(); 
    } 
} 

Y la función:

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; 

Respuesta

3

suficiente que había de jugar un poco con la APP tratando de conseguir que se ejecute un procedimiento almacenado.

Terminé usando JDBC con una declaración preparada. Lo hice en 15 minutos después de pasar varias horas infructuosas tratando de meter una clavija cuadrada en un agujero redondo. Llamé a la misma fuente de datos jndi que usa mi unidad de persistencia para obtener una conexión, creé una declaración preparada y la cerré cuando terminé.

Así que si usted necesita para ejecutar un procedimiento almacenado (o función de Postgres) de una (ahora en su mayoría) JPA aplicación, esto es lo que funcionó para mí:

@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  
    } 
    ... 
} 

No parece haber una supervisión horrible para dejar la simple capacidad de ejecutar una función o procedimiento almacenado en el servidor desde JPA; especialmente uno que no devuelve nada excepto vacío o el número de filas afectadas. Y si fue deliberado ... sin comentarios.

Editar: se ha añadido la conexión cercana.

1

Esto fue publicado hace un tiempo, pero tuve el problema similar. Como se dijo, Hibernate parece alérgico al vacío y tratará de encerrarlo en el uso del tipo de devolución cada vez. Desde mi punto de vista, esta es una buena práctica, ya que normalmente siempre debería tener un retorno al menos para especificar si tuvo éxito: lanzar una excepción a menudo es abusivo.

embargo, Hibernate ofrecer una manera de evitar sus limitaciones: org.hibernate.jdbc.Work

se pueden reproducir fácilmente lo que se requiere en una clase pequeña:

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(); 
     } 
    } 
} 

Ahora se le puede llamar siempre que lo desee mediante el uso de

VoidProcedureWork.execute (hibernateSession, sqlQuery);

Notarás dos cosas sobre esta clase: 1) NO cierro la conexión. Lo dejo así porque no sé quién abrió la Conexión, cómo y por qué. ¿Se usa la misma conexión en una transacción? Si lo cierro y alguien lo usa después, ¿se bloqueará? Etc. No codifiqué Hibernate y no usé connection.open(). Por lo tanto, no lo cierro y supongo que cualquier cosa que se abra lo cerrará también. 2) Es más programación de procedimiento que OOP. Lo sé, pero soy flojo: es más fácil (y más claro) usar VoidProcedureWork.execute (session, sql) que el nuevo VoidProcedureWork (sql) .execute (session). O peor: reproducir el código de ejecución cada vez que quiera usar ese pequeño truco (session.doWork (new VoidProcedureWork (sql)) con procesamiento de excepción.

11

Esto podría ser un truco, pero funcionó para mí y es bastante simple. Simplemente cambie la consulta a:

SELECT count(*) FROM your_function(); 

Ahora devuelve un número entero correcto e hibernate es feliz.

+0

Muy buena idea. Vi la otra respuesta y creo que la tuya es la mejor. – luizcarlosfx

+0

Tanto tuyo como el método de @Kikin-Sama funcionaron. – BillR

0

¡Amigo! Es tan fácil como citar el nombre de la función. De esta manera:

public void runRequestCleanup() { 
    String queryString = "SELECT \"a_function_that_hibernate_chokes_on\"()"; 
    Query query = em.createNativeQuery(queryString); 
    Object result = query.getSingleResult(); 
} 
1

Para los futuros visitantes de esta edición, un elenco también habría funcionado. publicado en this thread así

public void runRequestCleanup() { 
    String queryString = "SELECT cast(a_function_that_hibernate_chokes_on() as text)"; 
    Query query = em.createNativeQuery(queryString); 
    query.getSingleResult(); 
} 
2

Parece que el problema se produce cuando los postgres almacenados procedimiento devuelve vacío. Intente cambiar el tipo de devolución para devolver algún valor ficticio, quizás una cadena. Esto funcionó en mi caso.

+0

Estoy tratando de llamar a una función de administración de postgres que devuelve vacía. pg_advisory_xact_lock es lo que llamo. ¿Cómo puedo cambiar eso? – faizan