Doma2で楽観排他

Doma2で楽観排他を実現する方法と更新失敗時の例外処理についてまとめる。

Domaの設定方法

エンティティクラスの楽観ロック用のバージョンカラムにあたるメンバ変数に@Versionを付与する。

@Entity
@Table(name = "sample")
@ToString
@EqualsAndHashCode
public class Sample {

    /** ID */
    @Id
    @Column(name = "id")
    Integer id;
    
    ・
    ・
 
    /** バージョン */
    @Version
    @Column(name = "version")
    Integer version;
    
    ・
    ・
}

エンティティを自動生成する場合は、バージョン列の名前をversionもしくはversion_noとすることで、自動で@Versionアノテーションが付与される。

上記設定をしてデータを更新すると、以下のようなUPDATE文が実行される。

update sample 
set 
  name = '佐藤', 
  age = 27, 
  description = '理系', 
  version = 1 + 1 
where 
  id = 3 and 
  version = 1

楽観ロック例外の処理(Spring Boot)

楽観ロック例外にはOptimisticLockingFailureExceptionがスローされるので、例外ハンドラで例外をキャッチして任意の処理を行う。

以下は楽観ロック例外を捕捉して、画面にエラーメッセージを表示する例である。

〇Service

import org.springframework.stereotype.Service;

@Service
@DBTransactional
public class SampleServiceImpl implements SampleService {

    /** dao */
    private SampleDao dao;

    /**
     * @param dao
     */
    public SampleServiceImpl(SampleDao dao) {
        this.dao = dao;
    }

    @Override
    public void updateData(Sample sample) {
        sample.setDescription("りんごゼリーにはまる");
        dao.update(sample);
    }

    @Override
    public Sample select() {
        Sample sample = dao.selectById(3);
        return sample;
    }
}

〇Controller

package jp.co.cti.terashite.webapp.register.controller;

import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class HomeController {

    /** サービス */
    private SampleService service;

    /**
     * @param service
     */
    public HomeController(SampleService service) {
        this.service = service;

    }
    
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String index() {
        return "index";
    }

    @RequestMapping(value = "/select", method = RequestMethod.POST)
    public String result(Model model) {
        Sample sample = service.select();
        model.addAttribute("sample", sample);
        return "select";
    }

    @RequestMapping(value = "/update", method = RequestMethod.POST)
    public String result2(@ModelAttribute Sample sample) {
        service.updateData(sample);
        return "result";
    }

    @ExceptionHandler(OptimisticLockingFailureException.class)
    public String error(Model model) {
        model.addAttribute("message", "更新に失敗しました。検索からやり直してください");
        return "index";
    }
}