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;
    }

猜你喜欢

转载自blog.csdn.net/u012326462/article/details/81349157
今日推荐