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



我在发生错误时回滚 EJB 事务时遇到了问题。以下情况:我有一个无状态的 EJB,它试图在新事务中保留 3 个对象。我使用 Hibernate 5.2 作为 JPA 提供程序,使用 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.");
}
}

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

我的坚持.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 域中的池定义中完成.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 个插入语句。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
]]

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

终于我解决了我的问题。问题是在我使用的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>

最新更新