Сохранение новой, но идентичной сущности с JPA, сообщающей дублирующую запись - PullRequest
1 голос
/ 19 декабря 2009

У меня есть проект JPA, связанный с базой данных MySQL, где мой объект сущности сопоставлен с таблицей с ограничением в 2 столбца. То есть:

@Entity
@Table(name = "my_entity")
class MyEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "id")
    private Integer id;
    @Basic(optional = false)
    @Column(name = "myField1")
    private String myField1;
    @Basic(optional = false)
    @Column(name = "myField2")
    private int myField2;
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "myEntity")
    private Set<OtherEntity> otherEntitySet;
}

В базе данных таблица my_entity имеет уникальное ограничение (myField1, myField2). Проблема заключается в том, что если я удаляю существующую сущность с EntityManager.remove (entity), а затем добавляю новую с EntityManager.persist (entity), база данных выдает ошибку о дублирующейся строке.

Например:

entityManager.getTransaction().begin();

MyEntity entity1 = new MyEntity();
entity1.setMyField1("Foo");
entity1.setMyField2(500);
entityManager.persist(entity1);

entityManager.getTransaction().commit();
entityManager.getTransaction().begin();

entityManager.remove(entity1);

MyEntity entity2 = new MyEntity();
entity2.setMyField1("Foo");
entity2.setMyField2(500); 
entityManager.persist(entity2);

entityManager.getTransaction().commit();

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

Реализация hashCode и equals выглядит следующим образом:

public int hashCode() {
    int hash = 0;
    hash += (getMyField1().hashCode() + getMyField2());
    return hash;
}

public boolean equals(Object object) {
    if (!(object instanceof MyEntity)) {
        return false;
    }
    MyEntity other = (MyEntity) other;
    return (getMyField2() == other.getMyField2()) &&
        (getMyField1().equals(other.getMyField1()));
}

Ответы [ 2 ]

2 голосов
/ 19 декабря 2009

Я не думаю, что есть какой-либо стандартный способ указать операционный порядок в JPA, поэтому нет никакой гарантии, какой порядок будут выполняться. В идеале реализация JPA должна быть достаточно умной, чтобы обнаружить эту ситуацию и выполнить удаление перед вставкой, но это область, в которой они часто терпят неудачу. Альтернативно, если ваша база данных поддерживает отложенную проверку ограничений (например, Oracle делает, но MySQL не ), база данных будет обрабатывать это, ожидая времени фиксации, чтобы выдать исключение уникального нарушения ограничения.

Таким образом, одним из решений является просто выполнить дополнительную фиксацию после вашего вызова удалить (entity1). Другой возможностью было бы, прежде чем создавать entity2, сначала проверить, существует ли он в базе данных, и если да, просто использовать его. Оба эти варианта могут быть несколько громоздкими и не подходить для всех рабочих процессов. Возможно, вы захотите покопаться в документации по вашей текущей реализации JPA, чтобы узнать, предлагают ли они какие-либо расширения, которые могут помочь.

0 голосов
/ 20 января 2010

У меня была такая же проблема, связанная с UC и вставками / удалениями, происходящими в неправильном порядке. Мне пришлось создать составной ключ для моей сущности, соответствующий столбцам UC. После этого удаления и вставки происходили в правильном порядке. Смотрите этот пост и комментарий о добавлении @EmbeddedId:

https://forum.hibernate.org/viewtopic.php?p=2382504

...