9

This is my 1st time trying Spring3's @Scheduled , but found I cannot commit to DB. This is my code :

@Service
public class ServiceImpl implements Service , Serializable
{
  @Inject 
  private Dao dao;

  @Override
  @Scheduled(cron="0 0 * * * ?") 
  @Transactional(rollbackFor=Exception.class)
  public void hourly()
  {
    // get xxx from dao , modify it
    dao.update(xxx);
  }
}

I think it should work , I can see it starts-up hourly and load xxx from DB , but data is not committed to DB.

There's been tx:annotation-driven in spring's xml :

<bean id="entityManagerFactoryApp" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
  <property name="persistenceUnitName" value="myapp"/>
</bean>
<bean id="transactionManagerApp" class="org.springframework.orm.jpa.JpaTransactionManager">
  <property name="entityManagerFactory" ref="entityManagerFactoryApp" />
</bean>

<tx:annotation-driven transaction-manager="transactionManagerApp" />

Can somebody tell me what I missed here ?

I have one 'dirty' solution :

@Service
public class ServiceImpl implements Service , Serializable
{
  @Inject 
  private Dao dao;

  @Inject
  @Qualifier("transactionManagerApp")
  private PlatformTransactionManager txMgrApp;

  @Override
  @Scheduled(cron="0 0 * * * ?")
  @Transactional(rollbackFor=Exception.class)
  public void hourly()
  {
    final TransactionTemplate txTemplateApp = new TransactionTemplate(txMgrApp);
    txTemplateApp.execute(new TransactionCallbackWithoutResult()
    {
      @Override
      protected void doInTransactionWithoutResult(TransactionStatus status)
      {
        //get xxx from dao
        dao.update(xxx);
      }
    });
  }
}

It works fine here , but it is so redundant , making the code harder to read. I wonder why TransactionManager is not injected (and opened) in the previous code snippets?

Thanks a lot !

smallufo
  • 11,516
  • 20
  • 73
  • 111

3 Answers3

20

You probably have figured this out or moved on (I hope so), but for the benefit of others:

The @Transactional annotation tells Spring to wrap your original ServiceImpl bean with a dynamic proxy that also implements 'Service' (by default Spring proxies the interface, not the implementation). This proxy will transparently handle the creation and commit/rollback of the transaction when you call hourly() on the proxy. However, if you call hourly() directly on your implementation (which is what is happening above), the proxy is bypassed, so there is no transaction.

http://blog.springsource.org/2012/05/23/understanding-proxy-usage-in-spring/

The solution is to either

  1. Demarcate the transaction programmatically as you are doing in your 'dirty' solution (you don't need the annotations is this case).
  2. Make sure that your @Scheduled method makes its call to dao.update(xxx); via the Service interface, not directly on your implementation (thereby going through the proxy). Basically you need to move the @Scheduled method to another bean.

I hope that is clear enough!

Barry Pitman
  • 3,087
  • 1
  • 24
  • 32
  • so regarding your second solution - what do you mean? – Dejell May 07 '14 at 19:18
  • 1
    Did what the second solution says. Moved the @@Transactional methods to another bean that was injected into the @@Scheduled bean and transactions are now alive! Otherwise I kept getting "javax.persistence.TransactionRequiredException: No transactional EntityManager available" – agelbess Nov 06 '14 at 04:10
0

I had the same problem and after spending time on it, I realized that I got an exception after the dao.update() call in some unrelated code that didn't check null value - so it simply broke the transaction. There was no stackTrace printing because it has been treated well by spring (some catch block). I spent a while on that. So - just verify that your transaction method completes till its end. Hope it will help someone.

Yosi Lev

ylev
  • 2,313
  • 1
  • 23
  • 16
0

When you use annotation-driven support, it only works on classes created within that context. My bet is that ServiceImpl is not created in the same context as your transaction manager (either directly or by annotation scanning).

Andrew White
  • 52,720
  • 19
  • 113
  • 137
  • Thanks , but how to 'enforce' ServiceImpl created in the same context as my transactionManager ? – smallufo Mar 26 '11 at 17:22
  • Either create a bean with that class explicitly or ensure that your component-scan tag is in the same config or parent config of your transaction manager. – Andrew White Mar 26 '11 at 18:23
  • Hi , Can you be more specific ? I don't want to manually create a serviceBean in XML file. But I am not clear about "component-scan tag is in the same config or parent of txManager" ? Indeed , the "context:component-scan" and are in the same config ! But why it doesn't work ? – smallufo Mar 27 '11 at 00:40
  • Now you are on to something; since you define component-scan and annotation-driven in the same config file I am at a loss. I am going to give you a +1 and hope someone can best my answer. – Andrew White Mar 27 '11 at 00:49