繁体   English   中英

如何协调J2EE和Java EE数据库访问?

[英]How to coordinate J2EE and Java EE database access?

我们有一个巨大的应用程序,它始于十年前,并且仍在积极开发中。 所以有些部分仍然在J2EE 1.4体系结构中,其他部分则使用Java EE 5/6。

在测试一些新代码时,我意识到通过新旧代码部分进入的信息之间存在数据不一致,旧代码直接使用Hibernate会话而新代码注入了EntityManager。 这导致了一个问题,即一部分无法从另一部分看到新数据,因此也创建了数据库记录,导致主键约束违规。

计划完全迁移旧代码以摆脱J2EE,但与此同时 - 我可以做些什么来协调这两个部分之间的数据库访问? 并且不应该在应用服务器中的某个时刻在Hibernate层中两种方式结合在一起,无论是通过JPA还是直接访问?

您可以在同一个应用程序中混合使用Hibernate Session和Entity Manager,没有任何问题。 EntityManagerImpl只是委托调用私有的SessionImpl实例。

您描述的是事务配置异常。 每个数据库事务都是独立运行的(除非你使用REAN_UNCOMMITED,我猜它不是这种情况),但是一旦你提交它,就可以从任何其他事务或连接中获得更改。 因此,一旦事务提交,您应该看到任何其他Hibernate Session,JDBC连接甚至数据库UI管理器工具的更改。

你说有一个主要的冲突。 如果您使用Hibernate标识或序列生成器,则不会发生这种情况。 对于旧的hi-lo生成器 ,如果外部连接尝试在同一个表中插入记录,则可能会出现问题.Hibernate使用旧的hi / lo标识符生成器。

如果存在主/主复制异常,也会发生此问题。 如果您有多个节点且没有严格的一致性复制,则最终可能会违反主键约束。

更新

解决方案1:

在协调尝试插入同一实体的新代码和旧代码时,可以在SERIALIZABLE事务中运行一个slect-than-insert逻辑。 SERIALIZABLE事务代表游览获取适当的锁,因此您仍然可以具有默认的READ_COMMITTED隔离级别,而只有有问题的服务方法被标记为SERIALIZABLE。

因此旧代码和新代码都有这个逻辑运行一个select来检查是否已经有一行满足select约束,只有在没有找到任何内容时插入它。 SERIALIZABLE隔离级别可防止幻像读取,因此我认为它应该可以防止违反约束。

解决方案2:

如果您打开将此任务委派给JDBC,那么您可能还会调查MERGE SQL语句 (如果您当前的数据库支持它)。 基本上,这是一个在幕后发布更新或插入的upsert操作。 这个命令更有吸引力,因为你甚至可以在READ_COMMITTED上运行它。 唯一的缺点是你不能使用Hibernate,只有一些数据库支持它。

如果为旧代码单独实例化SessionFactory ,为新代码分别实例化EntityManagerFactory ,则可能导致第一级缓存中的值不同。 如果在单个Http请求期间,您更改了旧代码中的值,但未立即提交,则会在会话高速缓存中更改该值,但在提交之前它将不可用于新代码。 独立于保护持久值的任何事务或数据库锁定,两个不同的Hibernate会话的混合可以为内存值提供奇怪的东西。

我承认注入的EntityManager仍然使用Hibernate。 恕我直言,最强大的解决方案是获取PersistenceUnitEntityManagerFactory并将其转换为Hibernate EntityManagerFactoryImpl 然后你可以直接访问底层的SessionFactory

SessionFactory sessionFactory = entityManagerFactory.getSessionFactory();

然后,您可以在旧代码中安全地使用此SessionFactory ,因为它现在在您的应用程序中是唯一的,并在新旧代码之间共享。

您仍然必须处理会话创建 - 关闭和事务管理的问题。 我想它已经在旧代码中实现了。 在不知情的情况下,我认为你应该把它移植到JPA,因为我很确定如果一个EntityManager存在, sessionFactory.getCurrentSession()将给它的底层Session但我不能肯定相反的东西。

当我有一个列举的枚举查找值时,我遇到了类似的问题,其中两段代码将检查列表中是否存在给定值,如果它不存在,代码将创建一个新条目在数据库中。 当他们两个都遇到相同的不存在的值时,他们都会尝试创建一个新的值,并且会将其事务回滚(抛弃我们在事务中完成的一堆其他工作)。

我们的解决方案是在一个立即提交的单独事务中创建这些查找值; 如果该事务成功,那么我们知道我们可以使用该对象,如果它失败了,那么我们就知道我们只需要执行get来检索另一个进程保存的那个。 一旦我们知道了一个我们知道在我们的会话中可以安全使用的查找对象,我们就可以愉快地完成剩余的数据库修改而不会有回滚事务的风险。

从您的描述中很难知道您的数据模型是否会适用于类似的方法,您至少会立即提交实体的初始版本,然后一旦确定您正在使用持久对象您可以执行其他您知道需要执行的数据库修改。 但是如果你能找到一种方法来完成这项工作,就可以避免在不同的代码片段之间共享Session(即使新旧代码在不同的JVM中运行也能工作)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM