EJB事务回滚导致不必要的数据库更改/对象持久性

时间:2018-08-29 11:44:45

标签: hibernate jpa java-ee transactions

在发生错误的情况下回滚EJB事务时遇到问题。出现以下情况:我有一个无状态EJB,它试图在一个新事务中保留3个对象。我将Hibernate 5.2用作JPA Provider,并将Payara 4.1.2.173用作应用程序服务器。

@Stateless
public class MyEJB {

    @PersistenceContext(unitName = "myUnit")
    private EntityManager entityManager;

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void persistData() {
        this.log.info("Method start.");
        // valid
        MyObj obj1 = new MyObj();
        obj1.setField1("v1");
        obj1.setField2("v2");
        entityManager.persist(obj1);
        this.log.info("Persisted obj1");

        // valid
        MyObj obj2 = new MyObj();
        obj2.setField1("v1");
        obj2.setField2("v2");
        entityManager.persist(obj2);
        this.log.info("Persisted obj2");

        // failure
        MyObj obj3 = new MyObj();
        obj3.setField1("v1");
        obj3.setField2("FAILURE");
        entityManager.persist(obj3);
        this.log.info("Persisted obj3");

        this.log.info("Method finished.");
    }
}

由于field2上的值无效,obj3的持久性应该失败。从我的ejb外部(例如,从另一个EJB,消息驱动的bean,通过rest等)调用此方法时,在执行休眠插入语句时,日志文件中会出现PersistenceException。因此obj3不会持久保存在数据库中。日志文件还显示“事务标记为回滚”。但是当我查看数据库obj1和obj2时,它们是持久的。因此,恕我直言,回滚尚未完成或不完整。 我也尝试使用@Transactional批注,但没有任何行为更改。我也不能使用try-catch,因为在对象创建和persist(obj)调用时不会发生异常。 在persistence.xml中,将属性hibernate.connection.autocommit设置为false

我的persistence.xml是:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns="http://xmlns.jcp.org/xml/ns/persistence"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
             http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
             version="2.1">
    <persistence-unit name="myUnit" transaction-type="JTA">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <jta-data-source>jdbc/name</jta-data-source>

        <properties>
            <property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.SunOneJtaPlatform"/>
            <property name = "hibernate.show_sql" value = "false" />
            <property name="hibernate.dialect" value="org.hibernate.dialect.Oracle12cDialect" />
            <property name="hibernate.connection.autocommit" value="false" />
        </properties>
    </persistence-unit>
</persistence>

数据库连接在Payaras的domain.xml中的池定义中完成:

<resources>

    <jdbc-resource pool-name="pool-name" jndi-name="jdbc/name"></jdbc-resource>

    <jdbc-connection-pool match-connections="true" connection-leak-reclaim="true" driver-classname="" statement-leak-reclaim="true" 
                          connection-creation-retry-attempts="3" associate-with-thread="true" validation-table-name="DUAL" 
                          description="" validation-classname="" res-type="javax.sql.XADataSource" max-wait-time-in-millis="120000" 
                          fail-all-connections="true" allow-non-component-callers="true" datasource-classname="oracle.jdbc.xa.client.OracleXADataSource" 
                          is-connection-validation-required="true" transaction-isolation-level="read-committed" validate-atmost-once-period-in-seconds="30" 
                          lazy-connection-enlistment="true" connection-creation-retry-interval-in-seconds="7" max-pool-size="45" 
                          connection-leak-timeout-in-seconds="1800" statement-leak-timeout-in-seconds="900" steady-pool-size="10" 
                          name="pool-name" statement-timeout-in-seconds="900" sql-trace-listeners="" init-sql="">
      <property name="Password" value="password"></property>
      <property name="User" value="user"></property>
      <property name="URL" value="jdbc:oracle:thin:@//localhost:1521/db-identifier"></property>
    </jdbc-connection-pool>
</resources>

我的失败的堆栈跟踪显示在以下段落中。如您所见,将创建3个insert语句。在完成persistData()代码之后,Hibernate尝试执行该语句,并且在obj3-insert处发生故障并启动回滚。但是obj1和obj2之后会保留在数据库中。我添加了一些日志语句以更好地理解。

[2018-09-02T17:41:51.111+0200] [Payara 4.1] [INFO] [] [...] [tid: _ThreadID=28 _ThreadName=http-thread-pool::http-listener-1(2)] [timeMillis: 1535903502294] [levelValue: 800] [[
  Method start.]]

[2018-09-02T17:41:51.222+0200] [Payara 4.1] [INFO] [] [...] [tid: _ThreadID=28 _ThreadName=http-thread-pool::http-listener-1(2)] [timeMillis: 1535903502430] [levelValue: 800] [[
  Persisted obj1]]

[2018-09-02T17:41:51.333+0200] [Payara 4.1] [INFO] [] [...] [tid: _ThreadID=28 _ThreadName=http-thread-pool::http-listener-1(2)] [timeMillis: 1535903502431] [levelValue: 800] [[
  Persisted obj2]]

[2018-09-02T17:41:51.444+0200] [Payara 4.1] [INFO] [] [...] [tid: _ThreadID=28 _ThreadName=http-thread-pool::http-listener-1(2)] [timeMillis: 1535903502432] [levelValue: 800] [[
  Persisted obj3]]

[2018-09-02T17:41:51.555+0200] [Payara 4.1] [INFO] [] [...] [tid: _ThreadID=28 _ThreadName=http-thread-pool::http-listener-1(2)] [timeMillis: 1535903502432] [levelValue: 800] [[
  Method finished.]]

[2018-09-02T17:41:51.789+0200] [Payara 4.1] [INFO] [] [] [tid: _ThreadID=27 _ThreadName=http-thread-pool::http-listener-1(1)] [timeMillis: 1535902911789] [levelValue: 800] [[
  Hibernate: insert into ... // obj1

[2018-09-02T17:41:51.929+0200] [Payara 4.1] [INFO] [] [] [tid: _ThreadID=27 _ThreadName=http-thread-pool::http-listener-1(1)] [timeMillis: 1535902911929] [levelValue: 800] [[
  Hibernate: insert into ... // obj2

[2018-09-02T17:41:51.931+0200] [Payara 4.1] [INFO] [] [] [tid: _ThreadID=27 _ThreadName=http-thread-pool::http-listener-1(1)] [timeMillis: 1535902911931] [levelValue: 800] [[
  Hibernate: insert into ... // obj3

[2018-09-02T17:41:51.939+0200] [Payara 4.1] [WARN] [] [org.hibernate.engine.jdbc.spi.SqlExceptionHelper] [tid: _ThreadID=27 _ThreadName=http-thread-pool::http-listener-1(1)] [timeMillis: 1535902911939] [levelValue: 900] [[
  SQL Error: 12899, SQLState: 72000]]

[2018-09-02T17:41:51.939+0200] [Payara 4.1] [ERROR] [] [org.hibernate.engine.jdbc.spi.SqlExceptionHelper] [tid: _ThreadID=27 _ThreadName=http-thread-pool::http-listener-1(1)] [timeMillis: 1535902911939] [levelValue: 1000] [[
  ORA-12899: Wert zu groß für Spalte "FIELD2" (aktuell: 7, maximal: 1)
]]

[2018-09-02T17:41:51.941+0200] [Payara 4.1] [INFO] [] [org.hibernate.engine.jdbc.batch.internal.AbstractBatchImpl] [tid: _ThreadID=27 _ThreadName=http-thread-pool::http-listener-1(1)] [timeMillis: 1535902911941] [levelValue: 800] [[
  HHH000010: On release of batch it still contained JDBC statements]]

[2018-09-02T17:41:51.946+0200] [Payara 4.1] [ERROR] [] [org.hibernate.internal.ExceptionMapperStandardImpl] [tid: _ThreadID=27 _ThreadName=http-thread-pool::http-listener-1(1)] [timeMillis: 1535902911946] [levelValue: 1000] [[
  HHH000346: Error during managed flush [org.hibernate.exception.GenericJDBCException: could not execute statement]]]

[2018-09-02T17:41:51.946+0200] [Payara 4.1] [WARNING] [] [javax.enterprise.resource.jta.com.sun.enterprise.transaction] [tid: _ThreadID=27 _ThreadName=http-thread-pool::http-listener-1(1)] [timeMillis: 1535902911946] [levelValue: 900] [[
  DTX5014: Caught exception in beforeCompletion() callback:
javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: could not execute statement
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:147)
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:155)
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:162)
    at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1434)
    at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:484)
    at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3190)
    at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2404)
    at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:467)
    at org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorImpl.beforeCompletion(JtaTransactionCoordinatorImpl.java:320)
    at org.hibernate.resource.transaction.backend.jta.internal.synchronization.SynchronizationCallbackCoordinatorNonTrackingImpl.beforeCompletion(SynchronizationCallbackCoordinatorNonTrackingImpl.java:47)
    at org.hibernate.resource.transaction.backend.jta.internal.synchronization.RegisteredSynchronization.beforeCompletion(RegisteredSynchronization.java:37)
    at com.sun.enterprise.transaction.JavaEETransactionImpl.commit(JavaEETransactionImpl.java:452)
    at com.sun.enterprise.transaction.JavaEETransactionManagerSimplified.commit(JavaEETransactionManagerSimplified.java:879)
    at com.sun.ejb.containers.EJBContainerTransactionManager.completeNewTx(EJBContainerTransactionManager.java:721)
    at com.sun.ejb.containers.EJBContainerTransactionManager.postInvokeTx(EJBContainerTransactionManager.java:505)
    at com.sun.ejb.containers.BaseContainer.postInvokeTx(BaseContainer.java:4536)
    at com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:2023)
    at com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:1994)
    at com.sun.ejb.containers.EJBLocalObjectInvocationHandler.invoke(EJBLocalObjectInvocationHandler.java:220)
    at com.sun.ejb.containers.EJBLocalObjectInvocationHandlerDelegate.invoke(EJBLocalObjectInvocationHandlerDelegate.java:90)
    at com.sun.proxy.$Proxy406.testImport(Unknown Source)
    at ...
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory$1.invoke(ResourceMethodInvocationHandlerFactory.java:81)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:144)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:161)
    at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ResponseOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:160)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:99)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:389)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:347)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:102)
    at org.glassfish.jersey.server.ServerRuntime$2.run(ServerRuntime.java:326)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:267)
    at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:317)
    at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:305)
    at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1154)
    at org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:473)
    at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:427)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:388)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:341)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:228)
    at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1606)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:258)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:160)
    at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:654)
    at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:593)
    at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:155)
    at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:371)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:238)
    at com.sun.enterprise.v3.services.impl.ContainerMapper$HttpHandlerCallable.call(ContainerMapper.java:480)
    at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:180)
    at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:206)
    at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:180)
    at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:235)
    at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:284)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:201)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:133)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:112)
    at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
    at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:539)
    at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:112)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:117)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:56)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:137)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:593)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:573)
    at java.lang.Thread.run(Thread.java:748)
Caused by: org.hibernate.exception.GenericJDBCException: could not execute statement
    at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:47)
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:111)
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:97)
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:207)
    at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:45)
    at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3001)
    at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3501)
    at org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:89)
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:586)
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:460)
    at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:337)
    at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39)
    at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1428)
    ... 72 more
Caused by: java.sql.SQLException: ORA-12899: Wert zu groß für Spalte "FIELD2" (aktuell: 7, maximal: 1)

    at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:450)
    at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:399)
    at oracle.jdbc.driver.T4C8Oall.processError(T4C8Oall.java:1059)
    at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:522)
    at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:257)
    at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:587)
    at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:225)
    at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:53)
    at oracle.jdbc.driver.T4CPreparedStatement.executeForRows(T4CPreparedStatement.java:943)
    at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1150)
    at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:4798)
    at oracle.jdbc.driver.OraclePreparedStatement.executeUpdate(OraclePreparedStatement.java:4875)
    at oracle.jdbc.driver.OraclePreparedStatementWrapper.executeUpdate(OraclePreparedStatementWrapper.java:1361)
    at com.sun.gjc.spi.base.PreparedStatementWrapper.executeUpdate(PreparedStatementWrapper.java:125)
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:204)
    ... 81 more
]]

有人可以解释为什么执行有效的insert语句而不回滚,以及我可以做些什么来实现完全回滚吗?我想要的行为是将所有条目插入到事务中的数据库中,或者根本不插入任何条目(以防出现任何错误)。

1 个答案:

答案 0 :(得分:0)

最后,我解决了我的问题。问题是在我使用过的Payara服务器配置中,未注册jdbc资源。将其添加到domain.xml中的服务器配置中后,一切正常。

<resources>
    <jdbc-resource pool-name="pool-name" jndi-name="jdbc/name"></jdbc-resource>
    <jdbc-connection-pool ...> </jdbc-connection-pool>
</resources>
...
<servers>
  <server config-ref="server-config" name="server">
    ...
    <resource-ref ref="jdbc/name"></resource-ref>
  </server>
</servers>