调用getNextException以查看原因:如何使Hibernate / JPA显示数据库服务器消息以查找exception

我正在使用Postgresql,Hibernate和JPA。 每当数据库中出现exception时,我都会得到类似这样的东西,因为它没有显示DB服务器上真正出错的地方。

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 

我希望数据库中的exception消息出现在应用程序的日志中。

我遇到过这篇文章 ,它使用一个Aspect来填充exception链,否则在SQLExceptions的情况下,该exception链没有正确填充。

有没有办法解决这个问题,而无需使用Aspects或任何自定义代码。 理想的解决方案只涉及配置文件更改。

没有必要编写任何自定义代码来实现这一点 – Hibernate将默认记录exception原因。 如果您看不到这一点,则不能正确设置Hibernate日志记录。 这是slf4j + log4j的一个示例,并使用Maven进行依赖关系管理。

的src /主/爪哇/ 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 /主/资源/ 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 /主/资源/ META-INF / persistence.xml中

             

的pom.xml

  4.0.0 pgextest pgextest 0.0.1-SNAPSHOT    maven-compiler-plugin  1.6 1.6       org.hibernate hibernate-entitymanager 3.6.9.Final   postgresql postgresql 9.1-901.jdbc4 runtime   org.slf4j slf4j-log4j12 1.6.1 runtime   log4j log4j 1.2.15 runtime    

执行main方法将记录以下内容:

 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" 

值得一提的是,您可以通过将属性hibernate.jdbc.batch_size设置为0来禁用包装原始exception的JDBC批处理(不用说您可能不希望在生产中执行此操作。)

这对我有用,可以获得导致问题的exception消息(Hibernate 3.2.5.ga):

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

我认为Aspect编程是解决此类问题的更好解决方案。

但是,如果您想编写自定义代码来执行此操作,则可以捕获SqlException并循环遍历它并记录每个exception。 这样的事情应该有效。

 try { // whatever your code is } catch (SQLException e) { while(e!= null) { logger.log(e); e = e.getNextException(); } } 
 try { // code } catch (SQLException e) { for (Throwable throwable : e) { log.error("{}", throwable); } } 

对我来说exception是PersistenceException,所以我必须这样做:

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

如果碰巧你从JUnit测试中得到这个exception,你可以用TestRule转换JUnitexception(这是受ExpectedException TestRule的来源启发)

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

然后将规则添加到测试用例

 public class SomeTest { @Rule public HibernateBatchUnwindRule batchUnwindRule = HibernateBatchUnwindRule.create(); @Test public void testSomething(){...} } 

如果您有可能从Kafka-Connect遇到此exception,则可以将batch.size属性设置为0(暂时)以显示接收器工作者遇到的exception。