繁体   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