Spring dat jpa@version楽観ロック

15252 ワード

前言
MysqlはInnodbエンジンを使用して、トランザクション、ローロックをサポートします.では、高同時性の場合、複数のトランザクションが同時に行われます.複数のトランザクションが同じデータを操作し、成功した場合、ダーティリードなどの問題が発生し、最終的にデータベースのデータに問題が発生します.データの原子性,一意性,正確性を破壊した.
簡単に栗を挙げます.
銀行振替:李さんのカードには10元あります.李さんは王さんと同时にお金を取りに来ました.李さんは2元、王さんは2元取りました.お金を引き出す操作に事務が加わった.
【予想結果、最終残量:10-2-2=6】【実績、最終残量:8】
原因:李さんはお金を取って、文脈は事務1を開いて、読み取った残高は10です;王さんはお金を取って、この时事務1はまだ提出していないで、また事務2を開いて、読み取った残高も10です;事務1事務2がお金を引き出したことを知らず、提出に成功した.データベースは最終的に8.
@Version
会社用spring data jpaは、@version注記にテーブルデータを追加する楽観的なロックのサポートにより@version注記を追加し、システムが保存するたびに自動的にこの属性値に1を追加します.取引に協力して、バージョンが間違っていて、取引は提出することができなくて、データはロールバックして、データが汚れていないことを保証します
エンティティベースクラス:DomainObject
package com.test.domain;

import javax.persistence.*;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@MappedSuperclass
public class DomainObject implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    Long id;

    @Version
    @Column(name = "version", length = 11)
    private int version = 0;


    @Column(name = "guid")
    String guid = GuidGenerator.createGuid();


    @Column(name = "archived", length = 1)
    boolean archived = false;

    @Column(name = "created_datetime")
    protected LocalDateTime createdDateTime = LocalDateTime.now();


    @Column(name = "last_modified_datetime")
    LocalDateTime lastModifiedDateTime;

    @PrePersist
    @PreUpdate
    public void updateLastModifiedDateTime() {
        lastModifiedDateTime = LocalDateTime.now();
    }

    public void updateLastModifiedDateTime(LocalDateTime localDateTime) {
        lastModifiedDateTime = localDateTime;
    }

    public void archive() {
        archived = true;
    }

    public void unarchive() {
        archived = false;
    }


    public boolean isArchived() {
        return archived;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null || !(obj instanceof DomainObject)) {
            return false;
        }

        DomainObject other = (DomainObject) obj;

        // if the id is missing, return false
        if (guid() == null) {
            return false;
        }

        // equivalence by guid
        return guid().equals(other.guid());
    }

    @Override
    public int hashCode() {
        return guid.hashCode();
    }

    public String guid() {
        return guid;
    }

    public int version() {
        return version;
    }

    public LocalDateTime createdDateTime() {
        return createdDateTime;
    }

    public LocalDateTime lastModifiedDateTime() {
        return lastModifiedDateTime;
    }

    public boolean isNew() {
        return id == null;
    }

    public boolean isPersisted() {
        return id != null;
    }

    public void copyFrom(DomainObject domainObject) {
        this.guid = domainObject.guid;
        this.archived = domainObject.archived;
        this.version = domainObject.version;
        this.createdDateTime = domainObject.createdDateTime;
        this.lastModifiedDateTime = domainObject.lastModifiedDateTime;
    }

}


他のエンティティをこのエンティティに継承すればいいです.トランザクションがコミットされると、バージョンによってデータが検証され、トランザクションがコミットされたときにバージョンが変更された場合、今回のトランザクションのコミットに失敗し、ロールバックされます.これにより、データの一貫性が保証されます.