How to call @Transactional method from one service from other @Transactional method from other service

StackOverflow https://stackoverflow.com/questions/23641826

質問

I have:

1) Service:

@Service("scanner")
@Transactional
public class Scanner
{
    @Inject
    AnalyzerService analyzerService;

    @Transactional
    private void scan() {
        analyzerService.analyze();
    }
}

2) Service:

@Service
public class AnalyzerService
{
    @Inject
    AnalyzerDao analyzerDao;

    @Transactional
    public void analyze() {
        List<AnalyzerResult> items;
        // code filling items list removed;
        save(items); 
    }

    @Transactional
    private void save(List<SomeType> items) {
        analyzerDao.save(items); // <--- Why after call save items are not saved in DB?
    }
}

3) Dao:

@Repository
public class AnalyzerDao extends GenericDaoImpl<AnalyzerResult>
{
    //all needed methods for find, edit, delete and save which works fine in other cases.
}

Question:

Why after call analzyerDao.save(items) DB is still empty? Is it problem with transaction some how? When I invoke flush() method and getSession().getTransaction().commit() just after line analyzerDao.save(items) then records appearing in DB but exception is thrown:

Caused by: org.springframework.transaction.TransactionSystemException: Could not commit Hibernate transaction; nested exception is org.hibernate.TransactionException: Transaction not successfully started
    at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:660)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
    at com.sun.proxy.$Proxy44.execute(Unknown Source)
    at org.quartz.core.JobRunShell.run(JobRunShell.java:223)
    ... 1 more
Caused by: org.hibernate.TransactionException: Transaction not successfully started
    at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:127)
    at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:656)
    ... 9 more

How should implementation be done to work 'save' method fine? What should I do to save items just in line analyzerDao.save(items) and not only after first transaction will be finished?

役に立ちましたか?

解決

What you need is a new transaction just to save what you need to save. You can achieve this by configuring the propagation of @Transactional annotation to REQUIRES_NEW.

Unfortunately your case is a bit tricky, because you are invoking a method within this context when you do save(items);, this means the transaction interceptor will not intercept such invocation, therefore you have the possibility to inject the service to a field hold by itself and invoke it on the injected service instead of this forcing the invocation to that method be intercepted by the transaction interceptor, please try the following implementation:

@Service
public class DefaultAnalyzerService implements AnalyzerService {
    @Inject
    AnalyzerDao analyzerDao;
    @Inject
    AnalyzerService analyserService;

    @Transactional
    @Override
    public void analyze() {
        List<AnalyzerResult> items;
        // code filling items list removed;
        analyserService.save(items); 
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @Override
    public void save(List<SomeType> items) {
        analyzerDao.save(items); // <--- Why after call save items are not saved in DB?
    }

}

Another thing that changed was the visibility of save(List<SomeType> items), that is public now on in order to be intercepted by transaction interceptor and an interface was extracted. This is needed due to limitations with spring, but you can use AspectJ to handle such interceptor, therefore please take a look here.

他のヒント

Data won't appear in the database until the transaction is committed. For @Transactional methods, the transaction is committed by Spring after returning from the method.

By the way, @Transactional on private methods has no effect, so Scanner.scan() is not transactional at all.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top