Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

@Transactional does not work on method level

I have a question about Spring 3.2.3 @Transactional annotation. My Service class looks like this:

@Service @Transactional
class InventoryDisclosureBO {

@Autowired InventoryDisclosureDAO inventoryDisclosureDAO;

private static final Logger log = LoggerFactory.getLogger( InventoryDisclosureBO.class);

public void processDisclosureData(InventoryDisclosureStatus data){
  validate(data);
  persist(data);
}

@Transactional(propagation = REQUIRES_NEW)
void persist(InventoryDisclosureStatus data) {
  inventoryDisclosureDAO.setAllInvalid( data.getUnit());
  inventoryDisclosureDAO.insert( data );
}

void validate(InventoryDisclosureStatus data) {
 ...
}
}

All works perfectly if I call persist() method. But if I comment out @Transactional at class level - transaction does not start. Could anybody tell me why Spring could ignore @Transactional on methol-level only?

like image 800
never Avatar asked Sep 03 '13 10:09

never


2 Answers

You cannot call persist() from processDisclosureData() because it belongs to the same class and it will bypass transactional proxy created by Spring for InventoryDisclosureBO. You should call it from other beans to make @Transactional annotations work. When Spring injects a reference to InventoryDisclosureBO bean to other beans it actually injects a reference to InventoryDisclosureBOProxy which contains transactional logic, eg

    class Bean2 {

      @Autowire
      private InventoryDisclosureBO idbo;   <-- Spring will inject a proxy here

      public void persist(InventoryDisclosureStatus data) {
           idbo.persist(data);     <-- now it will work via proxy
      }
...
like image 107
Evgeniy Dorofeev Avatar answered Oct 21 '22 23:10

Evgeniy Dorofeev


This is related to how spring generates the transactional proxies.

In the case where you have @Transactional at the class level, when you call InventoryDisclosureBO.processDisclosureData(), in fact, you're calling a Spring proxy that starts the transaction, and then calls the real implementation.

If you only have @Transaction in persis(), spring doesn't start a transaction when you call InventoryDisclosureBO.processDisclosureData(), and then it cannot detect that you've called InventoryDisclosureBO.persist()

So Spring basically ignores the annotation on persist, because it cannot add the transactional proxy.

As a rule of thumb, the @Transactional annotation should be on a public method, and hopefully quite high in the call hierarchy (otherwise each persist would end up creating a new transaction)

You might find more information on this other SO question: Method Interceptor on private methods (any non-public methods behave in the same way)

like image 26
Augusto Avatar answered Oct 21 '22 21:10

Augusto