У меня проблема с откатом 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 должно завершиться ошибкой из-за недопустимого значения в field2. При вызове этого метода извне моего ejb (например, из другого EJB, bean-объекта, управляемого сообщениями, через rest ...) в файлах журналов возникает PersistenceException
при выполнении операторов вставки hibernate. Таким образом, 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.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-вставке происходит сбой и инициируется откат. Но 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
]]
Может кто-нибудь объяснить, почему действительные операторы вставки выполняются, а не откатываются, и что я могу сделать, чтобы добиться полного отката? Мне хотелось бы вставить все записи в базу данных в рамках моей транзакции или вообще не вводить записи (в случае ошибки).