当前位置: 首页 > 知识库问答 >
问题:

使用受保护的块同步Hibernate插入

沈骞仕
2023-03-14

我试图通过MySQL中的Hibernate解决并发数据库插入冲突的问题。

我有一段代码,可以很容易地被多个线程同时执行。它检查数据库中是否存在记录,如果不存在,就插入一条新记录。对相关的子记录执行相同的insert-if-existing操作。如果两个线程试图同时保存子记录,我会得到一个ConstraintViolationException,因为两个线程在查询时都发现记录不存在,所以两个线程都试图保存同一个违反唯一约束的记录,其中一个失败了。

我正在尝试使用受保护的块在应用程序级别同步查询插入操作,以便一个线程在查询数据库之前等待另一个线程完成记录插入。但是,即使我看到同步工作正常,查询记录仍然不会返回任何结果,即使该记录已保留在另一个线程中也是如此。因此,约束冲突仍然会发生。

    < li >我正在使用Hibernate 5.1.0 < li >我正在手动管理数据库事务 < li >我已经全局启用了查询缓存和二级缓存,但我使用的是CacheMode。刷新选择查询 < li >我没有使用乐观或悲观的数据库锁定或行版本控制。

这是一个代码示例

在每个同步操作中,如果产品不存在,我会尝试保留一个产品,如果不存在,我会尝试保留一个相关的父供应商。

public class UpdateProcessor extends HttpServlet {

  // Indicator used for synchronizing read-insert operations
  public static Boolean newInsertInProgress = false;

  @Override
  public void doPost(HttpServletRequest request, HttpServletResponse response) {

    Session hbSession = null;
    Transaction tx = null;
    try {
      hbSession = HibernateUtils.getNewSession();

      UpdateProcessor.waitForInsert(); // if there is an insert in progress, wait for it to finish
      UpdateProcessor.notifyInsertStarted(); // obtain lock

      tx = hbSession.beginTransaction();

      Product existingProduct = findProductBySKU(sku);
      if(existingProduct == null) {

        Product newProduct = new Product();
        newProduct.setSKU(sku);

        Supplier existingSupplier = findSupplierByName(name);
        if(existingSupplier == null) {
          Supplier newSupplier = new Supplier();
          newSupplier.setName(name);
          db.save(newSupplier);
          newProduct.setSupplier(newSupplier);
        } else {
          newProduct.setSupplier(existingSupplier);
        }

        db.save(newProduct);
      }

      tx.commit();

    } catch (Exception t) {
      // <rollback transaction>
      response.sendError(500);
    } finally {

      // Safeguard to avoid thread deadlock - release lock always, if obtained
      if(UpdateProcessor.newInsertInProgress) {
            UpdateProcessor.notifyInsertFinished(); // release lock and notify next thread
      }

      // <close session>
    }
  }

  private static synchronized void waitForInsert() {
    if(!UpdateProcessor.newInsertInProgress) {
        log("Skipping wait - thread " + Thread.currentThread().getId() + " - " + System.currentTimeMillis());
        return;
    }
    while(UpdateProcessor.newInsertInProgress) {
        boolean loggedEntering = false;
        if(!loggedEntering) {
            log("Entering wait - thread " + Thread.currentThread().getId() + " - " + System.currentTimeMillis());
            loggedEntering = true;
        }
        try {
            UpdateProcessor.class.wait();
        } catch (InterruptedException e) {}
    }
    log("Exiting wait - thread " + Thread.currentThread().getId() + " - " + System.currentTimeMillis());
  }

  private static synchronized void notifyInsertStarted() {
    UpdateProcessor.newInsertInProgress = true;
    UpdateProcessor.class.notify();
    log("Notify start - thread " + Thread.currentThread().getId() + " - " + System.currentTimeMillis());
  }

  private static synchronized void notifyInsertFinished() {
    UpdateProcessor.newInsertInProgress = false;
    UpdateProcessor.class.notify();
    log("Notify finish - thread " + Thread.currentThread().getId() + " - " + System.currentTimeMillis());
  }
}

并发发出请求后的输出:

Skipping wait - thread 254 - 1478171162713
Notify start - thread 254 - 1478171162713
Entering wait - thread 255 - 1478171162713
Entering wait - thread 256 - 1478171162849
Notify finish - thread 254 - 1478171163050
Exiting wait - thread 255 - 1478171163051
Notify start - thread 255 - 1478171163051
Entering wait - thread 256 - 1478171163051
Error - thread 255:
org.hibernate.exception.ConstraintViolationException: could not execute statement
...
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '532-supplier-name-1' for key 'supplier_name_uniq'

保存新的供应商记录仍然会在线程255中引发异常,因为违反了唯一约束(id,name)。

为什么SELECT在同步插入后仍然不返回任何记录?保护锁是避免多插入问题的正确方法吗?

共有1个答案

姬天宇
2023-03-14

根据梅奇科夫上面的回答:

简而言之:我需要在同步代码中包含Hibernate会话的创建。

长答案:受保护的块正确同步了查询插入块,但问题是即使一个线程完成了记录的持久化,在创建新的Hibernate会话之前,第二个线程也无法看到数据库中的更改。因此,并发数据库修改的影响并非对所有线程都立即可见。在其他线程中进行插入后,通过创建会话获得最新的数据库状态。在同步代码中包含会话创建可确保情况确实如此。

 类似资料:
  • 我希望Hibernate能够使用受保护的无参数构造函数实例化类,然而,我们得到了:。 更改自: 致: 问题已经解决了。我们使用的Hibernate版本:3.6.10.final。 对于什么时候无参数构造函数必须是而不是才能使Hibernate工作,是否有任何规则?

  • 6xx受保护 600 Series,Repliesregardingconfidentialityandintegrity 631 Integrityprotectedreply. 632 Confidentialityandintegrityprotectedreply. 633 Confidentialityprotectedreply.

  • 在< code>/users下,我有一些需要身份验证令牌的路由,还有一些不需要。为了实现这一点,我做了以下工作。 然后我按照以下方式安装这些路线。 当我向< code>/users发送POST时,它运行预期的路径,但是当< code>next()被调用时,< code > protected _ middleware 运行。这是因为它在标有“D”的行中找到了下一个< code>/users定义。

  • 我正在尝试使用SAML保护资源。有三个参与者在起作用:身份提供者(IDP,在我的控制范围之外)、服务提供者(SP,我碰巧在使用spring security saml,但这个问题并不特定于此),以及受保护的资源(PR,SP之外服务中的某个受保护endpoint)。 我需要支持两种场景: 用户第一次尝试访问PR,没有任何会话 对于场景1应该如何工作,有足够的指导,因为根据我所看到的,这是使用SAML

  • 我一直在尝试使用fpdf和fpdi向受密码保护的pdf(仅写保护)添加水印。 它在普通PDF上工作,但当它受密码保护时,会出现以下错误: FPDF错误:此文档(upd/509ae4da4044df9a43e03e09b4cf772b0.pdf)可能使用了FPDI附带的免费解析器不支持的压缩技术。 在文档上写是不可能的,这是有道理的,但是在搜索时,我看到类似的问题得到了解决。 感谢先进。

  • 本文向大家介绍svn 使用受密码保护的存储库,包括了svn 使用受密码保护的存储库的使用技巧和注意事项,需要的朋友参考一下 示例 可以配置Subversion存储库,以便某些用户只能访问某些内容或命令。为了访问此受限内容,您将需要指定用户名和密码。 您的用户名和密码可以直接在命令中指定: 不幸的是,这会使您的密码在控制台上以纯文本形式出现。为了避免这种可能的安全问题,请指定用户名而不是密码。这样做