How do you reorganize the @Transactional method to split non-transactional parts

I have a data access class that works as part of a standalone Java application. It currently works, which means the transaction manager is defined, but I want to reorganize the class to reduce the amount of the transaction, but if I do, I get org.hibernate.HibernateException: there is no Hibernate session associated with the stream, and no configuration lets create without a transaction here , which means that moving @Transactional somehow stopped him from recognizing.

In my initial version, refinanced methods were private, but I found a recommendation to change this to the public, as in some cases the annotation would not be received.

public class DoStuff { @Transactional public void originalMethod() { // do database stuff ... // do non-database stuff that is time consuming ... } } 

I want to make refactoring as follows

 public class DoStuff { public void originalMethod() { doDatabaseStuff() doNonDatabaseStuff() } @Transactional public void doDatabaseStuff() { ... } public void doNonDatabaseStuff() { ... } } 
+6
source share
2 answers

Edit:

You need to understand how Spring proxying works in order to understand why your refactoring does not work.

A method call by reference to an object will be a proxy server call, and thus the proxy server will be able to delegate to all interceptors (tips) that are relevant to this particular method call. However, as soon as the call finally reaches the target, any method call that it can make on its own will be called against this link, not the proxy. This has important implications. This means that self-exclusion will not cause the advice associated with the method call to be able to execute.

@Transactional uses Spring AOP, Spring uses a proxy. This means that when you call the @Transactional method from another class, Spring will use a proxy server, so transaction advice will be applied. However, if you call a method from the same class, Spring will use the "this" link instead of the proxy, so transaction recommendations will not apply.

Original answer:

Here's what worked for me in a similar scenario.

 public class DoStuff implement ApplicationContextAware { private ApplicationContext CONTEXT; public void setApplicationContext(ApplicationContext context) throws BeansException { CONTEXT = context; } public void originalMethod() { getSpringProxy().doDatabaseStuff() doNonDatabaseStuff() } private DoStuff getSpringProxy() { return context.getBean(this.getClass()); } @Transactional public void doDatabaseStuff() { ... } public void doNonDatabaseStuff() { ... } } 

Explanation:

  • Make the ApplicationContextAware class, so it has a context reference
  • When you need to call a transaction method, select the actual Spring proxy from the context
  • Use this proxy to call your method so that @Transactional actually applies.
+6
source

Your approach looks like it is working fine, I expect the problem is with Spring proxies.

The reason I asked about the interfaces is due to the default method by which Spring applies transactional behavior - dynamic JDK proxies.

If the actual definition of your class is:

 public class DoStuff implements Doable { public void originalMethod() { } } public interface Doable { public void originalMethod(); } 

If this is really a structure, when you switched to a new structure, Spring cannot proxy the new doDatabaseStuff method.

Your options to fix:

  • Add new methods to your interface so Spring can proxy them
  • Switch to using CGLIB-based proxies (they do not rely on interfaces).
0
source

Source: https://habr.com/ru/post/923546/


All Articles