使用 EclipseLink 处理 Spring 事务

Posted

技术标签:

【中文标题】使用 EclipseLink 处理 Spring 事务【英文标题】:Spring Transaction handling with EclipseLink 【发布时间】:2022-01-20 17:36:40 【问题描述】:

我是数据库和事务的新手。我发现了很多不同的最新信息,目前正在尝试整理我的想法。 关于上下文,我正在尝试使用 SQL Server 测试当前的隔离级别,但没有成功。

为此,我将 Spring Transaction 与 Eclipse Link 一起使用。我找到了一些关于 Spring 事务的信息 (https://www.marcobehler.com/guides/spring-transaction-management-transactional-in-depth)。不过还是有works unit的概念,现在不知道用的是什么(https://wiki.eclipse.org/Introduction_to_EclipseLink_Transactions_(ELUG)#Unit_of_Work_Architecture)

我要测试的内容: 我有一个实体用户(id、firstname、lastname)。我在表 id = 1, firstname = foo lastname = bar 中有一个条目

我有服务和交易。恕我直言,SQL Server 的默认设置是隔离 READ_COMMIT

@Transactional
public User updateUser()
   User updateUser = new User(1, "new firstname", "new lastname");
   User updatedUser = userRepository.save(updateUser); --> em.merge
   return updatedUser;

到目前为止一切顺利。我现在不明白。我在返回中设置了一个断点。 同时我打开了第二个 SQL 客户端并执行了以下 SQL。

SET IMPLICIT_TRANSACTIONS ON 
UPDATE 
    User 
SET 
    lastname = 'complete new', 
WHERE 
    id = 1
COMMIT TRAN 

我期望的是 SQL 语句将等待 Spring 事务完成。 BUT目前不是这样,只是简单的执行了SQL语句。

然后姓在表中姓氏“全新”然后我恢复断点,然后姓氏是“新姓氏”。这种行为我无法理解。这是正常的还是因为eclipse链接的单元工作?

【问题讨论】:

em.flush() 添加到您的存储库save 以查看任何内容。现在的情况是,数据库没有机会获得写锁,因为 JPA 会延迟 SQL 直到事务提交。 Mhm 在事务 spring 之后会执行一个 commit。这是一个弹簧数据存储库,为什么我需要刷新? EclipseLink 缓冲需要尽可能长时间执行的 SQL 语句,以减少 RDBMS 中的锁定时间。在您的特定情况下,当 Spring Data JPA 提交事务时,JDBC 驱动程序将收到 UPDATE 语句。您可以通过在 EclispeLink 中启用 SQL 日志记录来验证它:***.com/q/2374395/17695211 启用 SQL 日志记录后,您会看到断点处的控制台中不会有任何 SQL 调试输出。它会出现在返回之后。如果您真的想看到锁定效果,您需要使用@PersistenceContext-injected EntityManager 编写没有 Spring Data JPA 的存储库,并调用 EntityManager.flush 这会将 EclipseLink 的 SQL 语句缓冲区刷新到 JDBC 驱动程序 在断点之前. 或者(如果您喜欢冒险的话),您可以尝试在 EclipseLink 源代码中寻找执行相应PreparedStatement 的位置,并在其后设置断点。 【参考方案1】:

EclipseLink 缓冲需要尽可能长时间执行的 SQL 语句,以减少 RDBMS 中的锁定时间。在您的特定情况下,当 Spring Data JPA 提交事务时,JDBC 驱动程序将收到 UPDATE 语句。您可以通过在 EclispeLink 中启用 SQL 日志记录来验证它:***.com/q/2374395/17695211

启用 SQL 日志记录后,您会看到控制台中的断点处不会有任何 SQL 调试输出。返回后会出现。如果你真的想看到加锁效果,你需要在没有 Spring Data JPA 的情况下使用注入 @PersistenceContext 的 EntityManager 编写存储库,并调用 EntityManager.flush 将 EclipseLink 的 SQL 语句缓冲区在断点之前刷新到 JDBC 驱动程序。

或者(如果您喜欢冒险),您可以尝试在 EclipseLink 源代码中寻找执行相应 PreparedStatement 的位置,并在其后设置断点。

【讨论】:

然后我在新的一年用 SELECT 测试整个事情,读取事务并执行 em.flush。刷新后我将添加断点。也许当我测试一个脏读案例时会更清楚。 UnitOfWorkImpl 的 commitToDatabase 方法在预提交 JTA 事件以及刷新时被调用,因此可能是从断点开始的好地方。您可能不需要实际的 SQL 语句执行 - 在 Spring 提交事务之后但之前的任何时间都可以执行(所以在 writeAllObjectsWithChangeSet 调用之后)。 谢谢克里斯。尝试这样做,我想测试效果和差异。如果我的读取事务在 READ_COMMITTED_SNAPSHOT 上运行。据我所知,SQL Server 的默认值是 READ_COMMITTED。但是,到目前为止,我还没有注意到任何真正的区别。

以上是关于使用 EclipseLink 处理 Spring 事务的主要内容,如果未能解决你的问题,请参考以下文章

IntegrityException的spring eclipselink问题

JPA 2.1、Eclipselink、SQL Server、Spring - NamedStoredProcedureQuery 结果映射

在 postgres spring-boot + eclipselink 中保存为 JSON 数据类型的替代解决方案

SPRING DATA-JPA + eclipseLink2.0 失败

Oracle 中的 Spring Data JPA + EclipseLink

在 Spring dm Server 1.x 中使用 EclipseLink JPA 时出现 Aspectj 加载时间编织问题