Отсоединить JPA-объекты с ленивыми инициализированными свойствами - PullRequest
5 голосов
/ 04 сентября 2010

Есть два объекта JPA: Пользователь и Заказ с отношением один ко многим.

/**
 * User DTO
 */
@Entity
@Table(name="user")
public class User implements Serializable {
    private static final long serialVersionUID = 8372128484215085291L;

    private Long id;
    private Set<Order> orders;

    public User() {}

    @Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="sequenceUser")
    public Long getId() {
        return this.id;
    }
    private void setId(Long id) {
        this.id = id;
    }

    @OneToMany(mappedBy="user", cascade=CascadeType.PERSIST, fetch=FetchType.LAZY)
    @LazyCollection(LazyCollectionOption.EXTRA)
    public Set<Order> getOrders() {
        return orders;
    }
    public void setOrders(Set<Order> orders) {
        this.orders = orders;
    }
 }


/**
 * Order DTO
 */
@Entity
@Table(name="order")
public class Order implements Serializable {
    private static final long serialVersionUID = 84504362507297200L;

    private Long id;
    private User user;

    public Order() {
    }

    @Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="sequenceOrder")
    public Long getId() {
        return this.id;
    }
    private void setId(Long id) {
        this.id = id;
    }

    @ManyToOne
    @JoinColumn(name="user_id")
    public User getUser(){
        return user;
    }
    public void setUser(User user){
        this.user = user;
    }
}

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

@Transactional(readOnly=true)
public Set<Order> getOrders() {
    Set<Order> orders = user.getOrders();

    return orders;
}

Этот метод хорошо возвращает данные.Но когда я пытаюсь получить доступ к полученным элементам коллекции, у меня возникает исключение: «org.hibernate.LazyInitializationException: не удалось лениво инициализировать коллекцию ролей: package.User.orders, ни один сеанс или сеанс не был закрыт».было исключено.Я думал, что результат отсоединения решит мою проблему, но такой трюк

@Transactional(readOnly=true)
public Set<Order> getOrders() {
    Set<Order> orders = user.getOrders();

    for(Order order: orders)
        entityManager.detach(order);
    return orders;
}

ничего не изменил: (

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

Кто-нибудь может мне помочь?:)

Ответы [ 2 ]

7 голосов
/ 04 сентября 2010

Этот метод хорошо возвращает данные. Но когда я пытаюсь получить доступ к полученным элементам коллекции, я ловлю исключение: «org.hibernate.LazyInitializationException: не удалось лениво инициализировать коллекцию роли: package.User.orders, ни один сеанс или сеанс не был закрыт».

Ошибка самоочевидна: вы пытаетесь загрузить (ленивую) загруженную коллекцию, но больше нет активного сеанса, потому что экземпляр User отсоединен.

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

Это ничего не изменит. Метод EntityManager#detach(Object) не загружает ничего, он удаляет переданную сущность из контекста постоянства, делая ее отсоединенной .

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

Вам нужно либо создать ассоциацию EAGER (чтобы при получении пользователя загружались заказы), либо использовать FETCH JOIN при получении пользователя в вашем сервисе:

SELECT u
FROM User u LEFT JOIN FETCH u.orders
WHERE u.id = :id

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

В Hibernate есть метод Hibernate.initialize, но это явно не стандартный JPA (предпочтительнее выборочное соединение для переносимого кода).

У меня нет способа заставить hibernate игнорировать ленивые ассоциации текущего объекта (установите их как null)

Что? Что вы подразумеваете под игнорировать? Зачем ты это делаешь?

1 голос
/ 04 сентября 2010

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

Вы можете запросить загрузку данных несколькими способами.Самый простой:

for(Order order: orders)
    order.getX()

(где X - это какое-либо свойство, отличное от Id или версии)

Это, однако, будет загружать каждый ордер в отдельном запросе, что является медленным, если много ордеров.Более быстрый подход состоит в том, чтобы выполнить запрос (JP-QL или Criteria), возвращающий все заказы этого пользователя.(Изменить: см. Ответ Паскаля для подходящего запроса.)

...