@Async and @Transactional

Question about @Transactional working in @Async working

This is a code sketch for an example:

@Async
@Transactional
 public void data(List<Pass> pass) {
        //code
        for(City city : city) {
            for(Dep dep: city.getDeps()) {
                //code
                setXor(user, xor);
                //code
            }
        }
    }


@Transactional
public void setXor(User user, Xor xor) {
        //code
        user.setXor(xor);
    }

The question is, how will this work, does @Transactional extend from the data method to the setXor method (if you put an annotation on the data method, then maybe you don’t need to put it on setXor? And it will automatically switch to this method)

Also a question about @Async, will setXor work on a new thread along with Async? That is, there will be a main thread, a new one is created, which includes the data method, and since the data method called another method, it will be executed on the same thread as the data method, there will be no third thread. Or vice versa and the setXor method is executed in the main thread?

Upvotes: 13

Views: 28612

Answers (3)

i was successful, only when i used jdbc template(this is so sad :() Note: in this case @Scheduled and @Async have the same result

   @Configuration
    @PropertySource("")
    @EnableJpaRepositories(basePackages = "com.foo.myapp")
    public class DataSourceConfig {

@Bean
public LocalContainerEntityManagerFactoryBean transpEntityManager() {
final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();    
return em;
}   
    
    @Bean
    @ConfigurationProperties(prefix = "app.datasource.transp")
    public DataSourceProperties transpDataSourceProperties() {
        return new DataSourceProperties();
    }


@Bean(name = "datasource_transp")
public DataSource transpDataSource() {
return transpDataSourceProperties().initializeDataSourceBuilder().type(HikariDataSource.class).build();
}


@Bean(name = "tm_transp")
@Autowired
DataSourceTransactionManager tmTransp(@Qualifier("datasource_transp") DataSource datasource) {
DataSourceTransactionManager txm = new DataSourceTransactionManager(datasource);
return txm;
}


@Bean(name = "jdbcTemplateTransp")
public JdbcTemplate jdbcTemplateTransp(@Qualifier("datasource_transp") DataSource ds) {
        return new JdbcTemplate(ds);
}
       
}




@Service
@EnableScheduling
public class ScheduledService {

    @Autowired
    private ReportDataService reportDataService;

    @Autowired
    private AsyncService async;

    @Autowired
    @Qualifier("movProcTPTE")
    private TaskExecutor movProcTaskExecutor;

    @Scheduled(fixedDelay = 1000 * 60,initialDelay = 60000)
    public void agendamentoImportacaoMovProcessual(){
        movProcTaskExecutor.execute(
                () -> {
                    myService.methodWithDatabasePersist();
                }
        );

    }

}


@Service
public class ReportDataService {
    

    @Autowired
    @Qualifier("jdbcTemplateTransp")
    private JdbcTemplate jdbc;

    

    public void jdbcSave(List<ReportData> list){
        DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime date = LocalDateTime.now();
        String dateStr = date.format(fmt);
        ReportData report = list.get(0);
        String sql = "INSERT INTO .....";
        jdbc.update(sql);
    }

    public void methodWithDatabasePersist(){        
                    jdbcSave(relatorios);   

    }


}

Upvotes: 1

TriS
TriS

Reputation: 4038

As per Spring Transaction Documentation

All code within a transaction scope runs in that transaction. However, you can specify the behavior if a transactional method is run when a transaction context already exists. For example, code can continue running in the existing transaction (the common case), or the existing transaction can be suspended and a new transaction created.

@Transactional commonly works with thread-bound transactions managed by PlatformTransactionManager, exposing a transaction to all data access operations within the current execution thread. Note: This does not propagate to newly started threads within the method.

@Transactional is powered by Aspect-Oriented Programming. Therefore, processing occurs when a bean is called from another bean. In the example above, the method is called from the same class so that no proxies can be applied.

When a method without @Transactional is called within a transaction block, the parent transaction will continue to exist for the new method. It will use the same connection from the parent method (method with @Transactional) and any exception caused in the called method (method without @Transactional) will cause the transaction to rollback as configured in the transaction definition.

If the @Async annotation is being used extra care should be taken with respect to transaction. When a @Transactional Spring @Component calls a method annotated with @Async the call to the asynchronous method is being scheduled and executed at a later time by a task executor and is thus handled as a 'fresh' call, i.e. without a transactional context. If the @Async method (or the @Component in which it is declared) is not @Transactional by itself Spring will not manage any needed transactions.

In order to make Spring manage the transaction of the @Async method either the @Component or the method itself should declare the @Transactional annotation, this way Spring will manage the transaction even if a method is being executed asynchronous.

Upvotes: 16

kosta.dani
kosta.dani

Reputation: 457

Spring doesn't create transactions for calls in the same class so the setXor will be executed in the same Transaction as data no matter if you annotate it with transactional or not.

I think also setXor and data will be executed in one Thread other than the main.

Upvotes: 2

Related Questions