TransactionRequiredException при удалении в потоках

У меня проблема, когда я пытаюсь удалить данные из БД, используя несколько потоков с Hibernate.

Репо:

@Modifying
@Query("DELETE FROM Customer cus  WHERE cus.customerId in :customerIds")
public void deleteByCustomerIds(@Param("customerIds") List<Long> customerIds);

Услуга:

public runDelete (List<Long> customerIds) {
    List<List<Long>> partitions = Lists.partition(customerIds, 5000);

    for(int i = 0; i < partitions.size(); i++ ) {
        final int index = i;
        Runnable thread = () -> deleteCustomersInBatches(partitions.get(index));
        new Thread(thread).start();
    }
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
private void deleteCustomerInBatches(List<Long> customerIds) {

    for (List<Long> batch : Lists.partition(oldCalcIds, 1000)) {
        customerRepo.deleteByCustomerIds(batch);
    }
}

Вот как выглядит код: у меня есть тег @Transactional на уровне службы, где выполняется вызов репо.

at java.lang.Thread.run(Thread.java:748) Caused by: javax.persistence.TransactionRequiredException: Executing an update/delete query at org.hibernate.jpa.spi.AbstractQueryImpl.executeUpdate(AbstractQueryImpl.java:54)

Я продолжаю получать эту ошибку. Любая помощь приветствуется.

Вы звоните this.deleteCustomersInBatches, что аннотировано @Transactional. Эта аннотация здесь неэффективна, потому что вы вызываете нативный метод вместо прокси-метода, который будет перехвачен с использованием аннотации @Transactional.

Selaron 01.03.2019 23:50
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
1
423
1

Ответы 1

Это потому, что вы вызываете метод @Transactional из того же компонента.

@Transactional работает только с методами, вызываемыми на прокси, созданных Spring. Это означает, что когда вы создаете @Service или другой компонент, метод, вызываемый извне, будет транзакционным. При вызове из bean-компонента ничего не произойдет, так как он не проходит через прокси-объект.

Самым простым решением было бы перенести метод на другой компонент. Если вы действительно хотите сохранить его в одном компоненте, вам нужно вызвать его, чтобы он был обернут в прокси с помощью Spring AOP. Вы можете сделать это так:

private YourClass self;

@Autowired
private ApplicationContext applicationContext;

@PostConstruct
public void postContruct(){
    self = applicationContext.getBean(YourClass.class);
}

Тогда вызов метода на self приведет к открытию транзакции.

Другие вопросы по теме