2012-09-24 15 views
9

Ich habe ein Problem, wo ich bekomme ein: org.hibernate.MappingException: Keine Dialekt Zuordnung für JDBC Typ: 1111 beim Versuch, eine Postgres-Funktion mit JPA erstellen native Abfrage aufrufen .JPA Hibernate Call Postgres Funktion Void Return MappingException:

Ich habe ein EJB-Timer in einem Start-Singleton einer Postgres-Funktion alle 6 Stunden laufen. Die Funktion gibt void zurück und prüft auf abgelaufene Datensätze, löscht sie und aktualisiert einige Status. Es braucht keine Argumente und es ist ungültig.

  • Die Postgres Funktion läuft perfekt, wenn ich es PgAdmin Abfrage-Tool aufrufen (Auswahlfunktion();) und void zurückgibt.

  • Wenn ich die Anwendung auf Glassfish 3.1.1 bereitstellen erhalte ich eine Ausnahme und einen Fehler zu implementieren.

Das ist der (verkürzte) Stack-Trace:

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 

Hier ist der Code:

Zuerst wird das Material JPA:

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

Dies ist der Singleton es anruft, :

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

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

Und die Funktion:

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; 

Antwort

3

ich genug um mit JPA versucht hatte, durcheinander, um es eine gespeicherte Prozedur auszuführen zu bekommen.

Ich endete mit JDBC mit einer vorbereiteten Anweisung. Ich habe es in 15 Minuten getan, nachdem ich einige fruchtlose Stunden damit verbracht hatte, einen quadratischen Pflock in ein rundes Loch zu stecken. Ich rief dieselbe Jndi-Datenquelle an, die meine Persistenzeinheit verwendet, um eine Verbindung herzustellen, erstellte eine vorbereitete Anweisung und schloss sie, wenn sie fertig war.

Also, wenn Sie eine gespeicherte Prozedur (oder Postgres-Funktion) von einem (heute meist) JPA App laufen, hier ist das, was für mich gearbeitet:

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

Es scheint ein schreckliches Versehen sein zu lassen Aus der einfachen Möglichkeit, eine Funktion oder gespeicherte Prozedur auf dem Server von JPA auszuführen; vor allem eine, die nichts zurückgibt, außer void oder die Anzahl der betroffenen Zeilen. Und wenn es beabsichtigt war ... kein Kommentar.

Edit: hinzugefügt enge Verbindung.

1

Dies wurde vor einiger Zeit gepostet, aber ich hatte das ähnliche Problem. Wie bereits erwähnt, reagiert Hibernate allergisch auf void und versucht, Sie zu sperren, um jedes Mal den Rückgabetyp zu verwenden. Aus meiner Sicht ist dies eine gute Übung, da Sie normalerweise immer eine Rückgabe haben sollten, um zu spezifizieren, ob es erfolgreich war: eine Ausnahme zu werfen ist oft missbräuchlich.

Doch überwintert bieten eine Möglichkeit, es Einschränkungen zu umgehen: org.hibernate.jdbc.Work

Sie können leicht reproduzieren, was in einer kleinen Klasse erforderlich war:

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

Jetzt können Sie es nennen, wenn Sie mithilfe wollen

VoidProcedureWork.execute (hibernateSession, sqlQuery);

Sie werden zwei Dinge über diese Klasse bemerken: 1) Ich schließe die Verbindung NICHT. Ich verlasse es so, weil ich nicht weiß, wer die Verbindung geöffnet hat, wie und warum. Wird dieselbe Verbindung in einer Transaktion verwendet? Wenn ich es schließe und jemand es danach benutzt, wird es abstürzen? Etc. Ich habe Hibernate nicht programmiert und connection.open() nicht verwendet. Deshalb schließe ich es nicht und nehme an, was auch immer geöffnet wird, es wird es auch schließen. 2) Es ist mehr prozedurale Programmierung als OOP. Ich weiß, aber ich bin faul: es ist einfacher (und klarer imo), VoidProcedureWork.execute (Sitzung, SQL) als neue VoidProcedureWork (sql) .execute (Sitzung) zu verwenden. Oder noch schlimmer: wiederhole den Ausführungscode jedes Mal, wenn ich diesen kleinen Trick verwenden will (session.doWork (new VoidProcedureWork (sql)) mit Ausnahmeverarbeitung).

11

Dies könnte ein Hack sein, aber es funktionierte für mich und ist ziemlich einfach. Ändern Sie einfach die Abfrage zu:

Jetzt gibt es eine richtige Ganzzahl und Ruhezustand ist glücklich.

+0

Sehr gute Idee. Ich habe die andere Antwort gesehen und denke, dass deine die Beste ist. – luizcarlosfx

+0

Sowohl Ihre als auch @ Kikin-Sama's Methode hat funktioniert. – BillR

0

Alter! Es ist so einfach, wie den Funktionsnamen zu zitieren. Wie so:

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

Für zukünftige Besucher dieses Problems würde eine Besetzung auch gearbeitet haben. posted on this thread auch

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

Es scheint, dass das Problem auftritt, wenn die Postgres gespeicherte Prozedur void zurückgibt. Versuchen Sie, den Rückgabetyp zu ändern, um einen Dummy-Wert zurückzugeben, möglicherweise eine Zeichenfolge. Das hat in meinem Fall funktioniert.

+0

Ich versuche eine Admin-Funktion von Postgres aufzurufen, die void zurückgibt. pg_advisory_xact_lock ist was ich anrufe. Wie kann ich das ändern? – faizan