UnexpectedRollbackException - JTA transaction unexpectedly rolled back - spring

When I run the below code, I get UnexpectedRollbackException instead of ArrayIndexOutOfBoundsException
Environment: Weblogic, Spring, Hibernate, JTA and EJB
Code:
public class ServiceImpl
{
#Transactional
public void test()
{
throw new ArrayIndexOutOfBoundsException();
}
}
public class WebLayerClass
{
service.test();
}
log:
Caused by: org.springframework.transaction.UnexpectedRollbackException: JTA transaction unexpectedly rolled back (maybe due to a timeout); nested exception is weblogic.transaction.RollbackException: setRollbackOnly called on transaction
at org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.java:1024) ~[spring-tx-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:757) ~[spring-tx-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:726) ~[spring-tx-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:478) ~[spring-tx-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:272) ~[spring-tx-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95) ~[spring-tx-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207) ~[spring-aop-4.0.3.RELEASE.jar:4.0.3.RELEASE]
at com.sun.proxy.$Proxy261.update(Unknown Source) ~[na:na]
at de.unicreditgroup.esa.service.webtool.sql.WebToolSqlServiceImpl.updateSqlQuery(WebToolSqlServiceImpl.java:234) ~[de-unicreditgroup-esa-services-2.1.0-SNAPSHOT.jar:na]

I guess this is due to Hibernate has added wrapper on your exception.
Because you are trying to throw RunTimeException inside a transaction,hibernate converts this to it's own UnexpectedRollbackException exception.
See:- Exception doc

This happen also when you have not null field declared in database and in the Entity you are trying to save using JPA the same field is null so automatically it rolls back the transaction.

Related

spring security messaging throws NullPointerException internally (Spring context is null!)

