简体   繁体   English

Hibernate试图保留错误的对象

[英]Hibernate trying to persist the wrong object

I'm experiencing a problem in one of the unit test classes in my Spring Boot project which I just cannot explain. 我在Spring Boot项目中的一个单元测试类中遇到了一个我无法解释的问题。 Here are my test methods: 这是我的测试方法:

// This test creates a record with callback id 5a6775ab4b0af8693ba97c5b
@Test
public void testCreate() throws Exception {
    Callback callback = fixtures.getCallback(TestFixtureFactory.EMAILS_SUCCESS);
    boolean created = createCallbackDao.createCallback(callback);
    assertThat(created).as("Check create callback succeeded").isTrue();
}

// This test creates a record with callback id 5a6775ab4b0af8693ba97c5x and then tries to create it again,
// which is expected to fail with a unique constraint violation.
@Test
public void testAlreadyExists() throws Exception {
    Callback callbackA = fixtures.getCallback(TestFixtureFactory.EMAILS_DUPLICATE);
    boolean a = createCallbackDao.createCallback(callbackA);
    assertThat(a).as("Check first create callback succeeded").isTrue();
    assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(() -> {
        Callback callbackB = fixtures.getCallback(TestFixtureFactory.EMAILS_DUPLICATE);
        boolean b = createCallbackDao.createCallback(callbackB);
    });
}

What happens is that the testAlreadyExists() test is executed first and passes as expected but the testCreate(), which is executed second, fails with a unique constraint violation. 发生的情况是,首先执行testAlreadyExists()测试并按预期方式通过,但是第二次执行的testCreate()失败,并发生唯一约束冲突。

I have tried running each test separately and both pass, as expected, when doing this. 我试过分别运行每个测试,并且按预期通过两个测试。

Here is the log output from testCreate() when running both tests: 这是运行两个测试时testCreate()的日志输出:

2018-03-19 13:00:21.347  INFO 10646 --- [           main] c.y.p.apicallback.dao.impl.CallbackDao   : Create callback for callback id = 5a6775ab4b0af8693ba97c5b
2018-03-19 13:00:21.347  INFO 10646 --- [           main] c.y.p.apicallback.dao.impl.CallbackDao   : Callback=Callback [id=13, callbackId=5a6775ab4b0af8693ba97c5b, ...]
2018-03-19 13:00:21.349 DEBUG 10646 --- [           main] org.hibernate.SQL                        : insert into api_callback (created, last_modified, callback_id, last_update, message, request_source, state, id) values (?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into api_callback (created, last_modified, callback_id, last_update, message, request_source, state, id) values (?, ?, ?, ?, ?, ?, ?, ?)
2018-03-19 13:00:21.350 TRACE 10646 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [TIMESTAMP] - [Mon Mar 19 13:00:21 GMT 2018]
2018-03-19 13:00:21.350 TRACE 10646 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [2] as [TIMESTAMP] - [2018-03-19 13:00:21.311]
2018-03-19 13:00:21.350 TRACE 10646 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [3] as [VARCHAR] - [5a6775ab4b0af8693ba97c5x]
2018-03-19 13:00:21.350 TRACE 10646 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [4] as [TIMESTAMP] - [Tue Jan 23 17:49:31 GMT 2018]
2018-03-19 13:00:21.350 TRACE 10646 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [5] as [VARCHAR] - [Send email request completed.]
2018-03-19 13:00:21.350 TRACE 10646 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [8] as [BIGINT] - [7]
2018-03-19 13:00:21.350  WARN 10646 --- [           main] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 23505, SQLState: 23505
2018-03-19 13:00:21.351 ERROR 10646 --- [           main] o.h.engine.jdbc.spi.SqlExceptionHelper   : Unique index or primary key violation: "API_CALLBACK_U1_INDEX_4 ON API_CALLBACK(CALLBACK_ID) VALUES ('5a6775ab4b0af8693ba97c5x', 1)"; SQL statement:
insert into api_callback (created, last_modified, callback_id, last_update, message, request_source, state, id) values (?, ?, ?, ?, ?, ?, ?, ?) [23505-196]
2018-03-19 13:00:21.351  INFO 10646 --- [           main] o.h.e.j.b.internal.AbstractBatchImpl     : HHH000010: On release of batch it still contained JDBC statements
2018-03-19 13:00:21.351 ERROR 10646 --- [           main] c.y.p.apicallback.dao.impl.CallbackDao   : Error creating Callback, time(ms)=4!

org.hibernate.exception.ConstraintViolationException: could not execute statement
    at   org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:112) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]

Here is the code for the DAO method being called: 这是被调用的DAO方法的代码:

@Override
public boolean createCallback(Callback callback) throws Exception {
    LOG.info("Create callback for callback id = {}", callback.getCallbackId());
    Stopwatch timer = Stopwatch.createStarted();
    Session session = null;
    Transaction txn = null;
    try {
        session = sessionFactory.getCurrentSession();
        txn = session.getTransaction();
        LOG.info("Callback={}", callback);
        session.persist(callback);
        session.flush();
        txn.commit();
        LOG.info("CallbackDao.createCallback: company={}, callbackId={}, id={}, time(ms)={}",
                callback.getRequest().getUser(), callback.getCallbackId(), callback.getId(), timer.elapsed(TimeUnit.MILLISECONDS));
        return true;
    } catch (Exception e) {
        LOG.error(String.format("Error creating Callback, time(ms)=%d!", timer.elapsed(TimeUnit.MILLISECONDS)), e);
        if (txn.getStatus() == TransactionStatus.ACTIVE || txn.getStatus() == TransactionStatus.MARKED_ROLLBACK) {
            txn.rollback();
        }
        throw e;
    }
}

The API_CALLBACK table has a generated primary key value (ID) and a unique index on the CALLBACK_ID column. API_CALLBACK表具有一个生成的主键值(ID)和CALLBACK_ID列上的唯一索引。 The ID value is derived from a database sequence prior to calling the DAO method to persist the record. ID值是在调用DAO方法以保留记录之前从数据库序列派生的。

The problem is that the callback record that should be inserted into the database is the one that is logged as follows: 问题在于应插入数据库的回调记录是如下记录的:

2018-03-19 13:00:21.347  INFO 10646 --- [           main] c.y.p.apicallback.dao.impl.CallbackDao   : Callback=Callback [id=13, callbackId=5a6775ab4b0af8693ba97c5b, ...]

However, according to the bind variables logged by Hibernate it is in fact trying to insert a record with different id and callback id values: 但是,根据Hibernate记录的绑定变量,实际上是在尝试插入具有不同id和callback id值的记录:

2018-03-19 13:00:21.350 TRACE 10646 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [3] as [VARCHAR] - [5a6775ab4b0af8693ba97c5x] (should be 5a6775ab4b0af8693ba97c5b)
2018-03-19 13:00:21.350 TRACE 10646 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [8] as [BIGINT] - [7] (should be 13) 

The record with ID 7 and Callback Id 5a6775ab4b0af8693ba97c5x was inserted into the local (H2) database during the testAlreadyExists() test. 在testAlreadyExists()测试期间,将ID为7和Callback ID为5a6775ab4b0af8693ba97c5x的记录插入到本地(H2)数据库中。

I can't figure out why the DAO class is showing that it is working with one object instance but Hibernate is trying to persist a different one. 我不知道为什么DAO类显示它正在处理一个对象实例,但是Hibernate试图持久化另一个对象实例。 Can anyone help? 有人可以帮忙吗?

With thanks to kirinya (see comment above) the solution is to make the test class transactional using the annotation javax.transaction.Transactional . 感谢kirinya (请参见上面的注释),解决方案是使用注释javax.transaction.Transactional使测试类具有事务性。

@RunWith(SpringRunner.class)
@SpringBootTest(classes = { TestDatabaseConfiguration.class })
@Transactional
public class TestCreateCallbackDao {
...
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM