实际上,很久以前在 Legacy ATG 社区中推荐了一种最佳实践方法。只是贴在这里。
当您将 Order 对象与同步和事务一起使用时,需要遵循一个特定的使用模式。不遵循预期的模式会导致不必要的 ConcurrentUpdateExceptions、InvalidVersionExceptions 和死锁。在您的代码中必须严格遵守以下顺序:
- 获取配置文件 ID 的本地锁定。
- 开始交易
- 按订单同步
- 对订单对象执行所有修改。
- 调用 OrderManager.updateOrder。
- 结束同步
- 结束交易。
- 释放配置文件 ID 上的本地锁定。
第 1、2、7、8 步在需要更新订单的 ATG 表单处理程序的 beforeSet() 和 afterSet() 方法中为您完成。其中包括扩展 PurchaseProcessFormHandler 和 OrderModifierFormHandler(已弃用)的表单处理程序。如果您的代码在 PurchaseProcessFormHandler 之外访问/修改订单,则可能需要手动获取本地锁。可以使用 TransactionLockService 来获取锁。
因此,如果您扩展了基于 PurchaseProcessFormHandler 的 ATG 表单处理程序,并在更新订单的 handleXXX() 方法中编写了自定义代码,您的代码应如下所示:
synchronized( order )
{
// Do order updates
orderManager.updateOrder( order );
}
如果您编写了自定义代码来更新 PurchaseProcessFormHandler 之外的订单(例如 CouponFormHandler、droplet、pipeline servlet、fulfillment-related),您的代码应该如下所示:
ClientLockManager lockManager = getLocalLockManager(); // Should be configured as /atg/commerce/order/LocalLockManager
boolean acquireLock = false;
try
{
acquireLock = !lockManager.hasWriteLock( profileId, Thread.currentThread() );
if ( acquireLock )
lockManager.acquireWriteLock( profileId, Thread.currentThread() );
TransactionDemarcation td = new TransactionDemarcation();
td.begin( transactionManager );
boolean shouldRollback = false;
try
{
synchronized( order )
{
// do order updates
orderManager.updateOrder( order );
}
}
catch ( ... e )
{
shouldRollback = true;
throw e;
}
finally
{
try
{
td.end( shouldRollback );
}
catch ( Throwable th )
{
logError( th );
}
}
}
finally
{
try
{
if ( acquireLock )
lockManager.releaseWriteLock( profileId, Thread.currentThread(), true );
}
catch( Throwable th )
{
logError( th );
}
}
当多个线程尝试更新同一个ATG 实例上的相同顺序时,此模式仅对防止 ConcurrentUpdateExceptions、InvalidVersionExceptions 和死锁有用。这对于商业站点上的大多数情况应该是足够的,因为会话粘性会将更新限制在同一 ATG 实例的同一订单上。