Spring data jpa @Version注解及自定义数据库乐观锁实现

 

在数据库并发操作时,为了保证数据的正确性,经常要对数据加锁,加锁有两种方式:悲观锁、乐观锁

悲观锁:把所需要的数据全部加锁,不允许其他事务对数据做修改 

update xxx where xxxx for update

乐观锁:对数据进行版本校验,如果版本不一致,则操作数据失败   

update xxx,version+1  where xxxx and version=x

 

在jpa中,@Version注解,可以实现乐观锁功能

实体类Account,version属性上加@Version注解

package com.xhx.springboot.entity;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Version;

/**
 * @author xuhaixing
 * @date 2018/4/28 10:29
 */
@Entity
public class Account {
    @Id
    private int id;
    private String name;
    private Double money;
    @Version
    private int version;


    public int getVersion() {
        return version;
    }

    public void setVersion(int version) {
        this.version = version;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Double getMoney() {
        return money;
    }

    public void setMoney(Double money) {
        this.money = money;
    }
}

在更新数据时,需要用jpa自己实现的save方法

    <S extends T> S save(S var1);

    <S extends T> Iterable<S> saveAll(Iterable<S> var1);

如果是自己写的update方法,下面这样,是不生效的

@Repository
public interface AccountDao extends JpaRepository<Account, Integer> {


    @Modifying
    @Query("update Account set name=:name, money=:money where id=:id")
    int updateAccount(@Param("id") int id,@Param("name") String name, @Param("money") double money);

}

 

数据库数据如下:

我们更新id是10的数据,数据库中版本是0,我们设置版本1

        Account account = new Account();
        account.setId(10);
        account.setName("eeee");
        account.setMoney(7999.0);
        account.setVersion(1);

        accountController.update(account);

报如下错误:

org.springframework.orm.ObjectOptimisticLockingFailureException: Object of class [com.xhx.springboot.entity.Account] with identifier [10]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.xhx.springboot.entity.Account#10]

	at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:298)
	at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:225)
	at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:527)

把版本号改成0,再更新,数据库中执行如下语句,更新成功

再看数据库,版本号+1了

 

 

自己实现可以这样:

@Modifying
@Query("update Account set name=:name, money=:money,version=:version+1 where id=:id and version=:version")
int updateAccountByVersion(@Param("id") int id,@Param("name") String name, @Param("money") double money,@Param("version") int version);

service中加判断,抛异常,这样就自己通过数据库实现了乐观锁

    @Transactional(rollbackFor = Exception.class)
    public int updateAccountByVersion(Account account){
        int i =accountDao.updateAccountByVersion(account.getId(),account.getName(),account.getMoney(),account.getVersion());
        if(i==0){
            throw new ObjectOptimisticLockingFailureException("更新account失败",new Exception());
        }
        return i;
    }

实时内容请关注微信公众号,公众号与博客同时更新:程序员星星

  • 12
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值