如何纠正休眠测试

问题描述:

有人可以解释以下几点。我有代码:如何纠正休眠测试

@Entity 
public class Model { 

    @Id 
    @GeneratedValue(strategy = AUTO) 
    @Column 
    private long id; 

    @Column(length = 200, nullable = false) 
    private String field0; 

    @Column(length = 200, nullable = false) 
    private String field1; 

    @Column(length = 200, nullable = false) 
    private String field2; 

    @Column(length = 200, nullable = false) 
    private String field3; 

    @Column(length = 200, nullable = false) 
    private String field4; 

    @Column(length = 200, nullable = false) 
    private String field5; 

    @Column(length = 200, nullable = false) 
    private String field6; 

    @Column(length = 200, nullable = false) 
    private String field7; 

    @Column(length = 200, nullable = false) 
    private String field8; 

    @Column(length = 200, nullable = false) 
    private String field9; 

    public long getId() { 
     return id; 
    } 

    public void setId(long id) { 
     this.id = id; 
    } 

    public String getField0() { 
     return field0; 
    } 

    public void setField0(String field0) { 
     this.field0 = field0; 
    } 

    public String getField1() { 
     return field1; 
    } 

    public void setField1(String field1) { 
     this.field1 = field1; 
    } 

    public String getField2() { 
     return field2; 
    } 

    public void setField2(String field2) { 
     this.field2 = field2; 
    } 

    public String getField3() { 
     return field3; 
    } 

    public void setField3(String field3) { 
     this.field3 = field3; 
    } 

    public String getField4() { 
     return field4; 
    } 

    public void setField4(String field4) { 
     this.field4 = field4; 
    } 

    public String getField5() { 
     return field5; 
    } 

    public void setField5(String field5) { 
     this.field5 = field5; 
    } 

    public String getField6() { 
     return field6; 
    } 

    public void setField6(String field6) { 
     this.field6 = field6; 
    } 

    public String getField7() { 
     return field7; 
    } 

    public void setField7(String field7) { 
     this.field7 = field7; 
    } 

    public String getField8() { 
     return field8; 
    } 

    public void setField8(String field8) { 
     this.field8 = field8; 
    } 

    public String getField9() { 
     return field9; 
    } 

    public void setField9(String field9) { 
     this.field9 = field9; 
    } 

    @Override 
    public String toString() { 
     return "Model{" + "id=" + id + ", field0='" + field0 + '\'' 
       + ", field1='" + field1 + '\'' + ", field2='" + field2 + '\'' 
       + ", field3='" + field3 + '\'' + ", field4='" + field4 + '\'' 
       + ", field5='" + field5 + '\'' + ", field6='" + field6 + '\'' 
       + ", field7='" + field7 + '\'' + ", field8='" + field8 + '\'' 
       + ", field9='" + field9 + '\'' + '}'; 
    } 
} 


@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration("/testContext.xml") 
public class MainTest { 

    @Autowired 
    private SessionFactory sessionFactory; 
    private Session session; 
    private Transaction tx; 

    @Before 
    public void before() { 
     session = sessionFactory.openSession(); 
     tx = session.beginTransaction(); 
     session.setFlushMode(FlushMode.COMMIT); 
    } 

    @After 
    public void after() { 
     tx.commit(); 
     session.close(); 
    } 

    @Test 
    public void shouldFindModelByField() { 
     Model model = createRandomModel(); 
     session.save(model); 

     model.setField0("TEST1"); 
     session.save(model); 

     assertTrue(null != session.createSQLQuery(
       "select id from model where field0 = '" + model.getField0() 
         + "'").uniqueResult()); 
    } 

    private Model createRandomModel() { 
     Model ret = new Model(); 
     ret.setField0(RandomStringUtils.randomAlphanumeric(10)); 
     ret.setField1(RandomStringUtils.randomAlphanumeric(10)); 
     ret.setField2(RandomStringUtils.randomAlphanumeric(10)); 
     ret.setField3(RandomStringUtils.randomAlphanumeric(10)); 
     ret.setField4(RandomStringUtils.randomAlphanumeric(10)); 
     ret.setField5(RandomStringUtils.randomAlphanumeric(10)); 
     ret.setField6(RandomStringUtils.randomAlphanumeric(10)); 
     ret.setField7(RandomStringUtils.randomAlphanumeric(10)); 
     ret.setField8(RandomStringUtils.randomAlphanumeric(10)); 
     ret.setField9(RandomStringUtils.randomAlphanumeric(10)); 
     return ret; 
    } 
} 

如果我按原样运行测试,则测试失败,并且出现错误java.lang.AssertionError。

我有三个varints改变@Test方法运行成功测试:

1)

@Test 
public void shouldFindModelByField() { 
    Model model = createRandomModel(); 
    session.save(model); 

    session.evict(model); 

    model.setField0("TEST1"); 
    session.save(model); 

    assertTrue(null != session.createSQLQuery(
      "select id from model where field0 = '" + model.getField0() 
        + "'").uniqueResult()); 
} 

2)

@Test 
public void shouldFindModelByField() { 
    Model model = createRandomModel(); 
    session.save(model); 

    model.setField0("TEST1"); 
    session.save(model); 

    tx.commit(); 
    tx = session.beginTransaction(); 

    assertTrue(null != session.createSQLQuery(
      "select id from model where field0 = '" + model.getField0() 
        + "'").uniqueResult()); 
} 

3)

@Test 
public void shouldFindModelByField() { 
    Model model = createRandomModel(); 
    session.save(model); 

    model.setField0("TEST1"); 
    session.save(model); 

    session.flush(); 

    assertTrue(null != session.createSQLQuery(
      "select id from model where field0 = '" + model.getField0() 
        + "'").uniqueResult()); 
} 

问题: 1)为什么测试失败,如果我按原样运行它? 2)什么样的变体是正确的? 3)如果他们都不是,如何纠正代码?

+0

我可以解释一切,但修复#1 ..你确定你只添加了'evict()'和另一个'save()'这就是它?你可以仔细检查一下吗?另外,你的数据库是什么? –

+0

我刚刚在第一次“保存”后添加了evict。我的db是H2。请参阅xml文件http://prntscr.com/fswg10的屏幕截图。如果你能回答我上面的问题,这将是非常好的。 – ru51an

当Hibernate执行SQL

  • FlushMode决定当Hibernate生成实际的SQL语句。默认(auto)非常合理,它会尽可能地延迟语句。但它会在每个SELECT声明之前刷新(否则,您将无法找到您刚刚保留的记录)。
  • Hibernate在保存时必须为实体生成一个ID(save()的结果是一个必须有ID的PERSISTED实体)。因此,无论您选择哪种FlushMode,ORM都会发出INSERT语句,如果这是生成ID所需的语句。如果您要使用序列发生器 - INSERT可能会被推迟,但是您使用身份标识 - 此标记不能在INSERT期间由数据库生成ID推迟。

为什么原来的代码不起作用

您设置FlushMode承诺,这意味着休眠执行SQL事务提交权利之前。因此,当你更新你的实体时,不会调用UPDATE语句。它只会在您提交事务时才会被调用(您永远不会这样做)。

为什么要修复#1“工程”

你原来的INSERT语句为新的实体仍然执行,即使FlushMode提交 - 该ID必须生成,对不对?

之后evict()实体Hibernate不知道它了。当你save()再一次Hibernate认为这是一个全新的对象,所以它再次插入它,因为它需要生成新的ID。

因此,不是更新您的原始实体,而是实际插入一个新行,并最终以DB中的2个对象结束。您的测试通过,因为它选择第二个(字段已设置为正确的值)。

为什么要修复#2作品

嘛,所以休眠刷新所有SQL后,包括UPDATE语句,你居然提交事务。您的FlushMode = COMMIT按预期工作。

为什么要修复#3作品

在这种情况下,您手动flush()的变化 - 它会执行SQL语句,无论你选择哪一种刷新模式。

如何编写休眠测试

首先,SpringJUnit4ClassRunner支持在测试@Transactional注解。因此,您可以使用批注,而不是在@Before & @After中手动处理交易。

其次,要确保测试真正起作用,您需要手动刷新并清除第一级缓存。否则,您冒险使用缓存实体而不是实际的数据库。所以,你的测试可以是这样的:

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration("/testContext.xml") 
@Transactional 
public class MainTest { 
    ... 
    @Test public void canUpdateAllTheFields() { 
     Model original = createRandomModel(); 
     session.save(original); 
     session.flush(); 
     session.clear(); 

     Model updates = createRandomModel(); 
     model.setId(original.getId()); 
     session.update(updates); 
     session.flush(); 
     session.clear(); 

     assertReflectionEquals(updates, session.get(Model.class, original.getId())); 
    } 
} 

注:

  • 您可以结合的flush()和清晰()在一个单独的方法,这样它并不需要那么多的空间。
  • assertReflectionEquals()来自Unitils lib。

你可以找到这样的测试here(TestNG的)和here(斯波克)的例子。

+0

感谢您的详细解答。您能否介绍一下如何使用交易注释代替注释之前和之后 – ru51an

+0

更新了答案 –