Skip to content

no transaction is in progress: AOP with proxied sessionfactory #34795

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

Open
btpog opened this issue Apr 22, 2025 · 4 comments
Open

no transaction is in progress: AOP with proxied sessionfactory #34795

btpog opened this issue Apr 22, 2025 · 4 comments
Labels
status: waiting-for-triage An issue we've not yet triaged or decided on

Comments

@btpog
Copy link

btpog commented Apr 22, 2025

Seems like AOP transaction interceptor fails to start an active transaction when the session factory is wrapped in a proxy.

It doesn't appear like the AOP transaction interceptor works when a proxy is involved.
I removed my custom session factory to test as simply as possible.

I have a sessionfactory:

  <bean name="demoSessionFactory" id="demoSessionFactory" parent="abstractSessionFactory">
        <property name="dataSource" ref="demoDataSource"/>
    </bean>

If I inject it directly into the transaction manager like so everything works:

<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
        <property name="autodetectDataSource" value="false"/>
        <property name="sessionFactory" ref="demoSessionFactory"/>
    </bean>

However, if I instead wrap it in a TargetSource to proxy it fails.

   <bean id="sessionFactoryTarget" >
          class="com.shavara.stripeinternalispayment.config.TestTargetSource">
        <property name="sessionFactory" ref="demoSessionFactory"/>
    </bean>
    <bean id="sessionFactory"  name="sessionFactory, entityManagerFactory" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="targetSource" ref="sessionFactoryTarget"/>
        <property name="proxyInterfaces" value="org.hibernate.SessionFactory"/>
    </bean>

Here is the target source code:

public class TestTargetSource implements TargetSource
    {
    private SessionFactory sessionFactory;

    public TestTargetSource()
        {
        }

    public void setSessionFactory(SessionFactory sessionFactory)
        {
        this.sessionFactory = sessionFactory;
        }

    @Override
    public Class<?> getTargetClass()
        {
        return SessionFactory.class;
        }

    @Override
    public Object getTarget() throws Exception
        {
        return sessionFactory;
        }
    }

This is the error I get:

jakarta.persistence.TransactionRequiredException: no transaction is in progress
	at org.hibernate.internal.AbstractSharedSessionContract.checkTransactionNeededForUpdateOperation(AbstractSharedSessionContract.java:560) ~[hibernate-core-6.6.11.Final.jar:6.6.11.Final]
	at org.hibernate.internal.SessionImpl.checkTransactionNeededForUpdateOperation(SessionImpl.java:2564) ~[hibernate-core-6.6.11.Final.jar:6.6.11.Final]
	at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1420) ~[hibernate-core-6.6.11.Final.jar:6.6.11.Final]
	at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1415) ~[hibernate-core-6.6.11.Final.jar:6.6.11.Final]
	at org.springframework.orm.hibernate5.SessionFactoryUtils.flush(SessionFactoryUtils.java:113) ~[spring-orm-6.2.5.jar:6.2.5]
	at org.springframework.orm.hibernate5.SpringSessionSynchronization.beforeCommit(SpringSessionSynchronization.java:99) ~[spring-orm-6.2.5.jar:6.2.5]
	at org.springframework.transaction.support.TransactionSynchronizationUtils.triggerBeforeCommit(TransactionSynchronizationUtils.java:127) ~[spring-tx-6.2.5.jar:6.2.5]
	at org.springframework.transaction.support.AbstractPlatformTransactionManager.triggerBeforeCommit(AbstractPlatformTransactionManager.java:986) ~[spring-tx-6.2.5.jar:6.2.5]
	at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:775) ~[spring-tx-6.2.5.jar:6.2.5]
	at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:758) ~[spring-tx-6.2.5.jar:6.2.5]
	at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:698) ~[spring-tx-6.2.5.jar:6.2.5]
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:416) ~[spring-tx-6.2.5.jar:6.2.5]
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-6.2.5.jar:6.2.5]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.2.5.jar:6.2.5]
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:727) ~[spring-aop-6.2.5.jar:6.2.5]
	at com.test.service.implementations.PatientPaymentImpl$$SpringCGLIB$$0.getValidSetupIntent(<generated>) ~[classes/:na]
	at com.test.paymentmethodrequest.PaymentMethodController.home(PaymentMethodController.java:47) ~[classes/:na]
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:258) ~[spring-web-6.2.5.jar:6.2.5]
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:191) ~[spring-web-6.2.5.jar:6.2.5]
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) ~[spring-webmvc-6.2.5.jar:6.2.5]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:986) ~[spring-webmvc-6.2.5.jar:6.2.5]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:891) ~[spring-webmvc-6.2.5.jar:6.2.5]
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-6.2.5.jar:6.2.5]
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) ~[spring-webmvc-6.2.5.jar:6.2.5]
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) ~[spring-webmvc-6.2.5.jar:6.2.5]
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) ~[spring-webmvc-6.2.5.jar:6.2.5]
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) ~[spring-webmvc-6.2.5.jar:6.2.5]
	at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) ~[tomcat-embed-core-10.1.39.jar:6.0]
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) ~[spring-webmvc-6.2.5.jar:6.2.5]
	at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) ~[tomcat-embed-core-10.1.39.jar:6.0]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195) ~[tomcat-embed-core-10.1.39.jar:10.1.39]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) ~[tomcat-embed-core-10.1.39.jar:10.1.39]
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) ~[tomcat-embed-websocket-10.1.39.jar:10.1.39]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) ~[tomcat-embed-core-10.1.39.jar:10.1.39]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) ~[tomcat-embed-core-10.1.39.jar:10.1.39]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-6.2.5.jar:6.2.5]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.2.5.jar:6.2.5]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) ~[tomcat-embed-core-10.1.39.jar:10.1.39]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) ~[tomcat-embed-core-10.1.39.jar:10.1.39]
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-6.2.5.jar:6.2.5]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.2.5.jar:6.2.5]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) ~[tomcat-embed-core-10.1.39.jar:10.1.39]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) ~[tomcat-embed-core-10.1.39.jar:10.1.39]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-6.2.5.jar:6.2.5]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.2.5.jar:6.2.5]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) ~[tomcat-embed-core-10.1.39.jar:10.1.39]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) ~[tomcat-embed-core-10.1.39.jar:10.1.39]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) ~[tomcat-embed-core-10.1.39.jar:10.1.39]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) ~[tomcat-embed-core-10.1.39.jar:10.1.39]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483) ~[tomcat-embed-core-10.1.39.jar:10.1.39]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115) ~[tomcat-embed-core-10.1.39.jar:10.1.39]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) ~[tomcat-embed-core-10.1.39.jar:10.1.39]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-10.1.39.jar:10.1.39]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) ~[tomcat-embed-core-10.1.39.jar:10.1.39]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:397) ~[tomcat-embed-core-10.1.39.jar:10.1.39]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) ~[tomcat-embed-core-10.1.39.jar:10.1.39]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:905) ~[tomcat-embed-core-10.1.39.jar:10.1.39]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1743) ~[tomcat-embed-core-10.1.39.jar:10.1.39]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) ~[tomcat-embed-core-10.1.39.jar:10.1.39]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1190) ~[tomcat-embed-core-10.1.39.jar:10.1.39]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-10.1.39.jar:10.1.39]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) ~[tomcat-embed-core-10.1.39.jar:10.1.39]
	at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]