this is websocket security config:
#Configuration
public class WSSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer implements ApplicationContextAware {
#Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
messages
.nullDestMatcher().denyAll()
.simpSubscribeDestMatchers("/topic/t01", "/topic/t99").authenticated()
.anyMessage().denyAll();
}
}
the context fails to load with this error (trimmed only to the relevant parts):
Caused by: java.lang.NullPointerException: null
at org.springframework.security.config.annotation.web.socket.AbstractSecurityWebSocketMessageBrokerConfigurer.configureClientInboundChannel(AbstractSecurityWebSocketMessageBrokerConfigurer.java:106) ~[spring-security-config-5.3.3.RELEASE.jar:5.3.3.RELEASE]
at org.springframework.web.socket.config.annotation.DelegatingWebSocketMessageBrokerConfiguration.configureClientInboundChannel(DelegatingWebSocketMessageBrokerConfiguration.java:73) ~[spring-websocket-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.messaging.simp.config.AbstractMessageBrokerConfiguration.getClientInboundChannelRegistration(AbstractMessageBrokerConfiguration.java:153) ~[spring-messaging-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.messaging.simp.config.AbstractMessageBrokerConfiguration.clientInboundChannelExecutor(AbstractMessageBrokerConfiguration.java:144) ~[spring-messaging-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.web.socket.config.annotation.DelegatingWebSocketMessageBrokerConfiguration$$EnhancerBySpringCGLIB$$e1380a16.CGLIB$clientInboundChannelExecutor$35(<generated>) ~[spring-websocket-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.web.socket.config.annotation.DelegatingWebSocketMessageBrokerConfiguration$$EnhancerBySpringCGLIB$$e1380a16$$FastClassBySpringCGLIB$$1a42e7b2.invoke(<generated>) ~[spring-websocket-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244) ~[spring-core-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.web.socket.config.annotation.DelegatingWebSocketMessageBrokerConfiguration$$EnhancerBySpringCGLIB$$e1380a16.clientInboundChannelExecutor(<generated>) ~[spring-websocket-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_342]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_342]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_342]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_342]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.2.7.RELEASE.jar:5.2.7.RELEASE]
... 93 common frames omitted
I looked at the culprit class AbstractSecurityWebSocketMessageBrokerConfigurer, the exception was thrown in method configureClientInboundChannel, the context is null. after some debugging I found out that the context was set before this method get called (of course with not null value) by setApplicationContext but the object (proxy) reference was not same when these two method was called (configureClientInboundChannel and setApplicationContext)
#Override
public final void configureClientInboundChannel(ChannelRegistration registration) {
ChannelSecurityInterceptor inboundChannelSecurity = context.getBean(ChannelSecurityInterceptor.class);
.
.
.
}
more information:
spring boot version: 2.3.1.RELEASE
spring security version: 5.3.3.RELEASE

How to configure spring batch not to auto create batch tables for storing meta data?

I'm working on a spring batch which uses JPA to perform CRUD operations on PostGres database. I'm using Spring boot 2.1.3. Even though I added the below configuration to disable Spring batch to use my postgres database for storing batch job meta data information, I' getting "ERROR: relation "batch_job_instance" does not exist" exception as shown below. Also I have followed the solution mentioned in here. Can anyone please suggest on what additional things needs to be done?
hibernate.temp.use_jdbc_metadata_defaults=false
spring.batch.initialize-schema=never
spring.batch.initializer.enabled=false
Exception:
Exception in thread "main" org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [SELECT JOB_INSTANCE_ID, JOB_NAME from BATCH_JOB_INSTANCE where JOB_NAME = ? and JOB_KEY = ?]; nested exception is org.postgresql.util.PSQLException: ERROR: relation "batch_job_instance" does not exist
Position: 39
at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:234)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72)
at org.springframework.jdbc.core.JdbcTemplate.translateException(JdbcTemplate.java:1444)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:632)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:669)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:700)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:712)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:768)
at org.springframework.batch.core.repository.dao.JdbcJobInstanceDao.getJobInstance(JdbcJobInstanceDao.java:148)
at org.springframework.batch.core.repository.support.SimpleJobRepository.getLastJobExecution(SimpleJobRepository.java:297)
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.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
at com.sun.proxy.$Proxy69.getLastJobExecution(Unknown Source)
at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:101)
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.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:127)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
at com.sun.proxy.$Proxy78.run(Unknown Source)
at com.test.BatchApplication.main(BatchApplication.java:28)
Caused by: org.postgresql.util.PSQLException: ERROR: relation "batch_job_instance" does not exist
Position: 39
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2440)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2183)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:308)
at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:441)
at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:365)
at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:143)
at org.postgresql.jdbc.PgPreparedStatement.executeQuery(PgPreparedStatement.java:106)
at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeQuery(ProxyPreparedStatement.java:52)
at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeQuery(HikariProxyPreparedStatement.java)
at org.springframework.jdbc.core.JdbcTemplate$1.doInPreparedStatement(JdbcTemplate.java:678)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:617)
... 31 more
Since you told Spring Boot to not create meta-data tables, you need to create them manually upfront in your datasource. For Postgres, you need to execute the schema-postgresql.sql script on your database before running your job.
EDIT: If you don't want to persist meta-data tables, you can use the in-memory Map-based job repository:
#Configuration
#EnableBatchProcessing
public class MyJobConfiguration extends DefaultBatchConfigurer {
#Override
protected JobRepository createJobRepository() throws Exception {
MapJobRepositoryFactoryBean factoryBean = new MapJobRepositoryFactoryBean();
factoryBean.afterPropertiesSet();
return factoryBean.getObject();
}
// other beans related to job definition
}
Note that the Map-based job repository is not intended for production use.
Another option it to use the JDBC based job repository but with an embedded database, like HSQLDB, H2, etc.
Hope this helps.
Here is the working code: https://www.linkedin.com/pulse/spring-batch-create-metadata-table-different-schema-ashtikar/
This shows how to use different schema for metadata tables and other schema to store actual records.
#Configuration
public class DatabaseConfig {
#Autowired
private Environment env;
// For Test schema
#Bean(name="secondaryDS")
#ConfigurationProperties(prefix="spring.datasource")
public DataSource getSeconadaryBatchDataSource(){
return DataSourceBuilder.create()
.url(env.getProperty("spring.datasource.url"))
.driverClassName(env.getProperty("spring.datasource.driver-class-name"))
.username(env.getProperty("spring.datasource.username"))
.password(env.getProperty("spring.datasource.password"))
.build();
}
// For "batchmetadata" tables
#Bean(name="primaryDS")
#Primary
#ConfigurationProperties(prefix="spring.hello.datasource")
public DataSource getPrimaryBatchDataSource(){
return DataSourceBuilder.create()
.url(env.getProperty("spring.hello.datasource.url"))
.driverClassName(env.getProperty("spring.hello.datasource.driver-class-name"))
.username(env.getProperty("spring.hello.datasource.username"))
.password(env.getProperty("spring.hello.datasource.password"))
.build();
}
#Bean(name = "primaryEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory( EntityManagerFactoryBuilder builder) {
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("hibernate.hbm2ddl.auto", "update");
return builder
.dataSource(getSeconadaryBatchDataSource())
.packages("com.example.model")
.persistenceUnit("default")
.properties(properties)
.build();
}
}

