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

时间:2014-06-30 07:55:20

标签: java database hibernate java-ee jpa

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

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

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

3 个答案:

答案 0 :(得分:3)

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

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

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

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

<强>更新

解决方案1:

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

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

解决方案2:

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

答案 1 :(得分:2)

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

我承认注入的EntityManager仍然使用Hibernate。恕我直言,最强大的解决方案是获取EntityManagerFactory的{​​{1}}并将其转换为Hibernate PersistenceUnit。然后,您可以直接访问基础EntityManagerFactoryImpl

SessionFactory

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

您仍然需要处理会话创建 - 关闭和事务管理的问题。我想它已经在旧代码中实现了。在不知情的情况下,我认为您应该将其移植到JPA,因为我非常确定如果存在SessionFactoryEntityManager会给出其基础sessionFactory.getCurrentSession(),但我无法确认任何内容对面。

答案 2 :(得分:1)

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

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

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