2013-04-06 14 views
23

Używam PostgreSQL, Hibernacji i JPA. Ilekroć w bazie danych występuje wyjątek, pojawia się coś takiego, co nie jest zbyt pomocne, ponieważ nie pokazuje, co naprawdę poszło nie tak na serwerze DB.Wywołanie getNextException, aby zobaczyć przyczynę: Jak zrobić Hibernate/JPA pokazać komunikat serwera bazy danych dla wyjątku

Caused by: java.sql.BatchUpdateException: Batch entry 0 update foo set ALERT_FLAG='3' was aborted. Call getNextException to see the cause. 
    at org.postgresql.jdbc2.AbstractJdbc2Statement$BatchResultHandler.handleError(AbstractJdbc2Statement.java:2621) 
    at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1837) 
    at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:407) 
    at org.postgresql.jdbc2.AbstractJdbc2Statement.executeBatch(AbstractJdbc2Statement.java:2754) 
    at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeBatch(NewProxyPreparedStatement.java:1723) 
    at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:70) 
    at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:268) 
    ... 82 more 

Chcę, aby komunikat wyjątku z bazy danych pojawił się w dzienniku aplikacji.

Natknąłem się na this article, który używa aspektu do zapełnienia łańcucha wyjątków, który w przypadku SQLExceptions nie jest prawidłowo wypełniany.

Czy istnieje sposób, aby to naprawić bez użycia Aspects lub niestandardowego kodu. Idealne rozwiązanie wymagałoby tylko zmian w plikach konfiguracyjnych.

+0

W jaki sposób wypisujesz wyjątek? Moje doświadczenie z log4j i slf4j, dostaję spowodowane przez linie za darmo ... – mjwenk

+0

Biblioteki rejestrowania wydrukują przyczynę, ale robią tak, jeśli excption następuje zgodnie z konwencją łańcuchów wyjątków. tj. każdy wyjątek powinien zwracać swoją bezpośrednią przyczynę źródłową za pomocą metody getCause(). Wygląda na to, że wyjątek SQLException nie jest zgodny z tą konwencją. Łączony artykuł wyjaśnia to samo. Autor napisał zatem aspekt, który pobiera obiekt zwracany przez getNextException() i ustawia go jako przyczynę w wyjątku nadrzędnym. – Dojo

+1

@Priyank 'SQLException' używa łańcucha wyjątków (który można iterować za pomocą iteratora lub używając' getNextException() '), gdy wystąpi wiele wyjątków (niepowiązanych?). Ta koncepcja jest prostopadła do łańcucha przyczyn. W tym konkretnym przypadku uważam, że powinna być ustalona jako przyczyna. –

Odpowiedz

6

Nie ma potrzeby pisania niestandardowego kodu, aby to osiągnąć - Hibernacja domyślnie rejestruje przyczynę wyjątku. Jeśli tego nie widzisz, rejestrowanie w trybie hibernacji nie może być poprawnie skonfigurowane. Oto przykład z slf4j + log4j i używanie Maven do zarządzania zależnościami.

src/main/java/pgextest/PGExceptionTest.java

public class PGExceptionTest { 

    public static void main(String[] args) throws Exception { 

     EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory(
       "pgextest"); 
     EntityManager entityManager = entityManagerFactory.createEntityManager(); 
     entityManager.getTransaction().begin(); 
     // here I attempt to persist an object with an ID that is already in use 
     entityManager.persist(new PGExceptionTestBean(1)); 
     entityManager.getTransaction().commit(); 
     entityManager.close(); 
    } 
} 

src/main/resources/log4j.properties

log4j.rootLogger=ERROR, stdout 

log4j.appender.stdout=org.apache.log4j.ConsoleAppender 
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n 

src/main/resources/META -INF/persistence.xml