Getting "Scope 'step' is not active for the current thread" while creating spring batch beans

In my Spring batch configuration, I'm trying to setup a partitioned step, which accesses values from JobParameters as follows :
#Bean
#Qualifier("partitionJob")
public Job partitionJob() throws Exception {
return jobBuilderFactory
.get("partitionJob")
.incrementer(new RunIdIncrementer())
.start(partitionStep(null))
.build();
}
#Bean
#StepScope //I'm getting exception here - > Error creating bean
public Step partitionStep(
#Value("#{jobParameters[gridSize]}") String gridSize)
throws Exception {
return stepBuilderFactory
.get("partitionStep")
.partitioner("slaveStep", partitioner())
.gridSize(
StringUtils.isEmpty(gridSize) ? 10 : Integer
.parseInt(gridSize))
.step(slaveStep(50000))
.taskExecutor(threadPoolTaskExecutor()).build();
}
#Bean
#StepScope
public Step slaveStep(int chunkSize) throws Exception {
return stepBuilderFactory
.get("slaveStep")
.<Person,Person> chunk(chunkSize)
.reader(jdbcPagingItemReader()),
.writer(csvFileWriterParts())
.listener(stepExecutionListener()).build();
}
I have added #EnableBatchProcessing annotation to my SpringBoot application.
Since I wanted to access JobParameters while constructing a step, I used #StepScope. I have an example that works fine, without #StepScope annotation, but in that case I'm not accessing any JobParameters or anything from context.
But If I use StepScope annotation on partitionStep, I'm getting
Error creating bean with name 'scopedTarget.partitionStep': Scope
'step' is not active for the current thread; consider defining a
scoped proxy for this bean if you intend to refer to it from a
singleton; nested exception is java.lang.IllegalStateException: No
context holder available for step scope
but If I change it to JobScope, then it is failing at slaveStep() with same error message.
What is the correct scope to be used in such cases and how to resolve this issue I'm getting ?
What is the better way of accessing JobParameters while configuring spring beans ?
Exception stack is as below
2018-05-25 21:07:32,075 ERROR [main]
org.springframework.batch.core.job.AbstractJob : Encountered fatal
error executing job
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'scopedTarget.partitionStep': Scope 'step' is
not active for the current thread; consider defining a scoped proxy
for this bean if you intend to refer to it from a singleton; nested
exception is java.lang.IllegalStateException: No context holder
available for step scope at
org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:361)
at
org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at
org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:35)
at
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:192)
at com.sun.proxy.$Proxy55.getName(Unknown Source) at
org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:115)
at
org.springframework.batch.core.job.AbstractJob.handleStep(AbstractJob.java:392)
at
org.springframework.batch.core.job.SimpleJob.doExecute(SimpleJob.java:135)
at
org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:306)
at
org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:135)
at
org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50)
at
org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:128)
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.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333)
at
org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at
org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:127)
at
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
at com.sun.proxy.$Proxy54.run(Unknown Source) at
com.sample.main(ExtractApplication.java:58)
Caused by: java.lang.IllegalStateException: No context holder
available for step scope at
org.springframework.batch.core.scope.StepScope.getContext(StepScope.java:167)
at
org.springframework.batch.core.scope.StepScope.get(StepScope.java:99)
at
org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:346)
... 23 common frames omitted
If I modify to JobScope, I get exception on slaveStep, which is similar to the above exception.
Please try the option Spring batch scope issue while using spring boot, posted by Manh. I guess it solved the problem. Unfortunately I no longer have access to the code base, to confirm what I did for the fix.

Custom Exception translator not invoked in Hibernate/Spring

