I have a wicket page , which contains two Spring-managed beans , one is DAO , another is Service Object :
public class MergeAccountsPage extends WebPage
{
@SpringBean
private MergeEmailDao mergeEmailDao;
@SpringBean
private MergingService mergingService;
}
The MergingService
's implementation's methods are mostly annotated with @Transactional
, so every action involving MergingService works fine.
But the problem comes here :
Link<Void> link = new Link<Void>("cancelLink") {
@Override
public void onClick() {
ma.setNewEmail(null);
ma.setNewEmailClicked(null);
ma.setNewEmailSentTime(null);
mergeAccoungDao.update(ma); //not written to DB
setResponsePage(...);
}
};
The link will call mergeAccoungDao.update(ma)
to update a row in DB.
But the data is not updated to DB , I think it is because the DAO is not wrapped in @Transaction nor tx:advice
and aop
tags.
I wonder is there a way to programmatically get the transaction manager , and manually open/close the transaction ?
Note: I can solve the problem by adding this code in spring's XML :
<tx:advice id="txAdviceApp" transaction-manager="transactionManagerApp">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="*" propagation="SUPPORTS"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="methods" expression="execution(* destiny.utils.AbstractDao+.*(..))"/>
<aop:advisor advice-ref="txAdviceApp" pointcut-ref="methods"/>
</aop:config>
So that the DAO's save/update/delete will work like a charm.
But I'd not like to add this config . Because in fact , the DAO extends an AbstractDao , and there are other DB/DAOs extend this AbstractDao :
public interface AbstractDao<T> {
public T get(Serializable id);
public T save(T t);
public T update(T t);
public void delete(T t);
}
public abstract class AbstractDaoJpaImpl<T> implements AbstractDao<T>
public interface MergeAccountDao extends AbstractDao<MergeAccount>
@Repository
public class MergeAccountDaoImpl extends AbstractDaoJpaImpl<MergeAccount> implements MergeAccountDao
Therefore , if this AbstractDAO's CRUD is "adviced" by this transactionManagerApp , other DAOs may have problem , because other DAOs may depend on txManagerForum , txManagerBank , txManagerUser ...etc.
Back to the problem , is there a way to programmatically get txManager ? Such as :
TransactionManager txManager = TxManagerThreadLocal.get();
txManager.begin();
ma.setNewEmailSentTime(null);
mergeAccoungDao.update(ma);
txManager.commit();
Or is there any better way to wrap a transaction to the DAO ?
Thanks a lot.
You have to inject the transaction manager in the class you want to use it. You can use constructor or property based injection for it or use autowiring. One you get the transaction manger, you can use the programmatic transaction support in Spring to start and commit the transaction. Here is the sample code from Spring reference 2.5:
public class SimpleService implements Service {
// single TransactionTemplate shared amongst all methods in this instance
private final TransactionTemplate transactionTemplate;
// use constructor-injection to supply the PlatformTransactionManager
public SimpleService(PlatformTransactionManager transactionManager) {
Assert.notNull(transactionManager, "The 'transactionManager' argument must not be null.");
this.transactionTemplate = new TransactionTemplate(transactionManager);
}
public Object someServiceMethod() {
return transactionTemplate.execute(new TransactionCallback() {
// the code in this method executes in a transactional context
public Object doInTransaction(TransactionStatus status) {
updateOperation1();
return resultOfUpdateOperation2();
}
});
}
}
See the reference for more details.