I thought maybe it is a thing with Hibernate 5 which I need to use for the session factory for some legacy compatibility but I tried downgrading spring and still get errors.

Originally posted by @btpog in #34733

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Apr 22, 2025
@mdeinum
Copy link
Contributor

mdeinum commented Apr 24, 2025

The problem is the fact that with a proxy sessionfactory.openSession() is called 2 times. Leading to the transaction being run on a different Session then hibernate has.

This has to do with the logic in the TransactionSyncronizationManager.getResource this is called twice (once from the HibernateTransactionManager with the proxied SessionFactory, while the SpringCurrentSession calls this method with the actual SessionFactory.

Doing a small modification in the HibernateTransactionManager did fix this. When doing the getResource unwrapping the proxy makes the code succeed as now both use the same key for the getResource call.

@btpog
Copy link
Author

btpog commented Apr 24, 2025

The problem is the fact that with a proxy sessionfactory.openSession() is called 2 times. Leading to the transaction being run on a different Session then hibernate has.

This has to do with the logic in the TransactionSyncronizationManager.getResource this is called twice (once from the HibernateTransactionManager with the proxied SessionFactory, while the SpringCurrentSession calls this method with the actual SessionFactory.

Doing a small modification in the HibernateTransactionManager did fix this. When doing the getResource unwrapping the proxy makes the code succeed as now both use the same key for the getResource call.

Thanks for your response. The double call matches with some of the debugging I have been doing tracing with some output statements. For a while I thought I had 2 different sessionfactory beans being called.

This is a bit above my head, but it looks like getReource has a unwrapResourceIfNecessary call already in it.

If I extend HibernateTransactionManager is there a way to fix it in that?

@mdeinum
Copy link
Contributor

mdeinum commented Apr 25, 2025

If you override the getSessionFactory method of the HibernateTransactionManager with something like this is should work.

      @Override
      public SessionFactory getSessionFactory() {
        var sessionFactory = super.getSessionFactory();
        if (sessionFactory instanceof Advised advised) {
	    var targetSource = advised.getTargetSource();
            if (targetSource != null) {
              return (SessionFactory) targetSource.getTarget();
            }
        }
        return sessionFactory
      }

This will unwrap the proxy. There is probably something you can do with the InfrastructureProxy as well. But maybe @jhoeller has a better solution (or a fix to go into the framework). For now you could use this as a workaround.

@btpog
Copy link
Author

btpog commented Apr 27, 2025

If you override the getSessionFactory method of the HibernateTransactionManager with something like this is should work.

  @Override
  public SessionFactory getSessionFactory() {
    var sessionFactory = super.getSessionFactory();
    if (sessionFactory instanceof Advised advised) {
  var targetSource = advised.getTargetSource();
        if (targetSource != null) {
          return (SessionFactory) targetSource.getTarget();
        }
    }
    return sessionFactory
  }

This will unwrap the proxy. There is probably something you can do with the InfrastructureProxy as well. But maybe @jhoeller has a better solution (or a fix to go into the framework). For now you could use this as a workaround.

I did a rough test and that seems to work. Thanks.

I am shocked that I am the only person to wrap the session factory in a Proxy.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: waiting-for-triage An issue we've not yet triaged or decided on
Projects
None yet
Development

No branches or pull requests

3 participants