Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/database/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 读取/更新时休眠并发(?)问题_Java_Database_Spring_Hibernate_Concurrency - Fatal编程技术网

Java 读取/更新时休眠并发(?)问题

Java 读取/更新时休眠并发(?)问题,java,database,spring,hibernate,concurrency,Java,Database,Spring,Hibernate,Concurrency,情况: 我们有一个webapp(javaeespringhibernate)可以生成带有条形码的PDF。条形码包含运行时生成的ID。我们有一个Java服务,它使用Hibernate检查当前日期是否有ID,如果没有ID,则创建一个条目。如果存在,它将递增并更新。 每天有一行包含以下字段:id、coverPageId、日期 因此,当用户创建当天的第一个条形码时,将为当前日期添加一行coverPageId=1。然后,所有后续的条形码将从数据库中获取最后一个id,将其递增,并使用递增的值保存该行 问题:

情况:

我们有一个webapp(javaeespringhibernate)可以生成带有条形码的PDF。条形码包含运行时生成的ID。我们有一个Java服务,它使用Hibernate检查当前日期是否有ID,如果没有ID,则创建一个条目。如果存在,它将递增并更新。 每天有一行包含以下字段:id、coverPageId、日期

因此,当用户创建当天的第一个条形码时,将为当前日期添加一行coverPageId=1。然后,所有后续的条形码将从数据库中获取最后一个id,将其递增,并使用递增的值保存该行

问题:

当多个用户同时创建PDF时,Hibernate将为两个用户获取相同的值,从而在PDF上生成相同的条形码

尝试过的解决方案:

获取当前id、增加id和更新行的代码都以相同的方法执行。这是Springbean中的一个方法。因为SpringBean是单例的,所以我尝试使方法
同步化
。这没有帮助

方法:

@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;
}

有人知道如何防止这种并发问题发生吗?

我相信这与Hibernate无关。即使您没有使用Hibernate,您仍然会面临这样的问题

您有一些选择:

考虑对相关部分不使用Hibernate。许多数据库都具有执行原子“如果不存在则插入,如果存在则更新”逻辑的功能

如果您确实想使用Hibernate,以下是您需要的:

  • 确保数据库中有唯一约束,以避免重复记录
  • 在程序逻辑中,如果发现没有记录并尝试插入,请准备捕获重复数据/约束冲突的异常。在这种情况下,不要认为你的逻辑是失败的。改为执行后续更新

  • 关于您尝试过的使用对象实例处理插入/更新操作的解决方案,您的方法行不通

    首先,如果您实际上将所有ID保持为该bean的状态,并且如果您的应用程序只有一个进程,那么它就可以工作了。您的代码每次都要到DB进行检查,并相应地进行插入/更新。即使这段代码是同步的,它也不会工作。请记住,在不同的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
    
    看到了吗?你仍然面临着同样的问题


    除了我的方法之外,还有其他方法可以解决问题,但至少,您使用的方法不会有任何帮助。

    我相信这与Hibernate无关。即使您没有使用Hibernate,您仍然会面临这样的问题

    您有一些选择:

    考虑对相关部分不使用Hibernate。许多数据库都具有执行原子“如果不存在则插入,如果存在则更新”逻辑的功能

    如果您确实想使用Hibernate,以下是您需要的:

  • 确保数据库中有唯一约束,以避免重复记录
  • 在程序逻辑中,如果发现没有记录并尝试插入,请准备捕获重复数据/约束冲突的异常。在这种情况下,不要认为你的逻辑是失败的。改为执行后续更新

  • 关于您尝试过的使用对象实例处理插入/更新操作的解决方案,您的方法行不通

    首先,如果您实际上将所有ID保持为该bean的状态,并且如果您的应用程序只有一个进程,那么它就可以工作了。您的代码每次都要到DB进行检查,并相应地进行插入/更新。即使这段代码是同步的,它也不会工作。请记住,在不同的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
    
    看到了吗?你仍然面临着同样的问题


    除了我的方法之外,还有其他方法可以解决问题,但至少,您使用的方法不会有任何帮助。

    即使不是Hibernate的错,我的同步方法也能解决问题,不是吗?一个对象实例(单例),因此所有调用都通过同一对象的方法进行。如果synchronized已经在处理呼叫,那么它应该让所有呼叫者等待……这不会有帮助。我将更新我的答案。如果我在方法内提交事务?你可以,但显然这不是一个好方法。使用事务的一个重要原因是为您的工作单元提供原子性。以这种方式提交将使您的应用程序容易出错,并且难以维护。然而,实际上您可以尝试另一个类似的解决方案:在自己的事务中创建“插入/更新”逻辑(独立于原始txn)。然而,您必须注意,这种方法将导致u更新记录,即使整个txn需要回滚。如果这对你来说很好,这是一个你可以考虑的解决方案。即使不是Hibernate的错误,我的同步方法也应该做到这一点,不是吗?一个对象实例(单例),因此所有调用都通过同一对象的方法进行。如果synchronized已经在处理呼叫,那么它应该让所有呼叫者等待……这不会有帮助。我将更新我的答案。如果我在方法内提交事务?你可以,但显然这不是一个好方法。使用事务的一个重要原因是为您的工作单元提供原子性。以这种方式提交将使您的应用程序容易出错,并且难以维护。然而,实际上您可以尝试另一个类似的解决方案:在自己的事务中创建“插入/更新”逻辑(独立于原始txn)。豪