【问题标题】:Hibernate database integrity with multiple java applications多个 java 应用程序的休眠数据库完整性
【发布时间】:2025-12-12 23:25:01
【问题描述】:

我们有 2 个 Java Web 应用程序都是读/写和 3 个独立的 Java 读/写应用程序(一个通过电子邮件加载问题,一个处理 xml 提要,一个向订阅者发送电子邮件)都使用休眠并共享一个通用代码库.

我们最近遇到的问题是,通过电子邮件加载的问题有时会覆盖在其中一个网络应用程序中创建的问题。请注意,这些是单独的问题,应该有单独的 ID。我们最初认为这是一个缓存问题。我们已经尝试关闭二级缓存,但这并没有什么不同。

<property name="hibernate.transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</property>
<property name="hibernate.current_session_context_class">thread</property>
<property name="hibernate.cache.use_second_level_cache">false</property>

问题:

@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", unique = true, nullable = false)
@DocumentId
public Integer getId() {
    return this.id;
}

顺便说一句,我们正在使用 MySQL。

CREATE TABLE  `question` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  ...
  PRIMARY KEY (`id`),
  ...
) ENGINE=InnoDB DEFAULT CHARSET=utf8

我们没有明确地打开和关闭会话,而是让 hibernate 通过Util.getSessionFactory().getCurrentSession() 管理它们。

我们不希望在此阶段设置集群二级缓存,因为这会增加另一层复杂性,而且我们对从应用程序整体获得的性能水平非常满意。

那么在 Web 应用程序中实现 open-session-in-view 模式并在独立应用程序中手动管理会话听起来会解决这个问题吗?

或者有什么其他的建议/想法吗?

【问题讨论】:

  • 问题是如何识别的(还有什么是主键)是邮件和网络对同一问题的更新?
  • 听起来主键是由应用程序生成的,而不是使用数据库处理的序列。
  • 嗨,刚刚更新了我的问题,为您提供了更多信息。还以为是IDENTITY策略导致了mysql生成的id?

标签: java hibernate caching second-level-cache


【解决方案1】:

由于所有问题都有 id,所以我假设所有问题都是从您的 MySql 数据库中获取的。

假设您不需要将问题作为透明对象存储在内存中,而是每次提出问题时选择所有问题,我有一个简单的建议。

用数据库中的序列替换 ID 生成器。 (最终将 ID 作为 MySql 中的自动编号)。然后数据库而不是应用程序保证每个问题都有一个唯一的 id。

此解决方案非常简单,并降低了您的复杂性。只有当您将来自不同来源的所有传入问题保存到您的数据库中,然后从此处选择它们时,它才有效。

如果此解决方案给您带来性能问题,您应该进一步了解 Hibernate id 生成器的工作原理。 Hibernate 为不同的场景提供了几种不同的生成器。

希望对您有所帮助!

【讨论】:

  • 我已经为问题添加了我们的架构。那么您是建议我们删除 @GeneratedValue(strategy = IDENTITY) 注释吗?
  • 是的,我愿意。如果多个应用程序将值插入到同一个表中,那么表上的插入触发器是最防弹的解决方案。此触发器应设置序列中的 id。
  • 如果我删除@GeneratedValue 注释,我得到:org.hibernate.id.IdentifierGenerationException:必须在调用 save() 之前手动分配此类的 id:您是否建议我手动找出 id直接从数据库中,然后手动分配给新问题?
  • 据我了解,GenerationType.IDENTITY 可以与 MySQL 自动增量一起正常工作。见:*.com/questions/582526/…
  • 您可以在构造函数中将 id 字段设置为例如 -1 或作为字段的默认值。然后你会很容易看到你的数据库触发器是否有效。
【解决方案2】:

原来这个问题根本与 Hibernate 无关。

登台服务器上的一个数据库表充满了本应清理的旧数据。这最初给出了 id 被覆盖的外观,但进一步调查证明并非如此!

一旦我们删除了不可靠的数据,一切都很好。

【讨论】: