spring and @transactional, is this normal?

2019-08-09 23:01发布

问题:

i wrote this simple example:

//file TestController.java
public interface TestController {

    public List<Test> findAll();

}

//file TestControllerImp.java
@Controller
public class TestControllerImp implements TestController{

    @Autowired
    private SessionFactory sessionFactory;

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

    public List<Test> findAll() {
        return sessionFactory.getCurrentSession().createQuery("from Test").list();
    }

}

//file TestService.java
@Service
public class TestService {

    @Autowired
    private TestController controller;

    public boolean flag=true;

    public void setController(TestController controller){
        this.controller=controller;
    }

    @Transactional
    public List<Test> useController(){
        flag=false;
        return controller.findAll();
    }

}

And this is my try:

TestService s1=context.getBean(TestService.class);
TestService s2=context.getBean(TestService.class);
List<Test> list=s1.useController();
System.out.println(s1.flag+"  "+s2.flag);

Now the strange behaviour (im very new with spring):

  1. If i declare @Transactional the method "useController()", the output is: true true
  2. If i move @Transactional from TestService to TestControllerImp, and i declare "findAll()" with @Transactional, the output is: false false.

Why i have this behaviour? I know by default @Autowired classes are singletone, but why in the first case the flag still remains true?

Thanks all.

回答1:

The @Transactional mechanism works on JDK proxies per default and those work on interfaces only.

So if you let TestService be an interface and TestServiceImpl be its implementation, then the above code should work.

e.g. change the class declaration to this:

@Service
public class TestServiceImpl implements TestService {

but the test code must reference the interface, not the class:

// this code remains unchanged
TestService s1=context.getBean(TestService.class);
TestService s2=context.getBean(TestService.class);

Reference:

  • <tx:advice/> settings (Spring Reference)
  • Using @Transactional (Spring Reference)
  • TransactionProxyFactorybean (javadoc)