如何纠正休眠测试

ru5*_*1an 1 junit hibernate

有人可以解释以下内容吗?我有代码:

@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;
    }
}
Run Code Online (Sandbox Code Playgroud)

如果我按原样运行测试,测试将失败并收到错误 java.lang.AssertionError。

我有三个变体来更改 @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());
}
Run Code Online (Sandbox Code Playgroud)

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());
}
Run Code Online (Sandbox Code Playgroud)

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());
}
Run Code Online (Sandbox Code Playgroud)

问题: 1)如果我按原样运行,为什么测试会失败?2)什么变体是正确的?3)如果都没有,如何修正代码?

Sta*_*sev 5

当Hibernate执行SQL时

  • FlushMode 指示 Hibernate 何时生成实际的 SQL 语句。默认值(自动)非常合理,它会尝试尽可能延迟语句。但它会在每个SELECT语句之前刷新(否则你将找不到刚刚持久化的记录)。
  • 当你保存实体时,Hibernate 必须生成一个 ID(结果是save()一个PERSISTED必须有 ID 的实体)。因此,无论您选择哪种 FlushMode,INSERT如果需要生成 ID,ORM 都会发出语句。如果您要使用序列生成器 -INSERT可以推迟,但您使用身份 - 这个不能推迟,因为 ID 是由数据库在INSERT.

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

您将 FlushMode 设置为 COMMIT,这意味着 Hibernate 在事务提交之前执行 SQL。因此,当您更新实体时,不会调用 UPDATE 语句。它只会在您提交事务时才会被调用(您永远不会这样做)。

为什么修复#1“有效”

即使使用 FlushMode COMMIT,新实体的原始 INSERT 语句仍然会执行 - 必须生成 ID。

在您evict()创建实体之后,Hibernate 不再了解它,但它有一个 ID,因此下次您save()Hibernate 就知道它是一个 DETACHED 实体。每次分离实体时都会saved()调用 UPDATE。

为什么修复 #2 有效

好吧,您实际上提交了事务,因此 Hibernate 刷新了所有 SQL,包括 UPDATE 语句。您的 FlushMode=COMMIT 按预期工作。

为什么修复 #3 有效

在这种情况下,您手动flush()进行更改 - 无论您选择哪种刷新模式,它都会执行 SQL 语句。

如何编写 Hibernate 测试

首先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()));
    }
}
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 您可以将flush() 和clear() 组合在一个单独的方法中,这样就不会占用太多空间。
  • assertReflectionEquals()来自 Unitils 库。

您可以在此处(TestNG) 和此处(Spock)找到此类测试的示例。