【问题标题】:Hibernate StatelessSession and strange behavior on Oracle and SQL Server休眠 StatelessSession 和 Oracle 和 SQL Server 上的奇怪行为
【发布时间】:2014-11-30 11:12:21
【问题描述】:

我正在使用 Hibernate 的 StatelessSession 批量插入和更新行。不确定jdbc.batch_size 是否与问题有关,但无论如何在我的应用程序中,我根本没有配置hibernate.jdbc.batch_size 参数。

我向会话提供一个连接对象,该对象已经线程绑定到现有事务(由 Spring 管理)。这是通过 Spring 的DataSourceUtils 完成的。出于这个原因,当我处理完数据后,我不会关闭无状态会话,因为在事务完成之前,我仍然需要为其他一些事情打开连接。

基本上是这样的:

statelessSession = sessionFactory.openStatelessSession(DataSourceUtils.getConnection(...));
// repeat many times
try {
    statelessSession.insert(entity);
}
catch (ConstraintViolationException e) {
    statelessSession.update(entity);
}
// some more actions
// close transaction

在 SQL Server 上,一切正常。每个 session.insert(...) 调用都会运行 SQL,如果我尝试插入现有行,可能会在唯一约束上失败。当我最终提交事务时,一切都按我的预期进行。

但是,在 Oracle 上,什么也没有发生。 Hibernate 日志打印 SQL,但它根本不运行。此外,插入不会在应该失败的时候失败。

调试Hibernate代码后,发现与JDBC批处理行为有关。我决定使用managedFlush() 方法显式刷新statelessSession。即便如此,我发现我必须在每个命令之后刷新它,否则它不会表现出我想要的快速失败行为。

statelessSession = sessionFactory.openStatelessSession(DataSourceUtils.getConnection(...));
// repeat many times
try {
    statelessSession.insert(entity);
    ((Context) session).managedFlush()
}
catch (ConstraintViolationException e) {
    statelessSession.update(entity);
    ((Context) session).managedFlush()
}
// some more actions not related to statelessSession
// close transaction

虽然这可行,但当我分析我的代码时,我发现它现在运行速度较慢。即使在可能与 managedFlush() 方法无关的 SQL Server 上。

知道发生了什么吗?是否有任何隐藏的配置我可以调整以实现所需的行为 - 例如“自动刷新”(但当然没有自动提交)?

【问题讨论】:

    标签: java spring oracle hibernate orm


    【解决方案1】:

    首先,一旦出现异常,您应该关闭会话,因为持久性上下文可能会处于不一致的状态。

    所以你不应该更新任何异常。

    引用Session JavaDoc

    如果Session抛出异常,事务必须滚动 返回并丢弃会话。 Session的内部状态 异常发生后可能与数据库不一致。

    在您的示例中,您可以简单地调用合并,因为它适用于两者 插入/更新:

    statelessSession.merge(entity);
    

    更新

    由于您使用的是 MS SQL,您可能正在使用 IDENTITY 生成器 disables JDBC batching。最新的 MS SQL 版本增加了对 Seqeuneces 的支持,这对批处理更加友好。

    你需要启用Hibernate batching support,比如:

    hibernate.jdbc.batch_size=30
    

    如果不设置该属性,则默认值为0,因此没有默认的JDBC批处理。

    【讨论】:

    • 您引用的 javadocs 是常规会话,而不是无状态会话。我对他们很熟悉,但这里不是这样。谢谢,Yoni
    • 不,没有身份生成器。
    【解决方案2】:

    为什么适用于 SQL Server:
    SQL Server 支持 READ UNCOMMITTED 隔离级别。这意味着另一个事务可以看到对表的未提交更改。

    为什么不在 Oracle 上工作:
    Oracle 不支持 READ UNCOMMITTED,它只有 READ COMMITTED 和 SERIALIZABLE。 因此,来自“Oracle SQL Developer”或“Toad”的另一个事务无法在您的应用程序中看到该事务尚未提交的更改。

    假设“hibernate.jdbc.batch_size=20”,并且您想要进行 100 次插入。您的应用程序将分 5 批发送插页,每批有 20 个插页。另一个“READ UNCOMMITTED”事务将看到每个单独的批次。 “READ COMMITTED”事务将什么都看不到,直到您的应用程序提交产生插入的事务。

    【讨论】:

    • 这不相关。我的用例中只有一笔交易。假设我插入 5,000 条记录。我知道其中一些存在并且会失败,但我希望在运行 SQL 插入命令时立即发生失败,因此我可以跟进更新。
    猜你喜欢
    • 2010-12-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-19
    • 2016-01-03
    相关资源
    最近更新 更多