【问题标题】:Hibernate - Table Locked after update休眠 - 更新后表锁定
【发布时间】:2018-03-20 16:04:59
【问题描述】:

我正在通过使用 Hibernate 和 EntityManager 的方法执行更新。

这个更新方法被多次调用(在一个循环内)。

好像我第一次执行它时,它锁定了表并且没有释放它。

在关闭应用程序后尝试通过 SQL Developer 更新表时,我看到表仍被锁定,因为更新挂起。

您认为如何解决这个问题?如果您需要更多信息,请告诉我。

@Repository
@Transactional(propagation = REQUIRES_NEW)
public class YirInfoRepository {

    @Autowired
    EntityManager entityManager;

    @Transactional(propagation = REQUIRES_NEW)
    public void setSent(String id) {
        String query = "UPDATE_QUERY";
        Query nativeQuery = entityManager.createNativeQuery(String.format(query, id));
        nativeQuery.executeUpdate();
    }
}

更新

等了一个多小时后,我再次启动了应用程序,它运行良好,但现在又挂了。


更新 2 -- 我将给予任何帮助我解决此问题的人最大的赏金

在另一个地方,我使用了应用程序管理的实体管理器,但它仍然给我同样类型的错误。

public void fillYirInfo() {
    File inputFile = new File("path");
    try (InputStream inputStream = new FileInputStream(inputFile);
         BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) {
        bufferedReader.lines().skip(1).limit(20).forEach(line -> {
            String[] data = line.split(",");
            String rnr = data[0];
            String linked = data[1];
            String email = data.length > 2 ? data[2] : "";



    String insuredId = insuredPeopleRepository.getInsuredIdFromNationalId(rnr);
            int modifiedCounter = 0;

            if (!isNullOrEmpty(insuredId)) {
                EntityManager entityManager = emf.createEntityManager();
                EntityTransaction transaction = entityManager.getTransaction();
                Query nativeQuery = entityManager.createNativeQuery(
                        "QUERY"
                );
                transaction.begin();
                nativeQuery.executeUpdate();
                entityManager.flush();
                transaction.commit();
                entityManager.close();
            }

            System.out.println(modifiedCounter + " rows modified");
        });
    } catch (IOException e) {
        e.printStackTrace();
    }
}

【问题讨论】:

  • 看起来您没有提交交易。不知道如何在 Hibernate 中做到这一点。 Oracle 中的锁定是按行级别完成的,除非您通过 DDL 锁定整个对象,或者具有排他锁。
  • @FábioGalera 据我了解,这是一个容器管理的事务,因此它应该在方法完成时由容器自动提交。
  • @FábioGalera 奇怪的是更新是在不同的行上进行的。
  • 你为什么在类和方法级别都使用@Transactional? stackoverflow.com/questions/23132822/…
  • 另外,本地运行更新查询并不是使用 Hibernate/JPA 的首选方式。您应该从数据库中加载实体,在您的 Java 代码中对其进行变异,只要这是在事务中完成的,Hibernate 将确保在事务提交时将更改写入数据库。直接进行更新可能会弄乱 Hibernate 的缓存,这可能会导致奇怪的行为。

标签: spring oracle hibernate deadlock spring-transactions


【解决方案1】:

尝试不使用更新查询:

@Repository
@Transactional(propagation = REQUIRES_NEW)
public class YirInfoRepository {

    @Autowired
    EntityManager entityManager;

    @Transactional(propagation = REQUIRES_NEW)
    public void setSent(String id) {
        //guessing your class name and method..
        final YirInfo yirInfo = entityManager.find(YirInfo.class, id);
        yirInfo.setSent();
    }
}

可能不如单个更新查询快,但可以相当快地获得它,除非数据量很大。这是使用 Hibernate/JPA 的首选方式,您可以使用实体/对象和(有时)HQL/JPQL 查询,而不是考虑单个值和 SQL 查询。

【讨论】:

  • 您好,感谢您的回答,但由于性能问题,我必须坚持使用本机查询。
【解决方案2】:

您正在使用@Transactional 注释。这意味着您正在使用 Spring Transaction。然后在您的 UPDATE 2 中,您自己使用事务并由 spring 管理(我猜这是另一个不受 Spring 管理的项目或类)。

在任何情况下,我都会尝试在单个 spring 事务中更新您的记录,并且我不会在 DAO 层而是在服务层中使用 @Transactional。像这样的:

服务层:

@Service
public class YirInfoService {
    @Autowired
    YirInfoRepository dao;

    @Transactional(propagation = REQUIRES_NEW)
    public void setSent(List < String > ids) {
        dao.setSents(ids);
    }
}

DAO 层:

@Repository
public class YirInfoRepository {

    @Autowired
    EntityManager entityManager;

    //Here you can update by using and IN statement or by doing a cycle
    //Let's suppose a bulk operation
    public void setSents(List < String > ids) {
        String query = "UPDATE_QUERY";
        for (int i = 0; i < ids.size(); i++) {
            String id = ids.get(i);
            Query nativeQuery = entityManager.createNativeQuery(String.format(query, id));
            nativeQuery.executeUpdate();
            if (i % 20 == 0) {
                entityManager.flush();
                entityManager.clear();
            }
        }
    }
}

【讨论】:

  • 您好,有必要拨打entityManager.flush()entityManager.clear() 吗?如果我们不这样做,会发生锁定吗?
  • I%20 表示如果 20 条记录已被处理并准备好更新,则将这些更改“刷新”到数据库。因此,它也会清除这些对象的 java 内存。从这个意义上说,刷新、清除是必要的,因为您实际上是在批处理 20 条记录,以免内存中有数千个对象准备更新,但在发送批处理更新命令之前,您的内存会阻塞。它与锁本身没有任何关系,因为它还没有达到数据库级别。
【解决方案3】:

首先要了解的是,对于第一个示例,您正在使用本机查询来更新数据库中的行。在这种情况下,您完全跳过 Hibernate 为您做任何事情。

在您的第二个示例中,您有同样的事情,您正在通过更新查询进行更新。您不需要刷新实体管理器,因为它仅用于传输对该实体管理器中的实体对象所做的未决更改。

另外,我不知道您的示例是如何工作的,因为您正在自动装配实体管理器而不使用 @PersistenceContext 注释。确保您正确使用这个,因为您可能错误地配置了应用程序。此外,使用 Spring 时也无需手动创建实体管理器,如第二个示例所示。只需使用 @PersistenceContext 在您的应用中获取实体管理器。

您还混淆了事务管理。在第一个示例中,将 @Transactional 注释放在方法或类中就足够了。

对于另一个示例,您正在执行手动事务管理,这在这种情况下毫无意义。如果您使用的是 Spring,则可以简单地依赖声明式事务管理。

我要在这里检查的第一件事是将datasource-proxy 集成到您的连接管理中并注销您的语句是如何执行的。使用此信息,您可以确保查询已发送到 DB 端并且 DB 执行速度非常慢,或者您的应用和 db 之间存在网络问题。

如果您发现查询已正确发送到数据库,您需要分析您的查询,因为很可能它只是执行得很慢并且需要一些优化。为此,您可以使用解释计划功能,了解您的执行计划是什么样子,然后加快执行计划。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-09-28
    • 1970-01-01
    • 2023-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多