Я использую JPA 2.2 с Hibernate 5.3.7 для извлечения большого количества данных из базы данных.
Чтобы максимально уменьшить использование оперативной памяти в системе, я обрабатываю результаты запроса с использованием потоков.
Ниже приведен упрощенный код, который я использую:
String myQuery = "SELECT entity FROM MyEntity entity";
EntityManagerFactory emFactory = Persistence.createEntityManagerFactory(connector,propReader.getPropertiesObject());
EntityManager entityManager = emFactory.createEntityManager();
Session session = entityManager.unwrap(Session.class);
Stream<MyEntity> stream = session.createQuery(myQuery, MyEntity.class).stream();
stream.forEach(entity -> proccessEntity(entity));
В большинстве случаев это работает очень хорошо, но проблема в том, что иногда возникают взаимоблокировки, когда stream
пытается получить следующую сущность. Ниже приведен пример такого случая:
org.hibernate.exception.LockAcquisitionException: could not advance using next()
at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:123)
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:113)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:99)
at org.hibernate.internal.ScrollableResultsImpl.convert(ScrollableResultsImpl.java:69)
at org.hibernate.internal.ScrollableResultsImpl.next(ScrollableResultsImpl.java:104)
at org.hibernate.query.internal.ScrollableResultsIterator.hasNext(ScrollableResultsIterator.java:33)
at java.util.Iterator.forEachRemaining(Iterator.java:115)
at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
at testing.deadlock.Main.main(Main.java:115)
Caused by: java.sql.SQLException: Transaction (Process ID 59) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
at net.sourceforge.jtds.jdbc.SQLDiagnostic.addDiagnostic(SQLDiagnostic.java:372)
at net.sourceforge.jtds.jdbc.TdsCore.tdsErrorToken(TdsCore.java:2988)
at net.sourceforge.jtds.jdbc.TdsCore.nextToken(TdsCore.java:2421)
at net.sourceforge.jtds.jdbc.TdsCore.getNextRow(TdsCore.java:805)
at net.sourceforge.jtds.jdbc.JtdsResultSet.next(JtdsResultSet.java:611)
at com.mchange.v2.c3p0.impl.NewProxyResultSet.next(NewProxyResultSet.java:685)
at org.hibernate.internal.ScrollableResultsImpl.next(ScrollableResultsImpl.java:99)
... 10 more
Могу ли я как-то заставить поток попытаться снова получить объект несколько раз, когда возникает такая тупик? Если нет, то какие другие решения прикладного уровня вы предлагаете?
Спасибо!