2.4 乐观锁

乐观锁实现中,要求一个实体类中只能有一个乐观锁字段。

配置 @Version

想要使用乐观锁,只需要在实体中,给乐观锁字段增加 @tk.mybatis.mapper.annotation.Version 注解。

例如:

  1. public class User {
  2. private Long id;
  3. private String name;
  4. //...
  5. @Version
  6. private Integer version;
  7. //setter and getter
  8. }

@Version 注解有一个 nextVersion 属性,默认值为默认的实现,默认实现如下:

  1. package tk.mybatis.mapper.version;
  2. import java.sql.Timestamp;
  3. /**
  4. * @author liuzh
  5. * @since 3.5.0
  6. */
  7. public class DefaultNextVersion implements NextVersion {
  8. @Override
  9. public Object nextVersion(Object current) throws VersionException {
  10. if (current == null) {
  11. throw new VersionException("当前版本号为空!");
  12. }
  13. if (current instanceof Integer) {
  14. return (Integer) current + 1;
  15. } else if (current instanceof Long) {
  16. return (Long) current + 1L;
  17. } else if (current instanceof Timestamp) {
  18. return new Timestamp(System.currentTimeMillis());
  19. } else {
  20. throw new VersionException("默认的 NextVersion 只支持 Integer, Long" +
  21. " 和 java.sql.Timestamp 类型的版本号,如果有需要请自行扩展!");
  22. }
  23. }
  24. }

默认实现支持 Integer, Longjava.sql.Timestamp ,如果默认实现不能满足自己的需要,可以实现自己的方法,在配置注解时指定自己的实现即可。

支持的方法

  • delete
  • deleteByPrimaryKey
  • updateByPrimaryKey
  • updateByPrimaryKeySelective
  • updateByExample
  • updateByExampleSelective

这些方法在执行时会更新乐观锁字段的值或者使用乐观锁的值作为查询条件。

需要注意的地方

在使用乐观锁时,由于通用 Mapper 是内置的实现,不是通过 拦截器 方式实现的,因此当执行上面支持的方法时,如果版本不一致,那么执行结果影响的行数可能就是 0。这种情况下也不会报错!

所以在 Java6,7中使用时,你需要自己在调用方法后进行判断是否执行成功。

在 Java8+ 中,可以通过默认方法来增加能够自动报错(抛异常)的方法,例如:

  1. public interface MyMapper<T> extends Mapper<T> {
  2. default int deleteWithVersion(T t){
  3. int result = delete(t);
  4. if(result == 0){
  5. throw new RuntimeException("删除失败!");
  6. }
  7. return result;
  8. }
  9. default int updateByPrimaryKeyWithVersion(Object t){
  10. int result = updateByPrimaryKey(t);
  11. if(result == 0){
  12. throw new RuntimeException("更新失败!");
  13. }
  14. return result;
  15. }
  16. //...
  17. }

通过增加上面这种默认方法就能实现。