如何以编程方式在线程中获取事务管理器?

发布于 2024-09-07 09:33:31 字数 2622 浏览 6 评论 0原文

我有一个 wicket 页面,其中包含两个 Spring 管理的 bean,一个是 DAO,另一个是服务对象:

public class MergeAccountsPage extends WebPage
{
  @SpringBean
  private MergeEmailDao mergeEmailDao;

  @SpringBean
  private MergingService mergingService;
}

MergingService 的实现方法大多用 @Transactional 进行注释,因此涉及 MergingService 的每个操作都可以正常工作。

但问题来了:

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(...);
  }
};

链接将调用mergeAccoungDao.update(ma)来更新数据库中的一行。

但是数据没有更新到 DB ,我认为这是因为 DAO 没有包装在 @Transaction 中,也没有 tx:adviceaop 标签中。

我想知道是否有一种方法可以以编程方式获取事务管理器,并手动打开/关闭事务?

注意:我可以通过在 spring 的 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>

这样 DAO 的保存/更新/删除功能就会像魅力一样工作。

但我不想添加这个配置。因为事实上,DAO 扩展了一个 AbstractDao ,并且还有其他 DB/DAO 扩展了这个 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

因此,如果这个 AbstractDAO 的 CRUD 被这个 transactionManagerApp “建议”,其他 DAO 可能会出现问题,因为其他 DAO 可能依赖于 txManagerForum 、 txManagerBank 、 txManagerUser ...等。

回到问题,有没有办法以编程方式获取 txManager ?如:

TransactionManager txManager = TxManagerThreadLocal.get();
txManager.begin();
ma.setNewEmailSentTime(null);
mergeAccoungDao.update(ma); 
txManager.commit();

或者有没有更好的方法将事务包装到DAO?

多谢。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

汐鸠 2024-09-14 09:33:31

您必须将事务管理器注入到要使用它的类中。您可以使用构造函数或基于属性的注入,或者使用自动装配。
一旦获得事务管理器,就可以使用 Spring 中的编程事务支持来启动和提交事务。以下是 Spring 参考 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();
      }
    });
  }
}

请参阅 参考了解更多详情。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文