Отношения JPA / Hibernate Родитель / Ребенок - PullRequest
0 голосов
/ 17 марта 2011

Я довольно новичок в JPA / Hibernate (Java в целом), поэтому мой вопрос заключается в следующем (обратите внимание, я искал далеко и широко и не нашел ответа на этот вопрос):

У меня естьдве сущности:

Родитель и дочерний элемент (имя изменено).

Родитель содержит список дочерних элементов и дочерних элементов относится к родителю.

например,

@Entity
public class Parent {

@Id
@Basic
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "PARENT_ID", insertable = false, updatable = false)
private int id;        

    /* ..... */

    @OneToMany(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY)
    @JoinColumn(name = "PARENT_ID", referencedColumnName = "PARENT_ID", nullable = true)
    private Set<child> children;

    /* ..... */

}

@Entity
public class Child {

@Id
@Basic
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "CHILD_ID", insertable = false, updatable = false)
private int id; 

    /* ..... */

    @ManyToOne(cascade = { CascadeType.REFRESH }, fetch = FetchType.EAGER, optional = false)
@JoinColumn(name = "PARENT_ID", referencedColumnName = "PARENT_ID")
private Parent parent;

    /* ..... */

}

Я хочу иметь возможность сделать следующее:

  1. Получить родительский объект, который будет содержать список всех его дочерних элементов (List), однако при перечислении Parent (получение List,он, конечно, должен исключить дочерние элементы из результатов, поэтому установите FetchType.LAZY.

  2. Извлечь дочернюю сущность, которая будет содержать экземпляр родительской сущности.

Использование приведенного выше кода (или аналогичного) приводит к двум исключениям:

  1. Извлечение родителя: в графе объектов обнаружен цикл. Это приведет к бесконечно глубокому XML ...

  2. Получение ребенка: org.hibernate.LazyInitializationException: не удалось лениво инициализировать коллекцию ролей: xxxxxxxxxxx, ни один сеанс или сеанс не был закрыт

При получении родительского объекта я использую именованный запрос (т.е. вызываю его специально) @NamedQuery (name = "Parent.findByParentId", query = "SELECT p FROM Parent AS p LEFT JOIN FETCH p.children где p.id =: id")

Код для получения Parent (то есть уровня обслуживания):

public Parent findByParentId(int parentId) {
    Query query = em.createNamedQuery("Parent.findByParentId");
    query.setParameter("id", parentId);

    return (Parent) query.getSingleResult();
}

Почему я получаю событие LazyInitializationException, хотя свойство List в родительском объекте установлено как Lazy (при получении дочернего объекта)?

Ответы [ 3 ]

1 голос
/ 17 марта 2011

Исключение 1. Ваша сериализация XML не имеет ничего с JPA / Hibernate, вы сможете успешно сериализовать двустороннюю настройку. Если автоматическая сериализация не удалась, предварительно обработайте ваши сериализуемые объекты таким образом, чтобы вы удалили ссылку от дочернего к родительскому объекту - вне транзакции (при условии, что в вашем контейнере есть автоматическое слияние).

Исключение 2. Вы должны прочитать об Транзакциях. Короче говоря, существуют правила, когда можно выполнять обход объектов, требующих взаимодействия с базой данных, а не делать это. Логически этот разрыв должен существовать. Если вы хотите, чтобы ленивые отношения вели себя как «обычные» (читай: извлеченные) вне сеанса транзакции, вы просто «предварительно выбираете» отношения, просматривая или получая к ним доступ таким образом, что отношения разрешаются. Например, вызов .size или перебор членов в наборе или списке сделает это.

0 голосов
/ 06 марта 2014

У меня была такая же проблема. На самом деле у меня есть однонаправленное отношение (например, в Отделе много сотрудников). Таким образом, сотрудник будет иметь внешний ключ в отделе. После создания Hibernate устанавливает двунаправленную связь с ManyToOne и OneToMany. Поэтому проверьте, является ли ваше отношение односторонним или двунаправленным. В первом случае необходимо удалить отображение oneToMany (из отдела)

Надеюсь, что это поможет.

0 голосов
/ 17 марта 2011

ДОБАВЛЕНО

Ваше отображение немного странно. Ваше отображение описывает два разных однонаправленных корабля высвобождения :

  • Родитель - 1: n -> Ребенок
  • Ребенок - n: 1 -> Родитель

, которые оба независимы, но хранятся в одном столбце базы данных.

Полагаю, вы этого не хотите, я думаю, вы хотите иметь один двунаправленный корабль отношений . Самый простой способ - изменить сопоставление дочернего набора Родителя, используя mappedBy вместо @JoinColumn:

@Entity
public class Parent {
    ...
    /* fetch = FetchType.LAZY is default in one:many */
    @OneToMany(cascade = { CascadeType.ALL }, mappedBy="parent")
    private Set<child> children;    
    ...   
}

Из того, что вы описали в комментариях к моему старому ответу, это должно решить проблему. Потому что если вы теперь используете Naviagate Parant -> Child -> Parent, оба родителя будут одинаковыми, и поставщик сохраняемости знает об этом.

Остерегайтесь того факта, что отношения теперь поддерживаются только на стороне ребенка.

OLD

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

Вы можете делать все:

  • держать транзакцию открытой
  • загрузить объект перед закрытием транзакции
  • переключиться с ленивого на энергичный груз
  • исправьте свое отображение (см .: добавленную часть моего ответа)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...