【问题标题】:Spring Boot: H2 Not Actually Retrieving From DatabaseSpring Boot:H2 实际上并未从数据库中检索
【发布时间】:2020-08-11 07:18:54
【问题描述】:

我正在尝试在 Spring Boot 中编写一个简单的存储库测试。测试代码如下所示:

public class UserRepositoryTest {
    private final TestEntityManager entityManager;
    private final UserRepository userRepository;

    @Autowired
    public UserRepositoryTest(TestEntityManager entityManager, UserRepository userRepository) {
        this.entityManager = entityManager;
        this.userRepository = userRepository;
    }

    @Test
    public void test() {
        String firstName = "Frank";
        String lastName = "Sample";
        String email = "frank@example.com";
        String username = "frank@example.com";
        String password = "floople";
        String passwordConfirm = "floople";

        RegisterUserRequest registerUserRequest = new RegisterUserRequest(firstName, lastName, email, username, password, passwordConfirm);
        User user = new User(registerUserRequest);
        user.setSpinsRemaining(0);
        userRepository.save(user);
        userRepository.setSpinsRemainingToTen();
        User found = userRepository.findByUsername(username);
        assertThat(found.getSpinsRemaining()).isEqualTo(10);
    }

我期望发生的事情是新的用户对象被持久化到数据库中,数据库中的行被修改为将 spinsRemaining 设置为 10,然后从 H2 中检索现在修改的行并推入一个新变量命名为“找到”。 “found”变量将指向一个用户对象的实例,该对象还剩下十个旋转。

实际发生的是,“found”变量指向与“user”变量完全相同的 User 实例。事实上,如果我在将“用户”变量持久化到 H2 之后修改了“用户”变量的某些属性,则生成的“找到”对象也具有修改后的属性。根据 IntelliJ,“user”和“found”都指向同一个东西。这怎么可能?

【问题讨论】:

    标签: spring hibernate testing h2


    【解决方案1】:

    Hibernate 在内存中的事务中缓存实体 ("first level cache")。 - 每次从数据库中检索实体时(或者当实体 ID 要求它这样做时),它都会首先在缓存中查找它,这样您就不会拥有具有相同 ID 的一个实体的多个实例。

    但在测试中,sometimes useful 拥有一个“新鲜”实体,因为它可以发现持久性配置/代码中的错误。你需要做什么:

    • 调用EntityManager#flush - 这将强制将您的更改同步到数据库(save 方法不保证在事务中调用时)。
    • 调用EntityManager#clear - Hibernate 将忘记以前的实体实例,并重新开始从数据库中获取。

    或者:您也可以将您的 Spring 存储库方法指示为 clear entities automatically after a modifying query。 - 但这将清除所有实体实例,而不仅仅是您正在修改的实体实例,因此在您的应用程序代码中可能不需要。

    【讨论】:

    • 调用 EntityManager#clear 效果很好。我是否还需要在我定义的存储库中进行刷新和清除,或者这是我只需要在这些测试中做的事情,因为我直接与实体管理器交互?另外,我花了几个小时研究试图找到这个答案。用什么神奇的谷歌搜索词组可以很好地找到它?如果我不知道身份映射模式,有没有办法自己找到这个答案?
    • 您通常不需要在代码中调用 flushclear - 与 DB 同步在事务结束时自动发生。但您需要注意,修改查询不会影响内存中保存的实体。测试也有点具体,因为您想强制更新/插入并选择要完成的查询。
    • 对于搜索词组 - 不幸的是,我不知道如何很好地使用谷歌搜索。 .o) 我建议检查 Hibernate 如何处理实体 - 例如:baeldung.com/hibernate-entity-lifecycle 和 Hibernate 文档也很好(尽管很长)阅读。来自 Hibernate 文档:“Hibernate Session 充当事务范围的缓存,提供可重复的读取以通过标识符查找和查询,从而导致加载实体。” (编辑:这实际上是比 IM 更好的解释——我可能会稍微编辑一下答案,以便更准确。)
    猜你喜欢
    • 2015-05-09
    • 2018-12-28
    • 2017-08-03
    • 1970-01-01
    • 1970-01-01
    • 2017-08-02
    • 2018-12-22
    • 2019-04-25
    • 1970-01-01
    相关资源
    最近更新 更多