1

I know it's not a best practice to make a controller transactional but want to try the solution discussed here

According to the Spring MVC documentation 17.3.2:

A common pitfall when working with annotated controller classes happens when applying functionality that requires creating a proxy for the controller object (e.g. @Transactional methods). Usually you will introduce an interface for the controller in order to use JDK dynamic proxies. To make this work you must move the @RequestMapping annotations, as well as any other type and method-level annotations (e.g. @ModelAttribute, @InitBinder) to the interface as well as the mapping mechanism can only "see" the interface exposed by the proxy.

I should be able to make a RestController method transactional if I "move all method annotations to its interface"?

Under this assumption, I wrote following codes, which still didn't give me a transactional doSth() method:

public interface SomeController{
   @Transactional
   @RequestMapping(...)
   void doSth()
}

@RestController
public class SomeControllerImpl implements SomeController{
   @Override
   public void doSth(){...}
}

web.xml
    <servlet>
        <servlet-name>someServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>...</init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>someServlet</servlet-name>
        <url-pattern>...</url-pattern>
    </servlet-mapping>

context.spring.xml
    <jee:jndi-lookup id="dataSource" jndi-name="jdbc/project" resource-ref="true" />
    <tx:annotation-driven />
    <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" />

Could someone help explain why this doesn't work and is there a way to make a method in RestController transactional?

wayne
  • 598
  • 3
  • 15
  • *Curious:* Why do you call your controller a `Service`? Should it be named `SomeWebController`? – Andreas Oct 27 '19 at 10:36
  • That code won't compile, since `doSth` must be `public` in the class. --- Anyway, how do you know it didn't give you a transactional `doSth()` method? You didn't specify how you know that, so for all we know, you're mistaken and it did make it transactional. – Andreas Oct 27 '19 at 10:38
  • hey Andreas, I autowried a SessionFactory in SomeWebServiceImpl and used it to getCurrentSession().getTransaction() and there isn't a transaction – wayne Oct 27 '19 at 10:44
  • Did you configure a TransactionManager bean? – Andreas Oct 27 '19 at 10:48
  • to be specific, I called SomeService.transactionalMethod() (of propagation type Required) twice in doSth() and found out the 2 calls are using different transactions, which won't be the case if doSth() is already transactional – wayne Oct 27 '19 at 10:50
  • @Andreas yes I've configured a TransactionManager. Have posted context.spring.xml content in the quesiton – wayne Oct 27 '19 at 10:55

2 Answers2

1

Try to put @Transactional annotation on class SomeControllerImpl, not on the interface.

@Transactional annotation will work properly on an interface if you are using interface-based proxies. The fact that annotations are not inherited means that if you are using class-based proxies then the transaction settings will not be recognised by the class-based proxying.

Also the Spring team's recommendation is that you only annotate concrete classes with the @Transactional annotation, as opposed to annotating interfaces.
Spring Docummentation

jedicode
  • 145
  • 1
  • 11
  • Thanks @Ilya I did first try the normal way - putting "@Transactional" annotation on class SomeControllerImpl instead of the interface but it didn't give me a transactional method. I suppose it is because "mapping mechanism can only 'see' the interface exposed by the proxy"? That's why I resort to the way mentioned in my question – wayne Oct 27 '19 at 21:35
  • @wayne If you about `@RequestMapping` then it also can be put on a concrete class. About the transaction, I did not notice that you are using JTA Transaction manager. Then I have several questions: do you want to get XA transaction in your rest controller? Which Java EE server do you use? – jedicode Oct 28 '19 at 19:22
0

To enable annotations based transactional behavior, you need to mention the transactionManager in tx:annotation-driven.

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

This should resolve your issue. please let me know if you found this is helpful.

venkat
  • 117
  • 8
  • thanks @venkat. I put the transaciton-manager in but it didn't help. I didn't do it because as metioned in [Spring Doc](https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html) section 15.6.5 "You can omit the transaction-manager attribute in the tag if the bean name of the PlatformTransactionManager that you want to wire in has the name transactionManager." – wayne Oct 27 '19 at 21:48
  • @wayne, is it Ok for you to share the logs. can you share the server startup logs and the logs when you initiated transaction calls. – venkat Oct 28 '19 at 03:10