I have written code to handle a specific Oracle error code. I extend the LocalSessionFactoryBean to implement additional logic to the session factory creation.
class MyLocalSessionFactoryBean extends LocalSessionFactoryBean {
#Override
public DataAccessException translateExceptionIfPossible(RuntimeException ex)
{
if (ex instanceof HibernateJdbcException)
{
// Oracle serialization exception has SQL state [72000]; error code [8177]
SQLException sqle = ((HibernateJdbcException) ex).getSQLException();
if (equal(sqle.getSQLState(), "72000") && sqle.getErrorCode() == 8177)
return new CannotSerializeTransactionException("Cannot serialize transaction access", ex);
}
return super.translateExceptionIfPossible(ex);
}
}
It is declared in XML as the primary SessionFactory bean and works as it.
I have read this question, but the difference is that I use SessionFactory rather than EntityManager. I think my setup is fine in order to take advantage of persistence exception translator. Also, my DAOs are annotated #Repository
Unfortunately whenever I get a transaction serialization from Oracle, and only from Oracle, I get it propagated through all the stack trace. I could debug and LSFB's translateExceptionIfPossible method is never invoked.
Exception in thread "processRunManager-dequeue" org.springframework.orm.hibernate5.HibernateJdbcException: JDBC exception on Hibernate data access: SQLException for SQL [n/a]; SQL state [72000]; error code [8177]; could not execute statement; nested exception is org.hibernate.exception.GenericJDBCException: could not execute statement
at org.springframework.orm.hibernate5.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:248)
at org.springframework.orm.hibernate5.HibernateTransactionManager.convertHibernateAccessException(HibernateTransactionManager.java:755)
at org.springframework.orm.hibernate5.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:590)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:761)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:730)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:518)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:292)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.retry.interceptor.RetryOperationsInterceptor$1.doWithRetry(RetryOperationsInterceptor.java:91)
at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:287)
at org.springframework.retry.support.RetryTemplate.execute(RetryTemplate.java:164)
at org.springframework.retry.interceptor.RetryOperationsInterceptor.invoke(RetryOperationsInterceptor.java:118)
at org.springframework.retry.annotation.AnnotationAwareRetryOperationsInterceptor.invoke(AnnotationAwareRetryOperationsInterceptor.java:153)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
at com.sun.proxy.$Proxy272.pick(Unknown Source)
at sun.reflect.GeneratedMethodAccessor1806.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.__invoke(DelegatingMethodAccessorImpl.java:43)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy273.pick(Unknown Source)
at com.acme.Dequeuer.run(ProcessEngineImpl.java:298)
at java.lang.Thread.run(Thread.java:745)
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.update(AbstractEntityPersister.java:3184)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:3063)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3443)
at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:145)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:589)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:463)
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.flush(SessionImpl.java:1295)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:468)
at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3159)
at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2352)
at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:491)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:147)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$100(JdbcResourceLocalTransactionCoordinatorImpl.java:38)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:231)
at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:65)
at org.springframework.orm.hibernate5.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:582)
... 24 more
Caused by: java.sql.SQLException: ORA-08177: impossibile serializzare l'accesso per questa transazione
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:447)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:396)
at oracle.jdbc.driver.T4C8Oall.processError(T4C8Oall.java:951)
at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:513)
at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:227)
at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:531)
at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:208)
at oracle.jdbc.driver.T4CPreparedStatement.executeForRows(T4CPreparedStatement.java:1046)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1336)
at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3613)
at oracle.jdbc.driver.OraclePreparedStatement.executeUpdate(OraclePreparedStatement.java:3694)
at oracle.jdbc.driver.OraclePreparedStatementWrapper.executeUpdate(OraclePreparedStatementWrapper.java:1354)
at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeUpdate(NewProxyPreparedStatement.java:384)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:204)
... 43 more
The exception, as predictable, happens on #Transactional's commit phase. It is correct that the exception is thrown by JDBC to Hibernate, but should be translated by Spring using my code, so that Spring #Retry can replay the transaction again.
How do I get my custom exception translator working?
Found the culprit.
Such exception is thrown only at commit phase. My DAOs are annotated #Repository, but not #Transactional, so any operation will pass through DAO's #Repository annotation holding the exception translator interceptor without problems. When PlatformTransactionManager tries to commit, however, it is not decorated properly with a PersistenceExceptionTranslationAdvisor as shown in the stack trace.
HibernateTransactionManager only translates HibernateExceptions and PersistenceExceptions.
As I said in my question, I determined that Hibernate does not translate SQLException with those specific Oracle error codes to the correct subclass of HibernateException.
Solution 1 is to decorate services using DAOs with #Repository or solution 2 (our approach) to dispose of serialized transactions in favour of pessmistic row locks

With Spring 4, how do I create #Transactional outside of my main thread?

