Sulaiman Adeeyo
Sulaiman Adeeyo

Reputation: 495

Spring JPA: Data not saved to Database when processing from an Async method

I have an app that uses Spring JPA and does some background processes using the @Async spring annotation. The process requires saving the parameters in the database or, if it already exists, update the db.

This doesn't work as my data entity doesn't get persisted in the database after the @Async method is done. But when I remove the @Async annotation from the method, all things work fine. My data is saved or updated in the DB.

Does anyone have an idea what could be wrong or what I may be missing?

Thanks.

** UPDATE [CODE SAMPLE]

ASYNCSERVICE

@Service
@EnableAsync
class AsyncService() {
    @Async
    public void process(firstEntity, secondEntity) {
        firstEntity = prepareParams(firstEntity);
        secondEntity = prepareParams(secondEntity);
        save(firstEntity, secondEntity);
    }
}

ENTITYRELATIONSERVICE

@Transactional
@Service
class EntityRelationService() {

    private SecondEntityRepo secondEntityRepo;

    @Autowired
    public EntityRelationService(SecondEntityRepo secondEntityRepo) {
        this.secondEntityRepo = secondEntityRepo;
    }

    @Transactional
    public void save(p1, p2) {
        p2.setRelation(p1);

        this.secondEntityRepo(p2);
    }
}

BEAN CONFIG

@Bean
@Primary
@ConfigurationProperties(prefix = "app.database")
public DataSource dataSource() {
    DriverManagerDataSource dataSource = new DriverManagerDataSource();
    dataSource.setDriverClassName(driver);
    dataSource.setUrl(url);
    dataSource.setUsername(username);
    dataSource.setPassword(password);
    return dataSource;
}

@Bean
@Primary
public LocalSessionFactoryBean sessionFactory() {
    LocalSessionFactoryBean sf = new LocalSessionFactoryBean();
    sf.setDataSource(dataSource());
    sf.setPackagesToScan("com.app");
    Properties props = new Properties();
    props.put("hibernate.use_sql_comments", false);
    props.put("hibernate.show_sql", true);
    sf.setHibernateProperties(props);
    return sf;
}

@Bean
public HibernateTransactionManager transactionManager() {
    HibernateTransactionManager transactionManager =
            new HibernateTransactionManager();
    transactionManager.setSessionFactory(sessionFactory().getObject());
    return transactionManager;
}

Upvotes: 5

Views: 7458

Answers (2)

wargre
wargre

Reputation: 4753

I'm guessing the @Async method is on the same class as @Transactional.

Be ware : @Async will make a new thread that will NOT be transactional (so no write in DB). With Spring proxy stuff, your async method need to call a method on another class in order to have the @Transactional back.

In other word, create a new empty class with a @Async method that just call your main method in original class (that have the @Transactional).

A simple example will look like this:

a proxy to the real service:

@Service
@EnableAsync
class AsyncService() {
    private SyncService syncService;

    @Async
    public void process(String param) {
        syncService.process(param);
    }
}

And the real service:

@Service
@Transactional
class SyncService() {
    public void process(String param) {
        // ... Do whatever you want
    }
}

Upvotes: 2

judos
judos

Reputation: 444

I had a similar issue: Whenever a method (A) throws an exception all changes are rolled back. Also all changes that happened inside other methods that were called from method (A) are rolled back.

My solution was to put the following annotation on a method where I was sure to always save the object no matter what happens elsewhere:

import org.springframework.transaction.annotation.Transactional;


@Transactional(propagation = Propagation.REQUIRES_NEW)
public void doStuff() {
...
}

Upvotes: 0

Related Questions