<persistence xmlns="http://java.sun.com/xml/ns/persistence" 
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" 
     version="2.0"> 
    <persistence-unit name="pgextest"> 
     <properties> 
      <property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver"/> 
      <property name="javax.persistence.jdbc.url" value="jdbc:postgresql://localhost/pgextest"/> 
      <property name="javax.persistence.jdbc.user" value="postgres"/> 
      <property name="javax.persistence.jdbc.password" value="postgres"/> 
      <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/> 
      <property name="hibernate.jdbc.batch_size" value="5"/> 
     </properties> 
    </persistence-unit> 
</persistence> 

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 

    <modelVersion>4.0.0</modelVersion> 

    <groupId>pgextest</groupId> 
    <artifactId>pgextest</artifactId> 
    <version>0.0.1-SNAPSHOT</version> 

    <build> 
     <plugins> 
      <plugin> 
       <artifactId>maven-compiler-plugin</artifactId> 
       <configuration> 
        <source>1.6</source> 
        <target>1.6</target> 
       </configuration> 
      </plugin> 
     </plugins> 
    </build> 

    <dependencies> 
     <dependency> 
      <groupId>org.hibernate</groupId> 
      <artifactId>hibernate-entitymanager</artifactId> 
      <version>3.6.9.Final</version> 
     </dependency> 

     <dependency> 
      <groupId>postgresql</groupId> 
      <artifactId>postgresql</artifactId> 
      <version>9.1-901.jdbc4</version> 
      <scope>runtime</scope> 
     </dependency> 

     <dependency> 
      <groupId>org.slf4j</groupId> 
      <artifactId>slf4j-log4j12</artifactId> 
      <version>1.6.1</version> 
      <scope>runtime</scope> 
     </dependency> 

     <dependency> 
      <groupId>log4j</groupId> 
      <artifactId>log4j</artifactId> 
      <version>1.2.15</version> 
      <scope>runtime</scope> 
     </dependency> 
    </dependencies> 
</project> 

Wykonywanie główną metodę następnie zalogować się, co następuje:

ERROR [main] - Batch entry 0 insert into PGExceptionTestBean (label, id) values (NULL, '1') was aborted. Call getNextException to see the cause. 
ERROR [main] - ERROR: duplicate key value violates unique constraint "pgexceptiontestbean_pkey" 

To chyba warto wspomnieć, że można wyłączyć dozowanie JDBC, która otacza oryginalnego wyjątku przez ustawienie Właściwość hibernate.jdbc.batch_size do 0 (nie trzeba dodawać, że prawdopodobnie nie chcesz tego robić w produkcji.)

+0

Tak! Wiedziałem, czego można się spodziewać po komunikacie o wyjątku, tak przeszukiwanym, że w logu konsoli i znalazłem jedną małą linię z komunikatem. Tak więc pokazuje się, ale bez dzwonków i gwizdków pełnego śladu stosu. To mi wystarczy. – Dojo

+0

Czy można zrobić coś takiego w Play 2.2.x? Używa ona logback do logowania – Yar

+1

W moim przypadku "java.sql.BatchUpdateException: Batch entry 0 usuń z konta, gdzie id = 32 zostało przerwane. Wywołaj getNextException, aby zobaczyć przyczynę ..." org.slf4j + log4j było rozwiązaniem. Poziom dziennika musi być ustawiony na DEBUG, aby zobaczyć przyczynę podstawową. – Picrochole

4

Myślę, że programowanie aspektu jest lepszym rozwiązaniem w celu rozwiązania tego rodzaju problemu.

Ale jeśli chcesz napisać niestandardowy kod, aby to zrobić, możesz złapać wyjątek SqlException i przechodzić przez niego i rejestrować każdy wyjątek. Coś takiego powinno zadziałać.

try { 
-- whatever your code is 
} catch (SQLException e) { 
logger.log(e); 
Exception exception = e; 
    while(exception.getNextException() != null) { 
     logger.log(exception.getNextException()); //whatever you want to print out of exception 
     exception = exception.getNextException(); 
    } 
} 
+0

To wcale nie wygląda dobrze. Wywołujesz getNextException trzy razy dla każdego wyjątku !! –

