ConstraintViolationException во время транзакции для объекта с индексом

У меня есть следующая сущность:

@Entity
@Table(name = "product",
        indexes = {@Index(name = "productIndex", columnList = "family, group, type", unique = true)})
public class Product implements Serializable {

    @Id
    @Column(name = "id")
    private Long id;

    @Column(name = "family")
    private String family;

    @Column(name = "group")
    private String group;

    @Column(name = "type")
    private String type;

}

И я пытаюсь очистить свою таблицу базы данных и вставить новые строки:

@Transactional
public void update(List<Product> products) {
    productRepository.deleteAll();
    productRepository.batchInsert(products);
}

ProductRepository это:

public interface ProductRepository extends JpaRepository<Product, Long>, BatchRepository<Product> { }

И BatchRepository:

@Repository
public class BatchRepository<T> {

    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public int batchInsert(List<T> entities) {
        int count = 0;

        for (T entity : entities) {
            entityManager.persist(entity);
            count++;

            if (count % 2000 == 0) {
                entityManager.flush();
                entityManager.clear();
            }
        }

        entityManager.flush();
        entityManager.clear();

        return count;
    }

}

Попытка выполнить вставку не удалась с java.sql.BatchUpdateException: ORA-00001: unique constraint (productIndex) violated, но не должно ли предыдущее удаление сделать это нарушение невозможным?

Обновление №1

@Transactional
public void update(List<Product> products) {
    productRepository.deleteAll();
    insertProducts(products);
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void insertProducts(List<Product> products) {
    productRepository.batchInsert(products);
}
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
0
70
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

По моему мнению, вы должны выполнять операцию deleteAll() отдельно, поскольку вы используете аннотацию @Transactional, и изменения не фиксируются до тех пор, пока аннотированная функция не вернется успешно. Из-за этого ваши первичные ключи все еще дублируются.

это, вероятно, так, но я делаю и то, и другое в одной и той же транзакции, потому что я хочу, чтобы она отменяла изменения в случае возникновения какой-либо ошибки. Есть ли способ обновить индексы?

thmasker 30.01.2023 11:33

Вы можете написать другой метод для вставки данных и добавить к нему аннотацию @Transactional(propagation = Propagation.REQUIRES_NEW). Затем вызовите метод в вашем методе update(...).

Tania Gupta 30.01.2023 12:19

ваше предложение не работает

thmasker 30.01.2023 13:18

Не могли бы вы поделиться фрагментом кода, как вы его реализовали.

Tania Gupta 31.01.2023 06:54

обновил вопрос с ним!

thmasker 31.01.2023 09:58

Вы получаете эту ошибку, потому что вы поместили уникальное ограничение в индекс таблицы "productIndex"

Вы пытаетесь удалить все записи в первой строке

@Transactional
public void update(List<Product> products) {
 productRepository.deleteAll();
 productRepository.batchInsert(products);
}

но я думаю, что записи остаются в индексе с именем "productIndex"

да, это в основном то, что происходит, но я также ищу способ обновить эту индексную таблицу... Я думал, что это было автоматически обработано

thmasker 30.01.2023 12:01

Можете ли вы попробовать этот код

public void update(List<Product> products) {
 deleteAll()
 insertProducts(products);
}

@Transactional
public void deleteAll() {
 productRepository.deleteAll();
}

@Transactional
public void insertProducts(List<Product> products) {
 productRepository.batchInsert(products);
}

с этим кодом удаление выполняется, даже если вставка не удалась. Это не то, чего я хочу

thmasker 31.01.2023 12:01
Ответ принят как подходящий

Я думаю, что нашел несколько ответов, которые решают вашу проблему.

Решение 1 - #ссылка

Аннотируйте свой транзакционный метод следующим образом:

@Modifying(flushAutomatically = true)

Это обеспечит сброс вызова deleteAll, т.е. индексы обновляются перед следующим вызовом batchInsert.

Решение 2 - #ссылка

Используйте deleteAllInBatch из JpaRepository вместо deleteAll.

Решение 3 - #ссылка

Вручную вызовите flush после deleteAll в транзакционном методе.

Решение 4

Более специализированным SQL-решением будет пакетная вставка новых записей во временную таблицу. Тогда вы можете либо:

  1. Удалите существующую таблицу product и переименуйте временную в product.
  2. SELECT * FROM #temp_table INTO product - поместить все записи из временной таблицы в product.

Дайте мне знать, если что-то из этого работает. Удачи!

решение 2 работает! Спасибо!

thmasker 01.02.2023 13:05

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