簡體   English   中英

讀取/更新時休眠並發(?)問題

[英]Hibernate concurrency(?) issue when reading/updating

情況:

我們有一個webapp(Java EE-Spring-Hibernate),可以生成帶有條形碼的PDF。 條形碼包含在運行時生成的ID。 我們有一個Java服務,該服務使用Hibernate來檢查當前日期是否存在ID,如果不存在,則創建一個條目。 如果存在,則對其進行遞增和更新。 每天有一行包含以下字段:id,coverPageId,日期

因此,當用戶創建當天的第一個條形碼時,將為當前日期添加一行,其中coverPageId = 1。 然后,所有后續條形碼將從數據庫中獲取最后一個ID,對其進行遞增,並使用遞增的值保存行。

問題:

當多個用戶同時創建PDF時,Hibernate將為兩個用戶獲取相同的值,從而導致PDF上的條形碼相同。

嘗試過的解決方案:

獲取當前ID,對其進行遞增並更新該行的代碼都以相同的方法執行。 這是Spring bean中的一種方法。 由於Spring bean是單例,因此我嘗試使該方法synchronized 這沒有幫助。

方法:

@Transactional(isolation=Isolation.SERIALIZABLE)
public synchronized long getNextId(Date date)
{
    List<CoverpageID> allCoverpageIds = this.getAllCurrentIds(date);

    if(allCoverpageIds.size() == 0)
    {
        loggingService.warn(String.format("Found no existing ID for date '%tD', creating new record", date));
        System.out.println(String.format("Found no existing ID for date '%tD', creating new record", date));
        return this.createNewCoverpageIdRecord(new Date());
    }
    else if(allCoverpageIds.size() == 1)
    {
        CoverpageID coverpageId = allCoverpageIds.get(0);
        loggingService.debug(String.format("Found existing ID for date '%tD': %d, incrementing and persisting", date, coverpageId.getCoverPageId()));
        System.out.println(String.format("Found existing ID for date '%tD': %d, incrementing and persisting", date, coverpageId.getCoverPageId()));
        coverpageId.setCoverPageId(coverpageId.getCoverPageId() + 1);
        dao.save(coverpageId);
        loggingService.debug(String.format("Saved existing ID for date '%tD' with incremented ID: %d", date, coverpageId.getCoverPageId()));
        return coverpageId.getCoverPageId();
    }
    else
    {
        loggingService.warn(String.format("Found multiple records for date '%tD'", date));
        return -1;
    }
}

private List<CoverpageID> getAllCurrentIds(Date date)
{
    String exClause = "where date = :date";
    Map<String, Object> values = new HashMap<String, Object>();
    values.put("date", date);
    return dao.getAllEx(CoverpageID.class, exClause, values);
}

private long createNewCoverpageIdRecord(Date date)
{
    dao.save(new CoverpageID(new Date(), new Long(1)));
    return 1;
}

CoverpageID.class(條形碼的實體):

@NamedQueries({
@NamedQuery(
    name = "getCoverPageIdForDate",
    query = "from CoverpageID c where c.date = :date",
    readOnly = true
)})
@Entity
public class CoverpageID
{
private Long id;
private Date date;
private long coverPageId;

public CoverpageID() {}

public CoverpageID(Date date, Long coverPageId)
{
    this.date = date;
    this.coverPageId = coverPageId;
}

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

@Id
@GeneratedValue(strategy=GenerationType.AUTO)
public Long getId()
{
    return id;
}

@Temporal(TemporalType.DATE)
public Date getDate()
{
    return date;
}

public void setDate(Date date)
{
    this.date = date;
}

public long getCoverPageId()
{
    return coverPageId;
}

public void setCoverPageId(long coverPageId)
{
    this.coverPageId = coverPageId;
}
}

有誰知道我們如何防止發生並發問題?

我相信這與Hibernate無關。 即使您沒有使用Hibernate,您仍然會遇到這樣的問題。

有一些選擇供您選擇:

考慮不將Hibernate用於相關部分。 許多數據庫具有執行原子性的“如果不存在則進行更新,如果存在”的邏輯。

要么

如果您確實想使用Hibernate,則需要以下內容:

  1. 確保您在數據庫中具有唯一約束,以避免重復記錄
  2. 在程序邏輯中,如果發現沒有記錄並嘗試插入,請做好准備以捕獲重復數據/約束沖突的異常。 在這種情況下,請勿將您的邏輯視為失敗。 而是進行后續更新。

對於通過使用對象實例來處理插入/更新操作的已嘗試解決方案,您的方法將行不通。

首先,如果您實際上將所有ID保留為該bean的狀態,並且您的應用程序只有一個進程,那么它將正常工作。 您的代碼將每次都發送給DB進行檢查,並相應地進行插入/更新。 即使這段代碼是同步的,也無法正常工作。 請記住,在不同的數據庫會話之間,只有在實際提交事務后,一個會話的更改才對其他會話可見。 所以你會有這樣的情況

THREAD 1                    THREAD 2
-------------------------------------------------
enter method
check DB, no record found   enter method (wait coz synchronized)
insert record
exit method
                            check DB, no record found (coz prev txn not commited yet)
                            insert record
                            exit method
commit
                            commit

看到? 您仍然面臨着同樣的問題。

除了我的以外,還有其他解決方法,但至少,您使用的方法無濟於事。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM