簡體   English   中英

使用Spring處理Hibernate樂觀鎖定

[英]Handle Hibernate optimistic locking with Spring

我正在使用Hibernate和Spring Data,它將在插入或更新實體時執行樂觀鎖定,如果數據庫中的版本與要保留的版本不匹配,則會拋出StaleObjectStateException異常,在Spring中,您需要捕獲它使用ObjectOptimisticLockingFailureException

我想做的就是捕獲異常,並要求用戶刷新頁面以從數據庫中獲取最新數據,如下所示:

public void cancelRequest()
{
    try
    {
        request.setStatus(StatusEnum.CANCELLED);
        this.request = topUpRequestService.insertOrUpdate(request);
        loadRequests();
        //perform other tasks...
    } catch (ObjectOptimisticLockingFailureException ex)
    {
        FacesUtils.showErrorMessage(null, "Action Failed.", FacesUtils.getMessage("message.pleaseReload"));
    }
}

我認為它也可以與下面的代碼一起使用,但是我尚未對其進行測試。

public void cancelRequest()
{
    RequestModel latestModel = requestService.findOne(request.getId());
    if(latestModel.getVersion() != request.getVersion())
    {
        FacesUtils.showErrorMessage(null, "Action Failed.", FacesUtils.getMessage("message.pleaseReload"));
    } 
    else
    {
        request.setStatus(StatusEnum.CANCELLED);
        this.request = requestService.insertOrUpdate(request);
        loadRequests();
        //perform other tasks...
    }
}

我需要在我調用requestService.insertOrUpdate(request);任何地方應用此檢查requestService.insertOrUpdate(request); 我不想一一應用。 因此,我決定將檢查代碼放在函數insertOrUpdate(entity)本身內。

@Transactional
public abstract class BaseServiceImpl<M extends Serializable, ID extends Serializable, R extends JpaRepository<M, ID>>
        implements BaseService<M, ID, R>
{

    protected R repository;
    protected ID id;

    @Override
    public synchronized M insertOrUpdate(M entity)
    {
        try
        {
            return repository.save(entity);
        } catch (ObjectOptimisticLockingFailureException ex)
        {
            FacesUtils.showErrorMessage(null, FacesUtils.getMessage("message.actionFailed"),
                        FacesUtils.getMessage("message.pleaseReload"));
            return entity;
        }
    }
}

我的主要問題是,這種方法會有一個問題。 調用方將不知道實體是否成功持久化,因為將在函數內部捕獲並處理異常,因此調用方將始終假定持久化是成功的,並繼續執行其他任務,我不這樣做想。 如果無法持續,我希望它停止執行任務:

public void cancelRequest()
{
    try
    {
        request.setStatus(StatusEnum.CANCELLED);
        this.request = topUpRequestService.insertOrUpdate(request);
        //I want it to stop here if fail to persist, don't load the requests and perform other tasks.
        loadRequests();
        //perform other tasks...
    } catch (ObjectOptimisticLockingFailureException ex)
    {
        FacesUtils.showErrorMessage(null, "Action Failed.", FacesUtils.getMessage("message.pleaseReload"));
    }
}

我知道在調用insertOrUpdate ,我可以通過聲明新的模型變量來捕獲返回的對象,並將其版本與原始版本進行比較,如果版本相同,則表示持久性失敗。 但是,如果以這種方式執行此操作,則必須在所有調用insertOrUpdate地方編寫版本檢查代碼。 還有什么更好的辦法嗎?

能夠做到這一點且不必在所有調用點上進行重大代碼更改的最接近方法是研究某種類似於Spring的@Transactional注釋的Spring AOP建議。

@FacesReloadOnException( ObjectOptimisticLockingFailureException.class )
public void theRequestHandlerMethod() {
  // call your service here
}

這個想法是@FacesReloadOnException注釋會觸發環繞通知,該通知會捕獲注釋值中提供的任何異常,並且基本上會處理拋出任何異常類的FacesUtils的調用。

您可以使用的其他選項不會很直接,並且需要您以某種方式觸摸所有使用點,這是不可避免的。

但是,如果您不想更改服務層的方法返回類型,那么我當然不會考慮在服務層中放置try/catch塊,因為如您所指出的,控制器將需要更多上下文。 try/catch塊向下游推送的唯一方法是,如果您返回了某種類型的Result對象,然后您的控制器可以檢查該對象,例如

public void someControllerRequestMethod() {
  InsertOrUpdateResult result = yourService.insertOrUpdate( theObject );
  if ( result.isSuccess() ) {
    loadRequests();       
  }
  else {
    FacesUtils.showErrorMessage( ... );
  }
}

否則,如果您想以某種方式將其集中到您的Web層中,就需要發揮創意。 可能是模仿您的BaseService接口的Web層實用程序類,如下所示:

public <T extends BaseService, U> U insertOrUpdate(T service, U object, Consumer<U> f) {
  try {
    U result = service.insertOrUpdate( object );
    f.accept( result );
    return result;
  }
  catch ( ObjectOptimisticLockingFailureException e ) {
    FacesUtils.showErrorMessage( ... );
  }
}

但坦率地說,除非您有很多呼叫站點與類似的對這樣的消費者進行一般化的地方足夠合理,否則您可能會發現它比僅僅try/catch更多的努力和工作來進行一般化阻止控制器本身。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM