Итак, значит, имею небольшой тестовый проектик на Java и Hibernate, несколько потоков, все изменяют одну и ту же таблицу.
Использую пессимистическую блокировку. Вот проблемный участок кода:
Java |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| public void run() {
System.out.printf("Thread for user %s run", user.getName());
Session session = sessionFactory.openSession();
for (int i = 0; i < 1000; i++){
Transaction transaction = session.beginTransaction();
Long randomLotId = getRandomLotId();
Lot lot = session.get(Lot.class, randomLotId, LockMode.fromJpaLockMode(LockModeType.PESSIMISTIC_WRITE));//исключение тут
lot.setCurrentRate(lot.getCurrentRate()+100);
lot.setLastOwner(user);
transaction.commit();
try {
sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
session.close();
countDownLatch.countDown();
} |
|
Это метод run из унаследованного от класса
Thread класса
UserThread.
При запуске 8 потоков параллельно, я получаю:
Код
сент. 05, 2022 3:40:11 PM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
WARN: SQL Error: 0, SQLState: 40P01
сент. 05, 2022 3:40:11 PM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
ERROR: ОШИБКА: обнаружена взаимоблокировка
Подробности: Процесс 21301 ожидает в режиме ShareLock блокировку "транзакция 77116"; заблокирован процессом 21288.
Процесс 21288 ожидает в режиме ShareLock блокировку "транзакция 77111"; заблокирован процессом 21301.
Подсказка: Подробности запроса смотрите в протоколе сервера.
Где: при блокировке кортежа (4,33) в отношении "lots"
сент. 05, 2022 3:40:11 PM org.hibernate.event.internal.DefaultLoadEventListener doOnLoad
INFO: HHH000327: Error performing load command
org.hibernate.dialect.lock.PessimisticEntityLockException: could not obtain pessimistic lock
at org.hibernate.dialect.lock.PessimisticWriteSelectLockingStrategy.lock(PessimisticWriteSelectLockingStrategy.java:101)
at org.hibernate.persister.entity.AbstractEntityPersister.lock(AbstractEntityPersister.java:2235)
at org.hibernate.loader.ast.internal.LoaderHelper.upgradeLock(LoaderHelper.java:68)
at org.hibernate.event.internal.AbstractLockUpgradeEventListener.upgradeLock(AbstractLockUpgradeEventListener.java:35)
at org.hibernate.event.internal.DefaultLockEventListener.onLock(DefaultLockEventListener.java:80)
at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:107)
at org.hibernate.internal.SessionImpl.fireLock(SessionImpl.java:704)
at org.hibernate.internal.SessionImpl.fireLock(SessionImpl.java:693)
at org.hibernate.internal.SessionImpl$LockRequestImpl.lock(SessionImpl.java:2155)
at org.hibernate.sql.results.jdbc.internal.DeferredResultSetAccess.lambda$new$0(DeferredResultSetAccess.java:122)
at org.hibernate.sql.exec.internal.CallbackImpl.invokeAfterLoadActions(CallbackImpl.java:37)
at org.hibernate.sql.results.jdbc.internal.JdbcValuesSourceProcessingStateStandardImpl.lambda$postLoad$0(JdbcValuesSourceProcessingStateStandardImpl.java:214)
at java.base/java.util.HashMap.forEach(HashMap.java:1421)
at org.hibernate.sql.results.jdbc.internal.JdbcValuesSourceProcessingStateStandardImpl.postLoad(JdbcValuesSourceProcessingStateStandardImpl.java:202)
at org.hibernate.sql.results.jdbc.internal.JdbcValuesSourceProcessingStateStandardImpl.finishUp(JdbcValuesSourceProcessingStateStandardImpl.java:191)
at org.hibernate.sql.results.spi.ListResultsConsumer.consume(ListResultsConsumer.java:149)
at org.hibernate.sql.results.spi.ListResultsConsumer.consume(ListResultsConsumer.java:32)
at org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.doExecuteQuery(JdbcSelectExecutorStandardImpl.java:437)
at org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.executeQuery(JdbcSelectExecutorStandardImpl.java:166)
at org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl.list(JdbcSelectExecutorStandardImpl.java:91)
at org.hibernate.sql.exec.spi.JdbcSelectExecutor.list(JdbcSelectExecutor.java:31)
at org.hibernate.loader.ast.internal.SingleIdLoadPlan.load(SingleIdLoadPlan.java:140)
at org.hibernate.loader.ast.internal.SingleIdLoadPlan.load(SingleIdLoadPlan.java:110)
at org.hibernate.loader.ast.internal.SingleIdEntityLoaderStandardImpl.load(SingleIdEntityLoaderStandardImpl.java:72)
at org.hibernate.persister.entity.AbstractEntityPersister.doLoad(AbstractEntityPersister.java:4322)
at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:4312)
at org.hibernate.event.internal.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:598)
at org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:571)
at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:223)
at org.hibernate.event.internal.DefaultLoadEventListener.lockAndLoad(DefaultLoadEventListener.java:507)
at org.hibernate.event.internal.DefaultLoadEventListener.doOnLoad(DefaultLoadEventListener.java:115)
at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:74)
at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:118)
at org.hibernate.internal.SessionImpl.fireLoadNoChecks(SessionImpl.java:1241)
at org.hibernate.internal.SessionImpl.fireLoad(SessionImpl.java:1229)
at org.hibernate.loader.access.IdentifierLoadAccessImpl.doLoad(IdentifierLoadAccessImpl.java:182)
at org.hibernate.loader.access.IdentifierLoadAccessImpl.lambda$load$1(IdentifierLoadAccessImpl.java:158)
at org.hibernate.loader.access.IdentifierLoadAccessImpl.perform(IdentifierLoadAccessImpl.java:105)
at org.hibernate.loader.access.IdentifierLoadAccessImpl.load(IdentifierLoadAccessImpl.java:158)
at org.hibernate.internal.SessionImpl.get(SessionImpl.java:1158)
at threadprocessing.UserThread.run(UserThread.java:36)
Я прикрепил полный архив с проектом, он на maven.
Там есть два файла .sql, один из них создаёт таблицы, а второй наполняет их.
Вернуться к обсуждению:
Hibernate взаимная блокировка нескольких потоков Java БД