Jak załadować leniwe pobrane elementy z Hibernate/JPA w moim kontrolerze

Mam klasę osoby:

@Entity
public class Person {

    @Id
    @GeneratedValue
    private Long id;

    @ManyToMany(fetch = FetchType.LAZY)
    private List<Role> roles;
    // etc
}
Z relacją wielu do wielu, która jest leniwa.

W moim kontrolerze mam

@Controller
@RequestMapping("/person")
public class PersonController {
    @Autowired
    PersonRepository personRepository;

    @RequestMapping("/get")
    public @ResponseBody Person getPerson() {
        Person person = personRepository.findOne(1L);
        return person;
    }
}

A PersonRepository to właśnie ten kod, napisany według tego przewodnika

public interface PersonRepository extends JpaRepository<Person, Long> {
}
[[3]}jednak w tym kontrolerze naprawdę potrzebuję lazy-data. Jak mogę uruchomić jego ładowanie?

Próba dostępu nie powiedzie się

Nie udało się zainicjować zbioru ról: nie.dusken.momus.model.Osób.role, nie można zainicjować proxy-nie Sesja

Lub inne wyjątki w zależności od tego, co próbuję.

Mój XML-opis , w razie potrzeby.

Dzięki.
Author: Matsemann, 2013-03-12

6 answers

Będziesz musiał wykonać wyraźne wywołanie kolekcji lazy, aby ją zainicjować (powszechną praktyką jest wywołanie .size() w tym celu). W Hibernate istnieje dedykowana dla tego Metoda (Hibernate.initialize()), ale JPA nie ma jej odpowiednika. Oczywiście będziesz musiał upewnić się, że wywołanie zostało wykonane, gdy sesja jest nadal dostępna, więc dodaj adnotację do swojej metody kontrolera za pomocą @Transactional. Alternatywą jest utworzenie pośredniej warstwy usług pomiędzy kontrolerem a repozytorium, która może ujawniać metody inicjujące leniwe zbiory.

Aktualizacja:

Należy pamiętać, że powyższe rozwiązanie jest łatwe, ale skutkuje dwoma odrębnymi zapytaniami do bazy danych (jedno dla użytkownika, drugie dla jego ról). Jeśli chcesz osiągnąć lepszą wydajność dodaj następującą metodę do interfejsu repozytorium Spring Data JPA:

public interface PersonRepository extends JpaRepository<Person, Long> {

    @Query("SELECT p FROM Person p JOIN FETCH p.roles WHERE p.id = (:id)")
    public Person findByIdAndFetchRolesEagerly(@Param("id") Long id);

}

Ta metoda użyje klauzuli JPQL fetch join , aby chętnie załadować skojarzenie ról w jednej podróży powrotnej do bazy danych, i w związku z tym złagodzi kary za wydajność ponoszone przez dwa odrębne zapytania w powyższym rozwiązaniu.

 166
Author: zagyi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-02 13:33:10

Chociaż jest to stary post, rozważ użycie @NamedEntityGraph (Javax Persistence) i @EntityGraph (Spring Data JPA). Kombinacja działa.

Przykład

@Entity
@Table(name = "Employee", schema = "dbo", catalog = "ARCHO")
@NamedEntityGraph(name = "employeeAuthorities",
            attributeNodes = @NamedAttributeNode("employeeGroups"))
public class EmployeeEntity implements Serializable, UserDetails {
// your props
}

A następnie repo sprężyny jak poniżej

@RepositoryRestResource(collectionResourceRel = "Employee", path = "Employee")
public interface IEmployeeRepository extends PagingAndSortingRepository<EmployeeEntity, String>           {

    @EntityGraph(value = "employeeAuthorities", type = EntityGraphType.LOAD)
    EmployeeEntity getByUsername(String userName);

}
 25
Author: rakpan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-10 17:54:09

Masz kilka opcji

  • napisz metodę w repozytorium, która zwróci inicjalizowaną jednostkę zgodnie z sugestią R. J.
Więcej pracy, najlepsza wydajność.
  • Użyj OpenEntityManagerInViewFilter, aby utrzymać sesję otwartą dla całego żądania.

Mniej pracy, zwykle akceptowalny w środowisku sieciowym.

  • Użyj klasy pomocniczej, aby zainicjować encje, gdy jest to wymagane.

Mniej pracy, przydatne, gdy OEMIV nie jest w opcji, na przykład w huśtawce aplikacja, ale może być przydatna również w implementacjach repozytorium, aby zainicjować dowolny obiekt w jednym ujęciu.

Dla ostatniej opcji, napisałem klasę użytkową JpaUtils , aby zainicjować encje w jakimś deph.

Na przykład:

@Transactional
public class RepositoryHelper {

    @PersistenceContext
    private EntityManager em;

    public void intialize(Object entity, int depth) {
        JpaUtils.initialize(em, entity, depth);
    }
}
 12
Author: Jose Luis Martin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-12 12:29:21

Może być ładowany tylko leniwie podczas transakcji. Możesz więc uzyskać dostęp do kolekcji w swoim repozytorium, które ma transakcję - lub to, co normalnie robię, to get with association, lub ustawić fetchmode na eager.

 6
Author: NimChimpsky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:30

Myślę, że potrzebujesz OpenSessionInViewFilter aby utrzymać otwartą sesję podczas renderowania widoku (ale nie jest to zbyt dobra praktyka).

 6
Author: Michail Nikolaev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-12 11:51:50

Możesz zrobić to samo tak:

@Override
public FaqQuestions getFaqQuestionById(Long questionId) {
    session = sessionFactory.openSession();
    tx = session.beginTransaction();
    FaqQuestions faqQuestions = null;
    try {
        faqQuestions = (FaqQuestions) session.get(FaqQuestions.class,
                questionId);
        Hibernate.initialize(faqQuestions.getFaqAnswers());

        tx.commit();
        faqQuestions.getFaqAnswers().size();
    } finally {
        session.close();
    }
    return faqQuestions;
}

Po prostu użyj FAQ.getFaqAnswers ().size()nin kontrolera i otrzymasz rozmiar jeśli leniwie zainializowana lista, bez pobierania samej listy.

 0
Author: neel4soft,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-06 11:08:14