簡體   English   中英

Hibernate試圖保留錯誤的對象

[英]Hibernate trying to persist the wrong object

我在Spring Boot項目中的一個單元測試類中遇到了一個我無法解釋的問題。 這是我的測試方法:

// 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);
    });
}

發生的情況是,首先執行testAlreadyExists()測試並按預期方式通過,但是第二次執行的testCreate()失敗,並發生唯一約束沖突。

我試過分別運行每個測試,並且按預期通過兩個測試。

這是運行兩個測試時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]

這是被調用的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;
    }
}

API_CALLBACK表具有一個生成的主鍵值(ID)和CALLBACK_ID列上的唯一索引。 ID值是在調用DAO方法以保留記錄之前從數據庫序列派生的。

問題在於應插入數據庫的回調記錄是如下記錄的:

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

但是,根據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) 

在testAlreadyExists()測試期間,將ID為7和Callback ID為5a6775ab4b0af8693ba97c5x的記錄插入到本地(H2)數據庫中。

我不知道為什么DAO類顯示它正在處理一個對象實例,但是Hibernate試圖持久化另一個對象實例。 有人可以幫忙嗎?

感謝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