【问题标题】:HibernateTransactionManager @Transactional(propagation=REQUIRES_NEW) cannot open 2 sessionsHibernateTransactionManager @Transactional(propagation=REQUIRES_NEW) 无法打开 2 个会话
【发布时间】:2017-09-22 10:56:02
【问题描述】:

有一个批处理作业如下所示:

@Transactional
public void myBatchJob() {
    // retrieves thousands of entries and locks them 
    // to prevent other jobs from touthing this dataset
    entries = getEntriesToProcessWithLock(); 
    additional = doPrepWork(); // interacts with DB
    processor = applicationContext.getBean(getClass());
    while (!entries.isEmpty()) {
        result = doActualProcessing(entries, additional); // takes as many entries as it needs; removes them from collection afterwards
        resultDao.save(result);
    }
}

但是,如果 entries 集合足够大,我偶尔会收到以下错误。

ORA-01000: 超出最大打开游标数

我决定将责任归咎于 doActualProcessing()save() 方法,因为它们最终可能会在一次事务中创建数百个 blob。

显而易见的出路似乎是将处理拆分为多个事务:一个用于获取和锁定条目,另一个用于处理和持久化其他事务。像这样:

@Transactional
public void myBatchJob() {
    // retrieves thousands of entries and locks them 
    // to prevent other jobs from touthing this dataset
    entries = getEntriesToProcessWithLock(); 
    additional = doPrepWork(); // interacts with DB
    processor = applicationContext.getBean(getClass());
    while (!entries.isEmpty()) {
        processor.doProcess(entries, additional);
    }
}

@Transactional(propagation=REQUIRES_NEW)
public void doProcess(entries, additional) {
    result = doActualProcessing(entries, additional); // takes as many entries as it needs; removes them from collection afterwards
    resultDao.save(result);
}

现在每当doProcess 被调用时,我都会得到:

原因:org.hibernate.HibernateException:非法尝试将代理与两个打开的会话关联

如何让HibernateTransactionManager 执行REQUIRES_NEW javadoc 建议的操作:暂停当前事务并开始新事务?

【问题讨论】:

    标签: java spring hibernate jpa transactional


    【解决方案1】:

    在我看来,问题在于您已检索到顶部事务中的实体,并且虽然它们仍与该事务相关联,但您尝试将它们(代理)传递给将在单独事务中处理的方法。

    我认为您可以尝试两种选择:

    1) 在调用processor.doProcess(entries, additional);之前分离实体:

    session.evict(entity); // loop through the list and do this
    

    然后内部事务尝试合并:

    session.merge(entity);
    

    2) 第二种选择是检索 ID 而不是 getEntriesToProcessWithLock 中的实体。然后,您将传递不会导致代理问题的普通原始字段。然后,您将在内部事务中检索适当的实体。

    【讨论】:

    • 这是有道理的。会试一试。所有的手指蚂蚁脚趾交叉! :) 希望(1)不会弄乱数据库锁......
    猜你喜欢
    • 2020-06-12
    • 2017-11-18
    • 2020-02-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多