JPA - Удаление элементов двунаправленных отношений - PullRequest
2 голосов
/ 12 августа 2010

Почему я могу удалить элементы двунаправленного отношения, хотя только одна сторона отношения управляется в контексте постоянства (пример I)?Когда у меня есть однонаправленные отношения, которые не работают (см. Пример II).Почему?

Сущности:

@Entity
Class User {
    ...
    @OneToMany(mappedBy = "user")
    private List<Process> processes;

    @OneToOne // Unidirectional
    private C c;
    ...

    @PreRemove
    private void preRemove() {
        for (Process p : processes) {
            p.internalSetUser(null);
        }
    }
   ...
}

@Entity
Class Process {
    ...
    @ManyToOne
    private User user;
    ...

    @PreRemove
    protected void preRemove() {
        if (this.user != null) {
            user.internalRemoveProcess(this);
        }
    }
   ...
}

@Entity
Class C {

 }

Пример I:

// Create User u1 with Processes p1, p2

tx.start();
// Only u1 is manged in persistence context and no process
userFacade.delete(u1); // There following is called: >> em.remove(em.merge(u1)); // Works
tx.commit();

Пример II:

// Create User u and Object C c, establish their relation.

tx.start();
cFacade.remove(c); //>>MySQLIntegrityConstraintViolationException,foreign key constraint fails
ty.commit();

В первом примере я использую эти внутренние методы для установки в каждом случае другой стороны отношения, но эта другая сторона не управляется в контексте постоянства, я думаю ?!Когда я изменяю процесс пользователя и сохраняю пользователя, процесс не обновляется, если я не использую cascade.MERGE или оба загружаются в транзакции и управляются на ПК.Так почему же работает удаление?

Ответы [ 2 ]

1 голос
/ 25 августа 2012

Это ожидаемое поведение:

Поскольку отношения являются двунаправленными, так как обновления приложения одна сторона отношений, другая сторона также должна обновляться, и быть в синхронизации. В JPA, как и в Java в целом это ответственность приложения или объектной модели для поддержания отношения. Если ваше приложение добавляет одну сторону отношения, то это должно добавить к другой стороне.

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

Например:

public class Employee {
    private List phones;
    ...
    public void addPhone(Phone phone) {
        this.phones.add(phone);
        if (phone.getOwner() != this) {
            phone.setOwner(this);
        }
    }
    ...
}

Источник: OneToMany # Getters_and_Setters

1 голос
/ 13 августа 2010

В Примере II, я думаю, вам нужно будет позвонить user.setC(null), прежде чем удалять c.

В Примере I, вот мое понимание.Сначала вы объединяете u1, поэтому u1' загружается в ПК, а состояние u1 копируется в u1' (и это все, так как вы не каскадируете MERGE), которое затем возвращается.Затем вы вызываете remove (на u1'), вызывается preRemove и меняется p1' и p2'.Таким образом, они грязные и будут обновляться соответствующим образом при сбросе (установка FK на NULL), в то время как u1' будет удален.И все работает.

На всякий случай, вот семантика операции слияния из спецификации JPA 2.0:

3.2.7.1 Слияние состояния отдельного объекта

Операция слияния позволяет распространять состояние от отдельных сущностей на постоянные сущности, управляемые менеджером сущностей.

Семантика операции слияния, применяемой к сущности X, следующая:

  • Если X является отсоединенным объектом, состояние X копируется в ранее существующий экземпляр управляемого объекта X' с тем же идентификатором или создается новая управляемая копия X' из X.
  • Если X является новым экземпляром объекта, создается новый экземпляр управляемого объекта X' и состояние X копируется в новый экземпляр управляемого объекта X'.
  • ЕслиX является удаленным экземпляром объекта, IllegalArgumentException будет выдан операцией слияния (или фиксация транзакции завершится неудачей).
  • Если X является управляемым объектом, он игнорируетсяОперация слияния, однако, операция слияния каскадно относится к объектам, на которые ссылаются отношения из X, если эти отношения были аннотированы значением аннотации элемента cascade=MERGE или cascade=ALL.
  • Для всех объектов Y на который ссылаются отношения из X, имеющие значение элемента каскада cascade=MERGE или cascade=ALL, Y, рекурсивно объединяется как Y'.Для всех таких Y, на которые ссылается X, X' имеет значение Y'.(Обратите внимание, что если X управляется, то X - это тот же объект, что и X'.)
  • Если X - это объект, объединенный с X', со ссылкой на другой объект Y, где cascade=MERGE или cascade=ALL не указано, то навигация той же ассоциации из X' дает ссылку на управляемый объект Y' с таким же постоянным идентификатором, как Y.

Поставщик постоянства не должен объединять поля, помеченные как LAZY, которые не были извлечены: он должен игнорировать такие поля при объединении.

Любые Version столбцы, используемые объектом, должны проверяться реализацией среды выполнения постоянства во времяоперация слияния и / или в момент сброса или фиксации.При отсутствии столбцов Version дополнительная проверка версии, выполняемая средой выполнения поставщика сохраняемости во время операции слияния.

Ссылка

  • JPA 2.0 Спецификация
    • 3.2.7.1 Слияние состояния отдельного объекта
...