如何纠正休眠测试
有人可以解释以下几点。我有代码:如何纠正休眠测试
@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)如果他们都不是,如何纠正代码?
当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。
感谢您的详细解答。您能否介绍一下如何使用交易注释代替注释之前和之后 – ru51an
更新了答案 –
我可以解释一切,但修复#1 ..你确定你只添加了'evict()'和另一个'save()'这就是它?你可以仔细检查一下吗?另外,你的数据库是什么? –
我刚刚在第一次“保存”后添加了evict。我的db是H2。请参阅xml文件http://prntscr.com/fswg10的屏幕截图。如果你能回答我上面的问题,这将是非常好的。 – ru51an