У меня есть класс Person:
@Entity
public class Person {
@Id
@GeneratedValue
private Long id;
@ManyToMany(fetch = FetchType.LAZY)
private List<Role> roles;
// etc
}
С отношением многие ко многим это лениво.
У меня в контроллере есть
@Controller
@RequestMapping("/person")
public class PersonController {
@Autowired
PersonRepository personRepository;
@RequestMapping("/get")
public @ResponseBody Person getPerson() {
Person person = personRepository.findOne(1L);
return person;
}
}
А PersonRepository - это просто код, написанный в соответствии с этим руководством.
public interface PersonRepository extends JpaRepository<Person, Long> {
}
Однако в этом контроллере мне действительно нужны ленивые данные. Как я могу запустить его загрузку?
Попытка доступа к нему не удастся с
не удалось лениво инициализировать коллекцию ролей: no.dusken.momus.model.Person.roles, не удалось инициализировать прокси - нет сеанса
или другие исключения в зависимости от того, что я пытаюсь.
Мое xml-описание , на случай необходимости.
Спасибо.
Person
объекта с заданным параметром? В томQuery
, что включитьfetch
пункт и загрузитьRoles
тоже для человека.Ответы:
Вы должны будете сделать явный вызов для ленивого набора, чтобы инициализировать его (обычная практика - вызывать
.size()
для этой цели). В Hibernate есть специальный метод для this (Hibernate.initialize()
), но у JPA нет аналога. Конечно, вам нужно убедиться, что вызов выполнен, когда сеанс еще доступен, поэтому пометьте метод контроллера с помощью@Transactional
. Альтернативой является создание промежуточного уровня службы между контроллером и репозиторием, который может предоставлять методы, которые инициализируют ленивые коллекции.Обновить:
Обратите внимание, что приведенное выше решение является простым, но приводит к двум различным запросам к базе данных (один для пользователя, другой для его ролей). Если вы хотите добиться лучшей производительности, добавьте следующий метод в интерфейс репозитория Spring Data JPA:
Этот метод будет использовать предложение JPG « выборка соединения» для быстрой загрузки ассоциации ролей за один и тот же переход в базу данных и, следовательно, уменьшит снижение производительности, вызванное двумя различными запросами в вышеприведенном решении.
источник
join
безfetch
, набор будет возвращен сinitialized = false
; поэтому по-прежнему выдает второй запрос, как только к набору обращаются.fetch
является ключом к тому, чтобы убедиться, что отношения полностью загружены и избежать второго запроса.Хотя это старый пост, рассмотрите возможность использования @NamedEntityGraph (Javax Persistence) и @EntityGraph (Spring Data JPA). Комбинация работает.
пример
а затем весеннее репо, как показано ниже
источник
@NamedEntityGraph
это часть API JPA 2.1, которая не реализована в Hibernate до версии 4.3.0.@EntityGraph(attributePaths = "employeeGroups")
может использоваться непосредственно в репозитории данных Spring для аннотирования метода без использования@NamedEntityGraph
символа в вашем коде, не содержащем @Entity, который легко понять при открытии репо.У вас есть несколько вариантов
Больше работы, лучшая производительность.
Меньше работы, обычно приемлемой в веб-среде.
Меньше работы, полезно, когда OEMIV недоступен, например, в приложении Swing, но может быть также полезно при реализации репозитория для инициализации любого объекта за один раз.
Для последнего варианта я написал служебный класс JpaUtils для инициализации сущностей на некотором deph.
Например:
источник
он может быть загружен только в процессе транзакции. Таким образом, вы можете получить доступ к коллекции в вашем репозитории, в которой есть транзакция - или то, что я обычно делаю, -
get with association
или установить fetchmode в eager.источник
Я думаю, что вам нужен OpenSessionInViewFilter, чтобы держать сеанс открытым во время рендеринга представления (но это не слишком хорошая практика).
источник
Spring Data
JpaRepository
Spring Data
JpaRepository
определяет следующие два метода:getOne
, который возвращает прокси объекта, который подходит для установки@ManyToOne
или@OneToOne
родительской ассоциации при сохранении дочернего объекта .findById
, который возвращает объект POJO после выполнения оператора SELECT, который загружает объект из связанной таблицыТем не менее, в вашем случае, вы не звонили ни
getOne
илиfindById
:Итак, я предполагаю, что
findOne
метод - это метод, который вы определили вPersonRepository
. ОднакоfindOne
метод не очень полезен в вашем случае. Поскольку вам нужно получить коллекциюPerson
вместе с isroles
, лучшеfindOneWithRoles
вместо этого использовать метод.Пользовательские методы Spring Data
Вы можете определить
PersonRepositoryCustom
интерфейс следующим образом:И определите его реализацию следующим образом:
Это оно!
источник
Вы можете сделать то же самое, как это:
Просто используйте faqQuestions.getFaqAnswers (). Size () в вашем контроллере, и вы получите размер, если лениво инициализировать список, без извлечения самого списка.
источник