Множественное нарушение ограничений при сбросе и сохраняется в одной транзакции

#java #oracle #jpa #eclipselink #spring-transactions

#java #Oracle #jpa #eclipselink #spring-транзакции

Вопрос:

Я пытаюсь сохранить много записей в одной транзакции, используя JPA (EclipseLink). Запись имеет первичный ключ, определенный в DB.

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

Вот мой код:

Сущность:

 @Entity
@Table(name="TEST")
public class TestEntity {

    @Id
    private String name;

    public TestEntity() {

    }

    public TestEntity(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}
  

Операция:

 @Transactional
public void test() {

    List<String> list = Arrays.asList("A", "B","C","D","D","E","F","G");//duplicated D

    for(String name: list) {
        try {
            TestEntity entity = new TestEntity(name);
            logger.info(""   entity);
            entityManager.persist(entity);
            logger.info("Persist done");
            entityManager.flush();
            logger.info("Flush done");
        } catch(RuntimeException e) {
            logger.warn("Entry {}  was skipped due to following exception {}.",  name, e.getLocalizedMessage());
        }
    }

    logger.info("Importing dictionary finished. ");
}
  

В результате я получаю исключение нарушения ограничений для второго D (это то, что я ожидал), но я получаю это исключение и для следующих элементов, и это то, чего я не понимаю.

Я получаю: Внутреннее исключение: java.sql.SQLIntegrityConstraintViolationException: ORA-00001: уникальное ограничение (BBHDEV4.SYS_C0023890) нарушено

Что вызывает эту проблему?

Комментарии:

1. Вы должны удалить @Transactional . Это гарантирует, что либо все будет сохранено, либо изменения не будут внесены.

Ответ №1:

Ваш диспетчер объектов поддерживает состояние, состоящее из всех управляемых объектов (т.Е.. контекст сохранения) и транзакционное состояние его объектов.

Таким образом, после сброса, вызывающего нарушение ограничения, общий объект по-прежнему управляется и находится в устаревшем состоянии и будет снова сброшен при следующем явном вызове flush (вы не должны вызывать flush явно) или в конце текущей транзакции. вы должны вручную удалить общую сущность из контекста сохранения ( detach() ) перед возобновлением цикла.

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

Транзакция — это единица работы, если вы не хотите, чтобы ваши разные операции были независимыми, вы должны выполнять их в разных транзакциях. Обычно область диспетчера сущностей совпадает с областью транзакции (по крайней мере, когда она управляется контейнером ie. вводится с помощью @PersitenceContext), и вы решите обе проблемы сразу.

Ответ №2:

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

Чтобы исправить это (используя ванильный JPA), вы можете либо

  • Создайте новую транзакцию для каждого элемента, который вы хотите обновить.
  • Выполните обновление с помощью инструкции JPA update.

В режиме гибернации у вас есть третий вариант, который заключается в использовании сеанса без состояния, но я не знаю, имеет ли eclipselink понятие entitymanager без состояния

Комментарии:

1. Есть ли какой-нибудь способ удалить этот объект из текущей транзакции?

2. не из текущей транзакции, а из текущего состояния EM: detach() (вызывается evict в сеансе гибернации)

3. Что сказал Гэб 🙂 — Просто имейте в виду, что это не стандартный вызов JPA, а скорее часть внутреннего API EclipseLink.

4. Это стандартный JPA: docs.oracle.com/javaee/6/api/javax/persistence /…

5. Как только вы получите исключение сохранения, вы не сможете продолжать использовать свой EntityManager — его состояние повреждено, и транзакция должна быть помечена для отката. Почему бы просто не получить новый EM?