Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

@Transactional on @PostConstruct method

I want to read text data fixtures (CSV files) at the start on my application and put it in my database.

For that, I have created a PopulationService with an initialization method (@PostConstruct annotation).

I also want them to be executed in a single transaction, and hence I added @Transactional on the same method.

However, the @Transactional seems to be ignored : The transaction is started / stopped at my low level DAO methods.

Do I need to manage the transaction manually then ?

like image 820
Raphael Jolivet Avatar asked Jun 27 '13 15:06

Raphael Jolivet


People also ask

Does @transactional work on private methods?

The answer your question is no - @Transactional will have no effect if used to annotate private methods. The proxy generator will ignore them. When using proxies, you should apply the @Transactional annotation only to methods with public visibility.

What happens if one @transactional annotated method is calling another @transactional annotated method on the same object instance?

1 Answer. Show activity on this post. If you call method2() from method1() within the same class, the @Transactional annotation of the second method will not have any effect because it is not called through proxy, but directly.

On which can be @transactional annotation be applied?

The @Transactional annotation makes use of the attributes rollbackFor or rollbackForClassName to rollback the transactions, and the attributes noRollbackFor or noRollbackForClassName to avoid rollback on listed exceptions. The default rollback behavior in the declarative approach will rollback on runtime exceptions.

Can we use @transactional on class?

@Transactional on a class applies to each method on the service. It is a shortcut. Typically, you can set @Transactional(readOnly = true) on a service class, if you know that all methods will access the repository layer. You can then override the behavior with @Transactional on methods performing changes in your model.


3 Answers

Quote from legacy (closed) Spring forum:

In the @PostConstruct (as with the afterPropertiesSet from the InitializingBean interface) there is no way to ensure that all the post processing is already done, so (indeed) there can be no Transactions. The only way to ensure that that is working is by using a TransactionTemplate.

So if you would like something in your @PostConstruct to be executed within transaction you have to do something like this:

@Service("something")
public class Something {
    
    @Autowired
    @Qualifier("transactionManager")
    protected PlatformTransactionManager txManager;

    @PostConstruct
    private void init(){
        TransactionTemplate tmpl = new TransactionTemplate(txManager);
        tmpl.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                //PUT YOUR CALL TO SERVICE HERE
            }
        });
   }
}
like image 78
Platon Avatar answered Oct 09 '22 02:10

Platon


I think @PostConstruct only ensures the preprocessing/injection of your current class is finished. It does not mean that the initialization of the whole application context is finished.

However you can use the spring event system to receive an event when the initialization of the application context is finished:

public class MyApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
  public void onApplicationEvent(ContextRefreshedEvent event) {
    // do startup code ..
  }    
}

See the documentation section Standard and Custom Events for more details.

like image 38
micha Avatar answered Oct 09 '22 01:10

micha


As an update, from Spring 4.2 the @EventListener annotation allows a cleaner implementation:

@Service
public class InitService {

    @Autowired
    MyDAO myDAO;

    @EventListener(ContextRefreshedEvent.class)
        public void onApplicationEvent(ContextRefreshedEvent event) {
        event.getApplicationContext().getBean(InitService.class).initialize();
    }

    @Transactional
    public void initialize() {
        // use the DAO
    }

}

like image 10
DBE Avatar answered Oct 09 '22 03:10

DBE