Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Regression: Shared EntityManager proxy insists on actualTransactiveActive flag even with SYNCHRONIZATION_NEVER [SPR-13838] #18411

Closed
spring-projects-issues opened this issue Jan 5, 2016 · 17 comments
Assignees
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

@spring-projects-issues spring-projects-issues commented Jan 5, 2016

Krzysztof Lewandowski opened SPR-13838 and commented

If set the transaction synchronization to be SYNCHRONIZATION_NEVER, e.g.:

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    if(bean instanceof AbstractPlatformTransactionManager){
        AbstractPlatformTransactionManager transactionManager = (AbstractPlatformTransactionManager)bean;
        transactionManager.setTransactionSynchronization(SYNCHRONIZATION_NEVER);
    }
    return bean;
}

I get a following exception when trying to save an object using jpa repository:

Caused by: javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process 'persist' call
	at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:277)
	at com.sun.proxy.$Proxy126.persist(Unknown Source)
	at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:439)
	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:483)
	at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:483)
	at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:468)
	at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:440)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:61)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)

With that setup TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction()); is not called and now SharedEntityManagerCreator requires actual transaction to be active:

            if (target == null || !TransactionSynchronizationManager.isActualTransactionActive()) {
         throw new TransactionRequiredException("No EntityManager with actual transaction available " +
                           "for current thread - cannot reliably process '" + method.getName() + "' call");
}

Affects: 4.2.4

Attachments:

Issue Links:

  • #16541 Shared EntityManager should immediately throw TransactionRequiredException if no transaction in progress
  • #17833 Transaction propagation SUPPORTS leads to “HHH000326: Cannot join transaction” warning
  • #17834 Inconsistent JPA behavior using no transaction, propagation SUPPORTS and OpenEntityManager pattern
  • #18944 Shared EntityManager's target lookup doesn't work with Spring Data's ChainedTransactionManager

1 votes, 5 watchers

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jan 5, 2016

Juergen Hoeller commented

I suppose you are using JpaTransactionManager, so an EntityManager is bound to the thread... with just the actualTransactionActive flag not set? We should definitely relax that EntityManager status check then. Looking at it for 4.2.5.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jan 5, 2016

Krzysztof Lewandowski commented

Yes, exactly. JpaTransactionManager is used. i can work around it like:

    @Bean
    @Primary
    public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
        return new JpaTransactionManager(entityManagerFactory) {
            @Override
            protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
                if (status.isNewSynchronization()) {
                    super.prepareSynchronization(status, definition);
                } else {
                    TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());
                }
            }


        };
    }

But it doesn't seem to be a cleanest solution. Moreover thread locals won't get cleaned unless I add some transaction event listeners.

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jan 6, 2016

Sathishkumar Murugesan commented

Hi,

I am also facing similar issue. I have upgraded to latest Spring version from 4.1.6.Release to 4.2.4.Release and suddenly all what has functioned smoothly before, now throws the following exception. Exception occurs in SharedEntityManagerCreator.java. In the same method reported by other user.

javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process 'persist' call
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:277) ~[na:4.2.4.RELEASE]
at com.sun.proxy.$Proxy51.persist(Unknown Source) ~[na:na]
else if (transactionRequiringMethods.contains(method.getName())) {
                    // We need a transactional target now, according to the JPA spec.
                    // Otherwise, the operation would get accepted but remain unflushed...
                    if (target == null || !TransactionSynchronizationManager.isActualTransactionActive()) {
                        throw new TransactionRequiredException("No EntityManager with actual transaction available " +
                                "for current thread - cannot reliably process '" + method.getName() + "' call");
                    }
                }

Do you have work around for now?

I also created issue in Stackoverflow : http://stackoverflow.com/questions/34494754/upgraded-from-spring-4-1-6-to-4-2-4-and-suddenly-getting-transactionrequiredexce with more explanation and code samples

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jan 6, 2016

Krzysztof Lewandowski commented

The most dirty one is to manually call

TransactionSynchronizationManager.setActualTransactionActive(true);

somewhere before you invoke operation that force those checks

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jan 6, 2016

Sathishkumar Murugesan commented

Thanks for workaround. For me target == null, so it fails in the first condition not with ActualTransactionActive.

EntityManagerFactoryUtils.java

else if (!TransactionSynchronizationManager.isSynchronizationActive()) {
			// Indicate that we can't obtain a transactional EntityManager.
			return null;
		}

I dont know why it couldn't find the targetEntityManager. But when i switch 4.1.6 it works like charm

If I set this in code.. then it works

if(!TransactionSynchronizationManager.isSynchronizationActive()){
            TransactionSynchronizationManager.initSynchronization();
            TransactionSynchronizationManager.setActualTransactionActive(true);
        }
@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jan 6, 2016

Juergen Hoeller commented

If the first condition fails for you there, I assume you're not using JpaTransactionManager? Which transaction manager are you using then? You do set transaction synchronization to 'never' though?

Let's sort out any variant of this issue for 4.2.5 in February. Providing an immediate upgrade path from 4.1.x is a key requirement for us.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jan 7, 2016

Sathishkumar Murugesan commented

I use JPA transaction manager and attached code snippet of my configuration.

<bean id="xxxTxManager" class="org.springframework.orm.jpa.JpaTransactionManager"
          p:entityManagerFactory-ref="xxxEntityManagerFactory" p:dataSource-ref="xxxDataSource"/>