+0

Możesz go zoptymalizować do jednego, biorąc zmienną na zewnątrz ... ale technicznie nie ma w tym nic złego ... da ci ten sam wyjątek, ponieważ jest to ... –

+0

Ah, dobra, stoję poprawiony –

1

Tylko w przypadku, jeśli tak się dzieje, że są coraz to wyjątek od wewnątrz testu JUnit można przekształcić wyjątek JUnit z TestRule (to była inspirowana przez źródła ExpectedExceptionTestRule)

public class HibernateBatchUnwindRule implements TestRule { 

    private boolean handleAssumptionViolatedExceptions = false; 

    private boolean handleAssertionErrors = false; 

    private HibernateBatchUnwindRule() { 
    } 

    public static HibernateBatchUnwindRule create(){ 
     return new HibernateBatchUnwindRule(); 
    } 

    public HibernateBatchUnwindRule handleAssertionErrors() { 
     handleAssertionErrors = true; 
     return this; 
    } 

    public HibernateBatchUnwindRule handleAssumptionViolatedExceptions() { 
     handleAssumptionViolatedExceptions = true; 
     return this; 
    } 

    public Statement apply(Statement base, 
      org.junit.runner.Description description) { 
     return new ExpectedExceptionStatement(base); 
    } 

    private class ExpectedExceptionStatement extends Statement { 
     private final Statement fNext; 

     public ExpectedExceptionStatement(Statement base) { 
      fNext = base; 
     } 

     @Override 
     public void evaluate() throws Throwable { 
      try { 
       fNext.evaluate(); 
      } catch (AssumptionViolatedException e) { 
       optionallyHandleException(e, handleAssumptionViolatedExceptions); 
      } catch (AssertionError e) { 
       optionallyHandleException(e, handleAssertionErrors); 
      } catch (Throwable e) { 
       handleException(e); 
      } 
     } 
    } 

    private void optionallyHandleException(Throwable e, boolean handleException) 
      throws Throwable { 
     if (handleException) { 
      handleException(e); 
     } else { 
      throw e; 
     } 
    } 

    private void handleException(Throwable e) throws Throwable { 
     Throwable cause = e.getCause(); 
     while (cause != null) { 
      if (cause instanceof BatchUpdateException) { 
       BatchUpdateException batchUpdateException = (BatchUpdateException) cause; 
       throw batchUpdateException.getNextException(); 
      } 
      cause = cause.getCause(); 
     }; 
     throw e; 
    } 
} 

a następnie dodać regułę do testu

public class SomeTest { 

    @Rule 
    public HibernateBatchUnwindRule batchUnwindRule = HibernateBatchUnwindRule.create(); 

    @Test 
    public void testSomething(){...} 
    } 
3
try { 
// code 
} catch (SQLException e) {  
    for (Throwable throwable : e) { 
     log.error("{}", throwable); 
    } 
} 
+0

Uwielbiam pomysł. Symbole zastępcze nie są dla mnie zastępowane na log4j 1.2.15, więc napiszę własną wiadomość, aby pójść ze śladami. –

12

ten pracował dla mnie, aby uzyskać Wiadom wyjątku e co spowodowało problem (Hibernate 3.2.5.ga):

catch (JDBCException jdbce) { 
    jdbce.getSQLException().getNextException().printStackTrace(); 
} 
+0

można bezpośrednio przejść z, 'catch (SQLException e) {e.getNextException(). PrintStackTrace(); } ' –

3

Dla mnie wyjątek był PersistenceException, więc musiałem to zrobić:

try { 
//... 
} catch (javax.persistence.PersistenceException e) { 
    log.error(((java.sql.BatchUpdateException) e.getCause().getCause()).getNextException()); 
} 
0

Jeśli przez jakiś przypadek napotkania ten wyjątek od Kafka-Connect, możesz ustawić właściwość batch.size na 0 (tymczasowo), aby ujawnić wyjątek napotkany przez twojego pracownika sink.

Powiązane problemy