在 Spring Data JPA Repository 上调用 count() 方法时的 JPA 缓存行为

JPA cache behaviour when invoke count() method on Spring Data JPA Repository

我正在为 Spring Data JPA 存储库编写基于事务的 junit IT 测试。 要检查 table 中的行数,我使用 JDBCTemplate.

我注意到,在事务上下文中调用 org.springframework.data.repository.CrudRepository#save(S) 不会生效。 SQL 未执行插入,table 中的行数未增加。

但是如果我在 save(S) 之后调用 org.springframework.data.repository.CrudRepository#count 则执行 SQL 插入并增加行数。

我想这是 JPA 缓存的行为,但它是如何工作的?

使用 Spring 引导的代码:

@RunWith(SpringRunner.class)
@SpringBootTest
public class ErrorMessageEntityRepositoryTest {

    @Autowired
    private ErrorMessageEntityRepository errorMessageEntityRepository;
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Test
    @Transactional
    public void save() {
        ErrorMessageEntity errorMessageEntity = aDefaultErrorMessageEntity().withUuid(null).build();
        assertTrue(TestTransaction.isActive());
        int sizeBefore= JdbcTestUtils.countRowsInTable(jdbcTemplate, "error_message");
        ErrorMessageEntity saved = errorMessageEntityRepository.save(errorMessageEntity);
        errorMessageEntityRepository.count(); // [!!!!] if comment this line test will fail
        int sizeAfter= JdbcTestUtils.countRowsInTable(jdbcTemplate, "error_message");
        Assert.assertEquals(sizeBefore+1, sizeAfter);
    }

实体:

@Entity(name = "error_message")
public class ErrorMessageEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private UUID uuid;
    @NotNull
    private String details;

存储库:

public interface ErrorMessageEntityRepository extends CrudRepository<ErrorMessageEntity, UUID>

为了让测试数据不污染数据库,在使用Spring-test的单元测试时,默认会回滚事务,即@Rollback默认为true。如果想测试数据而不回滚,可以设置@Rollback(value = false)。如果你使用的是MySQL数据库,设置自动回滚后,如果发现事务还是没有回滚,可以检查数据库引擎是不是 Innodb ,因为其他数据库引擎如 MyISAM 和 Memory 做的不支持交易。

你是对的,这是 JPA 工作方式的结果。 JPA 尝试尽可能延迟 SQL 语句执行。

保存新实例时,这意味着它只会在需要时执行插入以获得实体的 ID。

只有当刷新事件发生时,存储在持久化上下文中的所有更改才会刷新到数据库中。该事件发生的三个触发器:

  1. 持久性上下文的关闭将刷新所有更改。在典型的设置中,这对事务提交很严格。

  2. EntityManager 上显式调用 flush,您可以直接调用或在通过 saveAndFlush

    使用 Spring Data JPA 时调用
  3. 执行查询之前。由于您通常希望在查询中查看更改。

数字 3 是您看到的效果。

请注意,细节有点复杂,因为您可以配置很多这样的东西。 As usual, Vlad Mihalcea has written an excellent post about it.