<tx:annotation-driven transaction-manager="xxxTxManager"/>

<bean id="xxxEntityManagerFactory"
          class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
          p:persistenceUnitName="xxxHibernatePersistenceUnit"
          p:packagesToScan="com.opensolutions"
          p:dataSource-ref="xxxDataSource" p:jpaVendorAdapter-ref="hibernateVendor"
          p:jpaPropertyMap-ref="jpaPropertyMap" p:persistenceXmlLocation="classpath*:META-INF/xxx-jpa-persistence.xml"/>

    <bean id="hibernateVendor"
          class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
          p:showSql="false"/>

    <util:map id="jpaPropertyMap">
        <entry key="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect"/>
        <entry key="jadira.usertype.databaseZone" value="jvm"/>
        <entry key="jadira.usertype.javaZone" value="jvm"/>
    </util:map>
@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jan 7, 2016

Juergen Hoeller commented

I'm not sure how target can be null there with a JpaTransactionManager driving the transaction. Could you please double-check what's happening there, in particular within the EntityManagerFactoryUtils.doGetTransactionalEntityManager call?

Juergen

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jan 7, 2016

Sathishkumar Murugesan commented

I checked it..,

EntityManagerHolder emHolder = (EntityManagerHolder) TransactionSynchronizationManager.getResource(emf);

returns null, so

else if (!TransactionSynchronizationManager.isSynchronizationActive()) {
			// Indicate that we can't obtain a transactional EntityManager.
			return null;
		}

!!
When i check the method doGetResource it has only JMS datasource not my database datasource. Attached screenshot for more reference. Please let me know if you need more details.

!screenshot-1.png|thumbnail!
Just giving back ground about my scenario, i am trying to persist the data in the database before i post the message in the queue while posting queue it works fine. Its async process i used to save the data when they post reply back its fails only in the reply section when i listener picks up the call. While posting the message to the queue, it still works fine without any issues.

Before i post the message in the queue, when i inspect getResources, I see all the resources. Attached screenshot of the same for reference. It fails only during reply
!screenshot-2.png|thumbnail!

Regards,
Sathish

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jan 7, 2016

Juergen Hoeller commented

This looks like your JpaTransactionManager isn't even kicking in though; otherwise an EntityManagerHolder would be bound for your EntityManagerFactory at that point. You could put corresponding breakpoints in JpaTransactionManager...

As far as my current analysis goes, #17834 seems to have caused the regression for SYNCHRONIZATION_NEVER in that it insists on the actualTransactiveActive flag; we can easily fix this for 4.2.5. The regression with no EntityManagerHolder bound at all seems to be different and not caused by #17834; it'd be my pleasure to address it for 4.2.5 as well but have yet to find its root cause.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jan 7, 2016

Sathishkumar Murugesan commented

I tried to put break point in JpaTransactionManager. But code doesnt even go to JpaTransactionManager. JpaTransactionManger class is never invoked.

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jan 13, 2016

Juergen Hoeller commented

I've resolved the original issue here through an extra EntityManager.getTransaction().isActive() check: so either the actualTransactiveFlag or that EntityManager state needs to be set in order to proceed with a transaction-requiring operation. This should cover the SYNCHRONIZATION_NEVER case.

As for the target suddenly being null in some scenarios on 4.x, this seems to be a separate issue not caused by the changes behind #17834. Let's create a separate JIRA issue for that scenario if it remains to be a problem.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jan 20, 2016

Murali Mohan Rath commented

I have raised a related bug at https://jira.spring.io/browse/DATACMNS-803
This is not resolved even with 4.2.5 SNAPSHOT build

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jan 20, 2016

Murali Mohan Rath commented

I had another go by building 4.3.0-BUILD-SNAPSHOT locally and it resolves the issue. I guess the snapshots on spring repo are not updated.

@Juergen , Is it possible to release 4.2.5 soon? if not would you know when will be the next release?

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jan 20, 2016

Juergen Hoeller commented

I haven't got around to the 4.2.5 backport yet; I'll make sure to commit it later this week. So the fix simply isn't available in that branch yet.

As for the release dates, we always reflect the current plan here: https://jira.spring.io/browse/SPR/?selectedTab=com.atlassian.jira.jira-projects-plugin:roadmap-panel - 4.2.5 is currently scheduled for Feb 18.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Mar 17, 2016

Paul Zhang commented

hello Juergen,

just fellow up with "As for the target suddenly being null in some scenarios on 4.x, this seems to be a separate issue not caused by the changes behind #17834. Let's create a separate JIRA issue for that scenario if it remains to be a problem." did you created separate JIRA issue for this one.

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jun 16, 2016

Sathishkumar Murugesan commented

As for the target suddenly being null in some scenarios on 4.x, this seems to be a separate issue not caused by the changes behind #17834. Let's create a separate JIRA issue for that scenario if it remains to be a problem.

This issue is not still resolved in Spring 4.3.0 also. Exact Scenario is explained by Murali Mohan Rath : I have raised a related bug at https://jira.spring.io/browse/DATACMNS-803

If we invoke JmsTransactionManager first, followed by JPATransactionManager then JPATransactionManager is coming as null. which eventually breaks code. Because of this issue we were not able to migrate from Spring 4.1.6 to any higher Spring Version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
2 participants
You can’t perform that action at this time.