【问题标题】:How to test EntityManager.persist() invocation如何测试 EntityManager.persist() 调用
【发布时间】:2013-10-22 10:19:38
【问题描述】:

我在 Spring Container 中做了一个简单的测试:

public class JpaCategoryRepositoryTest extends AbstractJpaJavaTestBase {

    @Inject
    private CategoryService categoryService;

    @Test
    public void testStoreCategory(){
        final Category category = new CategoryBuilder()
                .name("Test category")
                .build();
        assertEquals("Cateogory ID is not assigned", 0L, category.getId());
        categoryService.storeCategory(category);
        assertNotEquals("Category ID is persistent", 0L, category.getId());
    }
}

assertNotEquals 失败。我认为该事务尚未提交。好的,我已经更新了添加事务管理的测试:

public class JpaCategoryRepositoryTest extends AbstractJpaJavaTestBase {

    @Inject
    private CategoryService categoryService;

    @Inject
    TransactionTemplate transactionTemplate;

    @Test
    public void testStoreCategory(){
        final Category category = new CategoryBuilder()
                .name("Test category")
                .build();
        assertEquals("Cateogory ID is not assigned", 0L, category.getId());
        transactionTemplate.execute(new TransactionCallback<Void>() {
            @Override
            public Void doInTransaction(TransactionStatus status) {
                categoryService.storeCategory(category);
                return null;
            }
        });
        assertNotEquals("Category ID is persistent", 0L, category.getId());
    }
}

但这并没有帮助。 在集成测试期间测试实体是否已保存的最佳模式是什么?实际实体被保存,当我在测试失败后检查表时。

【问题讨论】:

    标签: java spring configuration transactions spring-transactions


    【解决方案1】:

    在 JPA 规范中,何时设置实体的 ID 由 JPA 实现决定。但是,必须在将实体写入数据库时​​设置它。您可以通过调用 entityManager.flush() 来强制执行此操作。因此,将 entityManager 添加到您的测试中:

    @PersistenceContext
    private EntityManager entityManager;
    

    存储实体后调用flush():

    categoryService.storeCategory(category);
    entityManager.flush();
    

    应该修复您的测试。

    另请参阅:When does the JPA set a @GeneratedValue @Id

    【讨论】:

    • Flushing 表示对数据库运行 sql 查询。如果我不了解正确性,那么当我明确使用 TransactionTemplate 时也会存档。所以我看不出有什么区别。无论如何,我尝试了您的解决方案,但出现了异常:“原因:org.springframework.transaction.InvalidIsolationLevelException:标准 JPA 不支持自定义隔离级别 - 为您的 JPA 实现使用特殊的 JpaDialect”
    • 在实体中获取 ID 的唯一方法是从数据库中获取 ID。因此,如果您想验证是否设置了 ID,则需要对数据库(也可以是内存数据库)运行查询。当您在配置中更改隔离级别时,该异常是一个已知问题,特别是在单元测试中:jira.springsource.org/browse/SPR-5012
    • 看来你不明白你在说什么。首先你建议冲洗它。然后您说获取 ID 的唯一方法是运行查询。接下来你给我一个异常解决方案的链接,但我没有为每个事务设置特定的隔离级别。
    • 我的意思是,通过刷新,您强制尚未与数据库通信的(更新)查询将通信到数据库。这将导致设置实体的 ID 字段。但是,您仍然可以选择回滚或提交事务。我不清楚隔离级别异常。这可能是由您用于单元测试的 Spring 或 persistence.xml 配置引起的。
    【解决方案2】:

    目前我找不到解决方案来获取刚刚被持久化的实体的 ID。它可能取决于 id 生成策略、jpa 提供程序等。

    为了测试实体是否被持久化,我在测试之前和事务提交之后检查数字或记录。测试现在看起来像:

    @Inject
    private CategoryService categoryService;
    
    @PersistenceContext
    private EntityManager entityManager;
    
    @Inject
    TransactionTemplate transactionTemplate;
    
    @Test
    public void testStoreCategory(){
        final Category category = new CategoryBuilder()
                .name("Test category")
                .build();
        assertEquals("The number of test categories loaded during initial setup is incorrect",
                1, entityManager.createQuery("SELECT c FROM Category c").getResultList().size());
        assertEquals("Cateogory ID is not assigned", 0L, category.getId());
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                categoryService.storeCategory(category);
            }
        });
        assertEquals("The test category has not been persisted",
                2, entityManager.createQuery("SELECT c FROM Category c").getResultList().size());
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多