it-swarm.com.de

Rufen Sie getNextException auf, um die Ursache zu sehen: So legen Sie fest, dass Hibernate/JPA die DB-Servernachricht für eine Ausnahme anzeigt

Ich verwende Postgresql, Hibernate und JPA. Wenn es eine Ausnahme in der Datenbank gibt, bekomme ich so etwas, was nicht sehr hilfreich ist, da es nicht zeigt, was auf dem DB-Server wirklich schief gegangen ist.

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

Ich möchte, dass die Ausnahmemeldung aus der Datenbank im Anwendungsprotokoll angezeigt wird.

Ich bin auf diesen Artikel gestoßen, der einen Aspekt verwendet, um die Ausnahmekette aufzufüllen, die sonst bei SQLExceptions nicht richtig aufgefüllt wird.

Gibt es eine Möglichkeit, dies zu beheben, ohne Aspects oder benutzerdefinierten Code zu verwenden. Die ideale Lösung würde nur Änderungen an Konfigurationsdateien beinhalten.

26
Dojo

Dazu müssen Sie keinen benutzerdefinierten Code schreiben. Hibernate protokolliert standardmäßig die Ausnahmebedingung. Wenn Sie dies nicht sehen können, muss die Hibernate-Protokollierung nicht richtig eingerichtet sein. Hier ist ein Beispiel mit slf4j + log4j und der Verwendung von Maven für die Verwaltung von Abhängigkeiten.

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>

Die Ausführung der Hauptmethode protokolliert dann Folgendes:

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"

Es ist wahrscheinlich erwähnenswert, dass Sie das JDBC-Batching deaktivieren können, das die ursprüngliche Ausnahme umschließt, indem Sie die Eigenschaft hibernate.jdbc.batch_size auf 0 setzen (unnötig zu erwähnen, dass Sie dies wahrscheinlich in der Produktion nicht möchten.)

7
ryanp

Dies funktionierte für mich, um die Ausnahmemeldung zu erhalten, die das Problem verursacht hat (Hibernate 3.2.5.ga):

catch (JDBCException jdbce) {
    jdbce.getSQLException().getNextException().printStackTrace();
}
13
user3652083

Ich denke, dass Aspect Programming eine bessere Lösung ist, um diese Art von Problem zu lösen.

Wenn Sie jedoch einen benutzerdefinierten Code dafür schreiben möchten, können Sie SqlException abfangen, durchlaufen und jede Ausnahme protokollieren. So etwas sollte funktionieren.

try {
 // whatever your code is
} catch (SQLException e) {
    while(e!= null) {
      logger.log(e);
      e = e.getNextException();
    }
}
4
Ankit Bansal

Für mich war die Ausnahme eine PersistenceException, also musste ich folgendes tun:

try {
//...
} catch (javax.persistence.PersistenceException e) {
    log.error(((Java.sql.BatchUpdateException) e.getCause().getCause()).getNextException());
}
4
Amalgovinus
try {
 // code
} catch (SQLException e) {      
  for (Throwable throwable : e) {
        log.error("{}", throwable);
  }
}
3
kayz1

Für den Fall, dass Sie diese Ausnahme innerhalb eines JUnit-Tests erhalten, können Sie die JUnit-Ausnahme mit einer TestRule transformieren (dies wurde durch die Quelle ExpectedExceptionTestRule inspiriert.) )

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

und fügen Sie die Regel dann dem Testfall hinzu

public class SomeTest {

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

    @Test
    public void testSomething(){...}
  }
1
Boris Treukhov

Wenn Sie zufällig auf diese Ausnahme von Kafka-Connect stoßen, können Sie die Eigenschaft batch.size (vorübergehend) auf 0 setzen, um die von Ihrem Sink-Worker festgestellte Ausnahme anzuzeigen.

0
mancini0