2015-03-06 2 views
4

У меня есть коллекция ленивого типа выборки в сущности. И я использую Spring Data (JpaRepository) для доступа к объектам.Задание того, нужна ли ленивая загрузка с данными весны

@Entity 
public class Parent{ 
@Id 
private Long id; 

    @OneToMany(mappedBy = "parentId", fetch = FetchType.LAZY) 
    private Set<Child> children; 
} 

Я хочу две функции в классе обслуживания и текущей реализации являются следующие:

  1. «дети» должно быть пустым при выборке родителю

    public Parent getParent(Long parentId){ 
        return repo.findOne(parentId); 
    } 
    
  2. «дети» должны быть заполняется при извлечении родителя:

    public Parent getParentWithChildren(Long parentId){ 
        Parent p = repo.findOne(parentId); 
        Hibernate.initialize(p.children); 
        return p; 
    } 
    

При возврате "Parent" объект из RestController, после исключения:

@RequestMapping("/parent/{parentId}") 
public Parent getParent(@PathVariable("parentId") Long id) 
{ 
    Parent p= parentService.getParent(id);//ok till here 
    return p;//error thrown when converting to JSON 
} 

org.springframework.http.converter.HttpMessageNotWritableException: Не удалось записать содержание: не удалось инициализировать лениво коллекция Роль: com.entity.Parent.children, не удалось инициализировать прокси - нет Сессия (через ссылочную цепочку: com.entity.Parent ["children"]); вложенного исключения com.fasterxml.jackson.databind.JsonMappingException: не удалось инициализировать лениво коллекцию роли: com.entity.Parent.children, не может инициализировать прокси - не сессий (через эталонную цепь: com.entity .Parent [ "дети"])

ответ

6

Если вы хотите, чтобы для различных JSON представлений той же модели предметной области, в зависимости от случая использования, то вы можете посмотреть на следующем, что позволит вам сделать это без требующих DTOS:

https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring

в качестве альтернативы, см также «Проекции в Spring Data Rest» секции в следующем

https://spring.io/blog/2014/05/21/what-s-new-in-spring-data-dijkstra#projections-in-spring-data-rest

+0

Я думаю, что ссылка # 1 идеально подходит в нашем случае. Спасибо. – coolscitist

2

RestController должен возвращать ParentDTO вместо Parent сущности. ParentDTO может быть заполнен методом транзакции.

+0

Наш фактический сущности является гораздо более сложным, и больше, чем образец кода I ш Рота. ParentDTO будет слишком большой накладной для нас. – coolscitist

+0

С объектами вы можете получить проблемы с круговой зависимостью при преобразовании их в json. – amdalal

+0

Хм .. поэтому мне придется либо полностью создать DTO, либо удалить детей. Круговая зависимость была разрешена с помощью аннотации. – coolscitist

2

Исключение выбрано потому, что сериализатор JSON требует, чтобы все свойства были уже инициализированы. Таким образом, все контроллеры REST, которые нужно возвращать Родитель, должны инициализировать свойства первого:

@RequestMapping("/parent/{parentId}") 
public Parent getParent(@PathVariable("parentId") Long id) { 
    return parentService.getParentWithChildren(id); 
} 

Метод getParentWithChildren служба запускается внутри транзакции, и связанный с Hibernate Session закрыт, когда сделка совершается. Это означает, что вы должны инициализировать все свойства, пока сеанс Hibernate все еще открыт (внутри метода службы).

Вы можете также использовать поддержку Spring Data entity graph:

@Entity 
@NamedEntityGraphs(@NamedEntityGraph(name = "Parent.children", attributeNodes = @NamedAttributeNode("children"))) 
public class Parent{ 
@Id 
private Long id; 

    @OneToMany(mappedBy = "parentId", fetch = FetchType.LAZY) 
    private Set<Child> children; 
} 

и метод getParentWithChildren становится:

@Repository 
public interface ParentRepository extends CrudRepository<Parent, Long> { 

    @EntityGraph(value = "Parent.children", type = EntityGraphType.LOAD) 
    Parent getParentWithChildren(Long parentId); 
} 

Таким образом, вам даже не нужно реализовать:

  1. getParent
  2. getParentWithC hildren

Эти методы могут быть предоставлены данными Spring.

+0

Привет, но я не хочу, чтобы дети родительского метода getParent(). Я хочу, чтобы дети были пустыми. – coolscitist

+0

Это не то, как работает JPA, вы не должны устанавливать коллекции в null. Вы можете получить Parant и преобразовать его в DTO, у которого нет детей. –

+0

Получил. Получение сборов является тяжелым бременем, и управление DTO также является тяжелым бременем в нашем случае. Таким образом, мы, вероятно, собираемся с решением Алана Хэя. – coolscitist

1

Прежде всего, вы не показали нам класс Child Java: Я надеюсь, что свойство называется parentId и не parent:

public class Child { 
    @ManyToOne 
    private Parent parentId; 
} 

Решение 1: ваш код на самом деле правильно, просто, что вы должны использовать второй уровень DTO s (простые классы POJO) для переноса вашего домена на клиент/браузер. Если вы этого не сделаете, после того, как вы решите свои «ленивые исключения», у вас возникнет проблема с круговой зависимостью от родителя к ребенку, а маршаллер JSON (Джексон) попытается кодировать Child, а затем его Parent, а затем его детей, затем снова их Parent и так далее. Пример DTO будет:

public class ParentDto { 
    private Long id; 
    private String prop1; 

    public ParentDto(Parent parent) { 
      this.id = parent.id; 
      this.prop1 = parent.prop1; 
      //...other properties 
    } 

    //here come all getters for the properties defined above. 
} 

Решения 2: Используйте @JsonIgnore для общественной собственности Parent.getChildren(), так что Джексон не пытается кодировать ребенок, когда сортировочная экземпляр Parent.

+0

Я использовал JsonManagedReference и JsonBackReference для решения проблемы циклической зависимости. Является ли DTO единственным вариантом? Наша фактическая сущность довольно огромная и сложная, поддержание DTO вызывает огромные накладные расходы. Нет ли другого решения? – coolscitist

+0

Я добавил еще одно решение (с @JsonIgnore). –

+0

@coolscitist помогает ли второе решение? –