I'm using Spring 4.3.8.RELEASE with Hibernate 5.1. I have a thread pool set up in my context
<bean id="myprojectThreadFactory" class="org.springframework.scheduling.concurrent.CustomizableThreadFactory">
<constructor-arg value="myproject-"/>
</bean>
<bean id="myprojectTaskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="threadFactory" ref="myprojectThreadFactory"/>
<property name="corePoolSize" value="${myproject.core.thread.pool.size}" />
<property name="maxPoolSize" value="${myproject.max.thread.pool.size}" />
</bean>
But how do I create a transaction within one of my task threads, which is outside of my main thread? I'm trying this
// Cue up threads to execute
for (final Organization org : allOrgs)
{
m_threadExecutor.execute(new Thread(new Runnable(){
#Override
#Transactional(propagation=Propagation.REQUIRES_NEW)
public void run()
{
System.out.println("started.");
processData(org.getId());
System.out.println("finished.");
}
}));
but this is resulting in the below exception
Exception in thread "myproject-1" Exception in thread "myproject-2" org.hibernate.SessionException: Session is closed!
at org.hibernate.internal.AbstractSessionImpl.errorIfClosed(AbstractSessionImpl.java:132)
at org.hibernate.internal.SessionImpl.setCacheMode(SessionImpl.java:1511)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:1109)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:1033)
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:497)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:298)
at com.sun.proxy.$Proxy68.find(Unknown Source)
at org.mainco.subco.core.repo.GenericDao.find(GenericDao.java:123)
at org.mainco.subco.organization.repo.OrganizationDaoImpl.find(OrganizationDaoImpl.java:61)
at org.mainco.subco.organization.service.OrganizationServiceImpl.getDescendantOrganizations(OrganizationServiceImpl.java:283)
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:497)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
at com.sun.proxy.$Proxy75.getDescendantOrganizations(Unknown Source)
at org.mainco.subco.myproject.repo.myprojectClassDaoImpl.findBymyprojectOrg(myprojectClassDaoImpl.java:89)
at org.mainco.subco.myproject.service.myprojectClassServiceImpl.find(myprojectClassServiceImpl.java:300)
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:497)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
at com.sun.proxy.$Proxy91.find(Unknown Source)
at org.mainco.subco.myproject.quartz.ImportClassesWorker.preProcessData(ImportClassesWorker.java:56)
at org.mainco.subco.myproject.quartz.AbstractImportDataWorker.processData(AbstractImportDataWorker.java:126)
at org.mainco.subco.myproject.quartz.AbstractImportDataWorker.access$0(AbstractImportDataWorker.java:123)
at org.mainco.subco.myproject.quartz.AbstractImportDataWorker$1.run(AbstractImportDataWorker.java:110)
at java.lang.Thread.run(Thread.java:745)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Use TransactionTemplate for the logic. Something like this
final TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
taskExecutor.execute(new Runnable() {
#Override
public void run() {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
System.out.println("started.");
processData(org.getId());
System.out.println("finished.");
}
});
}
});
Spring doesn't know anything about your Runnable instance. Therefore adding annotation on the run method doesn't make any sense. Instead what you want to do is to put #Transactional on processData method. But note that it should be the method of a Spring bean!
UPDATE:
Just tried to mark a business method with both #Transactional and #Async and it worked for me. I used spring-boot of version 1.5.4.RELEASE that comes with spring 4.3.9.RELEASE and hibernate 5.0.12.Final. For my #Async configuration, I just used #EnableAsync. Here what I've got:
#Test
#Transactional
public void test() throws InterruptedException {
beanA.testMethodA();
//Sleeping, since after test is completed application is down which leaves #Async transaction not completed.
Thread.sleep(3000);
System.out.println("Thread in test method: " + Thread.currentThread().getName());
System.out.println("Transaction in test method: " + TransactionSynchronizationManager.getCurrentTransactionName());
}
Here is a method of BeanA. It is transactional with a default propagation level which makes it bounded to a transaction started by the test:
#Override
#Transactional
public void testMethodA() {
beanB.testMethodB();
System.out.println("Thread in method A: " + Thread.currentThread().getName());
System.out.println("Transaction in method A: " + TransactionSynchronizationManager.getCurrentTransactionName());
}
And the last part - method of a BeanB which is marked as #Async and #Transactional. It does run in a separate thread and in a separate transaction:
#Async
#Transactional
#Override
public void testMethodB() {
System.out.println("Thread in method B: " + Thread.currentThread().getName());
System.out.println("Transaction in method B: " + TransactionSynchronizationManager.getCurrentTransactionName());
}
After I run the test I get the following output:
Thread in test method: main
Transaction in test method: com.example.stackoverflow.TransactionTest.test
Thread in method A: main
Transaction in method A: com.example.stackoverflow.TransactionTest.test
Thread in method B: SimpleAsyncTaskExecutor-1
Transaction in method B: com.example.stackoverflow.BeanBImpl.testMethodB
Note the last two statements. The method marked as #Async and #Transactional is running in its own transaction and its own thread.

Resources