【问题标题】:Unit Testing Hibernate's Optimistic Locking (within Spring)单元测试 Hibernate Optimistic Locking (with Spring)
【发布时间】:2010-03-16 15:43:52
【问题描述】:

我想编写一个单元测试来验证乐观锁定是否已正确设置(使用 Spring 和 Hibernate)。

我想让测试类扩展 Spring 的 AbstractTransactionalJUnit4SpringContextTests

我最终想要的是这样的方法:


@Test (expected = StaleObjectStateException.class) 
public void testOptimisticLocking() {
    A a = getCurrentSession().load(A.class, 1);
    a.setVersion(a.getVersion()-1);
    getCurrentSession().saveOrUpdate(a);
    getCurrentSession().flush();           
    fail("Optimistic locking does not work");
}

此测试失败。您推荐的最佳做法是什么?

我尝试这样做的原因是我想将version 传输到客户端(使用 DTO)。我想证明,当 DTO 被发送回服务器并与新加载的实体合并时,如果同时其他人更新了该实体,则保存该实体将失败。

【问题讨论】:

    标签: hibernate spring testing


    【解决方案1】:

    似乎无法将所有字段从 DTO(包括版本)传输到新加载的实体,尝试保存它,并在 DTO 被修改时获取异常以防实体被修改正在客户端修改。

    原因是 Hibernate 根本不关心你对版本字段做了什么,因为你在同一个会话中工作。版本字段的值被会话记住。

    一个简单的证明:

    
    @Test (expected = StaleObjectStateException.class) 
    public void testOptimisticLocking() {
        A a = getCurrentSession().load(A.class, 1);
        getCurrentSession().evict(a); //comment this out and the test fails
        a.setVersion(a.getVersion()-1);
        getCurrentSession().saveOrUpdate(a);
        getCurrentSession().flush();           
        fail("Optimistic locking does not work");
    }
    

    感谢大家的帮助!

    【讨论】:

    • 是的,这真的很愚蠢。将每次触发乐观锁的程序复制到测试中是行不通的。它不会抛出异常,而是持续存在。
    【解决方案2】:

    你在这里缺少的一点是

    A a1 = getCurrentSession().load(A.class, 1);
    A a2 = getCurrentSession().load(A.class, 1);    
    
    System.out.println("the following is true!! " + a1 == a2) ;
    

    Hibernate 将为同一会话返回同一类/ID 的同一实例。

    为了测试乐观锁定,请尝试以下操作:

    A a1 = getCurrentSession().load(A.class, 1);
    
    // update the version number in the db using some sql.
    runSql("update A set version = version + 1 where id = 1");
    
    // change object
    a1.setField1("Thing");
    
    getCurrentSession().flush(); // bang should get exception here
    

    【讨论】:

    • 感谢您的回答 (+1),这将测试乐观锁定。我将尝试更新问题以充分反映我想要实现的目标。
    • 没问题...一旦我不工作了,我会晚一点...今天已经在 SO 上花费了太多时间 :)
    【解决方案3】:

    使用AbstractTransactionalDataSourceSpringContextTests,您可以这样做(代码取自this thread):

    public void testStatelessDetectedOnObjectWithOptimisticLocking () {
        long id = 1l;
        CoffeeMachine cm1 = (CoffeeMachine) hibernateTemplate.get(CoffeeMachine.class, id);
        Session firstSession = hibernateTemplate.getSessionFactory().getCurrentSession();
    
        endTransaction();
    
        // Change outside session
        cm1.setManufacturerName("And now for something completely different");
    
        startNewTransaction();
        Session secondSession = hibernateTemplate.getSessionFactory().getCurrentSe ssion();
        assertNotSame(firstSession, secondSession);
    
        CoffeeMachine cm2 = (CoffeeMachine) hibernateTemplate.get(CoffeeMachine.class, id);
        cm2.setManufacturerName("Ha ha! Changed by someone else first. Beat you!");
    
        hibernateTemplate.flush();
    
        try {
            hibernateTemplate.merge(cm1);
            fail("Stateless should be detected");
        }
        catch (OptimisticLockingFailureException ex) {
        // OK
        }
    }
    

    注意 startNewTransaction() 的使用(这是这里的关键)。

    【讨论】:

    • 谢谢,我已经看到了,但是 AbstractTransactionalJUnit4SpringContextTests 没有事务方法...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-09
    • 1970-01-01
    • 1970-01-01
    • 2011-11-17
    • 2019-03-30
    • 1970-01-01
    相关资源
    最近更新 更多