情况:
我们有一个 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;
}
}
有没有人知道如何防止这种并发问题的发生?