状況:
バーコード付きの PDF を生成する webapp (Java EE-Spring-Hibernate) があります。バーコードには、実行時に生成される ID が含まれています。Hibernate を使用して現在の日付の ID があるかどうかを確認する Java サービスがあり、存在しない場合はエントリが作成されます。存在する場合は、インクリメントおよび更新されます。次のフィールドを含む 1 日あたり 1 つの行があります: id、coverPageId、date
そのため、ユーザーがその日の最初のバーコードを作成すると、現在の日付の行が追加され、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;
}
}
この同時実行性の問題が発生するのを防ぐ方法を他に知っている人はいますか?