Я использую Spring Data JPA, выполняю следующие результаты теста в нелогичном поведении
@Test
public void testAsync() throws ExecutionException, InterruptedException {
Job job = jobRepository.save(new Job());
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
long origJobID = job.getId();
executor.initialize();
Future<?> wait = executor.submit(() -> {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
Job outcome = jobRepository.save(job.setStopTime(Instant.now()));
// this assertion fails, Hibernate requested a new ID and persisted a new entity ... even though I am reusing the same instance with an ID already populated
assertEquals(origJobID, outcome.getId().longValue());
});
wait.get();
}
Поскольку jobRepository
предоставляет только интерфейс save()
, как пользователь I службы я могу вызывать этот метод только для ВСТАВЛЕНИЯ или ОБНОВЛЕНИЯ моей сущности ... как это возможно, что управляющий сущностью просто игнорирует этот факт? что у моей сущности уже есть идентификатор, и он создает дубликат строки?
Если заглянуть дальше в кодовую базу Hibernate, она появится в новом потоке, persistentContext будет стерт. Поэтому моя сущность превращается в состояние ОТКЛЮЧЕНО, что касается DefaultMergeEventListener
... создавая каскад решений, которые необъяснимым образом приводят к генерации нового идентификатора
Ссылка на конкретный код I находится по адресу: https://github.com/hibernate/hibernate-orm/blob/master/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultMergeEventListener.java#L109
Если запустить из нового потока, контекст персистентности Hibernate будет пустым ... что нормально ... но тогда я не понимаю, почему моя сущность теперь считается DETACHED ...
Дополнительная выдержка из исходного кода Hibernate: по умолчанию
https://github.com/hibernate/hibernate-orm/blob/master/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultMergeEventListener.java#L293
if ( result == null ) {
//TODO: we should throw an exception if we really *know* for sure
// that this is a detached instance, rather than just assuming
//throw new StaleObjectStateException(entityName, id);
// we got here because we assumed that an instance
// with an assigned id was detached, when it was
// really persistent
entityIsTransient( event, copyCache );
}
РЕДАКТИРОВАТЬ Если это связано с тем фактом, что транзакции еще не зафиксированы, и новый поток не использует ту же транзакцию ... есть ли способ форсировать транзакцию в коде?
Примечание: вызов JpaRepository.saveAndFlush()
не решает проблему
РЕДАКТИРОВАТЬ 2 Я использую встроенный h2 для этого теста, в любом случае я ожидал, что saveAndFlush()
передаст транзакцию в базу данных (встроенную или на другой стороне мира), чтобы несколько потоков могут использовать JpaRepository для просмотра состояний, сохраненных другими потоками, верно?
РЕДАКТИРОВАТЬ 3 Глядя на другие подобные вопросы, он помечает сам метод теста как @ Транзакция (распространение = NOT_SUPPORTED) заставляет базовый менеджер транзакций выполнить JpaRepository.save()
. ... это до сих пор сбивает с толку ... с чего начинался транзакционный метод тестирования? то есть почему транзакция не была зафиксирована с самого начала?