2012-09-24 15 views
9

Mam problem, w którym dostaję: org.hibernate.MappingException: Brak odwzorowania dialektów dla typu JDBC: 1111 przy próbie wywołania funkcji postgres przy użyciu JPA utwórz zapytanie natywne .JPA Hibernate Call Postgres Funkcja Void Return MappingException:

Utworzono timer EJB w singletonie startowym, aby uruchomić funkcję PostgreS co 6 godzin. Funkcja zwraca nieważne i sprawdza wygasłe rekordy, usuwa je i aktualizuje niektóre statusy. Nie przyjmuje argumentów i zwraca nieważne.

  • Funkcja postgres działa perfekcyjnie jeśli zgłoszę go za pomocą narzędzia kwerendy pgAdmin (select function();) i zwraca void.

  • Po wdrożeniu aplikacji na Glassfish 3.1.1 otrzymuję wyjątek i niepowodzenie w instalacji.

To (skrócony) ślad stosu:

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 

Oto kod:

Najpierw rzeczy JPA:

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

to jest Singleton nazywając go :

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

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

a funkcja:

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; 

Odpowiedz

3

miałem wystarczająco aprowizacji z JPA próbuje go uruchomić procedurę przechowywaną.

Skończyłem z wykorzystaniem JDBC z przygotowanym oświadczeniem. Zrobiłem to w 15 minut po spędzeniu kilku bezowocnych godzin, próbując dopasować kwadratowy kołek do okrągłej dziury. Nazwałem to źródło danych Jndi, którego moja jednostka wytrwałości używa do uzyskania połączenia, utworzyło przygotowane oświadczenie i zamknęło je po zakończeniu.

Więc jeśli trzeba uruchomić procedurę przechowywaną (lub funkcja Postgres) z (obecnie w większości) aplikacji JPA, tutaj jest to, co pracował dla mnie:

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

Nie wydaje się być straszną nadzór opuścić out prosta zdolność do uruchamiania funkcji lub procedury składowanej na serwerze z JPA; zwłaszcza taki, który nie zwraca niczego poza pustką lub liczbą uszkodzonych wierszy. A jeśli to było celowe ... bez komentarza.

Edytuj: dodano ścisłe połączenie.

1

Zostało to opublikowane jakiś czas temu, ale miałem podobny problem. Jak stwierdzono, Hibernate wygląda alergicznie na nieważne i będzie próbował zablokować użytkownika za pomocą typu zwrotu za każdym razem. Z mojego punktu widzenia jest to dobra praktyka, ponieważ normalnie zawsze powinieneś mieć przynajmniej powrót, żeby określić, czy się udało: wyrzucanie wyjątków jest często nadużyciem.

jednak, Hibernate daje sposób, aby ominąć to ograniczenia: org.hibernate.jdbc.Work

Można łatwo odtworzyć to, co było wymagane w małej klasie:

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

Teraz można nazwać to, kiedy tylko chcesz przy użyciu

VoidProcedureWork.execute (hibernateSession, sqlQuery);

Zobaczysz dwie rzeczy o tej klasie: 1) NIE zamykam połączenia. Pozostawiam to w ten sposób, ponieważ nie wiem, kto otworzył połączenie, jak i dlaczego. Czy to samo połączenie jest używane w transakcji? Jeśli zamknę to, a ktoś go użyje po, czy się zawiesi? Itd. Nie zakodowałem Hibernate i nie korzystałem z connection.open(). Dlatego nie zamykam go i nie zakładam, że to, co zostało otwarte, również je zamknie. 2) To jest bardziej programowanie proceduralne niż OOP. Wiem, ale jestem leniwy: łatwiej (i bardziej wyraźnie) użyć VoidProcedureWork.execute (session, sql) niż nowy VoidProcedureWork (sql) .execute (session). A nawet najgorsze: odtworzyć kod wykonywalny za każdym razem, gdy chcę użyć tej małej sztuczki (session.doWork (nowy VoidProcedureWork (sql)) z przetwarzaniem wyjątków).

11

To może być hack, ale zadziałało to dla mnie i jest całkiem proste. Wystarczy zmienić zapytanie na:

SELECT count(*) FROM your_function(); 

Teraz zwraca odpowiednią liczbę całkowitą i hibernacja jest szczęśliwa.

+0

Bardzo dobry pomysł. Widziałem inną odpowiedź i myślę, że twoja jest najlepsza. – luizcarlosfx

+0

Zarówno Twoja, jak i metoda @ Kikin-Sama działały. – BillR

0

Koleś! To tak proste, jak cytowanie nazwy funkcji. Tak jak w przypadku:

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

Dla przyszłych odwiedzających ten numer działałaby również obsada. pisał na this thread oraz

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

Wydaje się, że problem występuje, gdy postgres procedura przechowywana zwraca void. Spróbuj zmienić typ zwracany, aby zwrócić jakąś fałszywą wartość, być może ciąg znaków. To działało w moim przypadku.

+0

Próbuję wywołać funkcję administratora postgres, który zwraca void. pg_advisory_xact_lock to to, co nazywam. jak mogę to zmienić? – faizan

Powiązane problemy