2015-07-09 13 views
7

Zajmuję się tworzeniem aplikacji przy użyciu Wiosna odpoczynek i Hibernate i chcę dostać zagnieżdżone rekordy z bazy danych jak Dostaję Profession dla User, teraz chcę sprowadzić Users powiązanych z Profession I pobrane wcześniej.Jak pobrać podmiotom związanym z Hibernate

Oto moja klasa Dao

@SuppressWarnings({ "unchecked", "rawtypes" }) 
public List<Profession> getProfessionById(long id) throws Exception { 
    session = sessionFactory.openSession(); 
    Criteria cr = session.createCriteria(Profession.class); 
    cr.add(Restrictions.eq("uid", id)); 
    List results = cr.list(); 
    tx = session.getTransaction(); 
    session.beginTransaction(); 
    tx.commit(); 
    return results; 
} 
+0

Kryteria cr = session.createCriteria (User.class); cr.add (Restrictions.eq ("pid", pid)); List results = cr.list(); Czy mógłbyś również zaktualizować pytanie za pomocą mapowania i relacji ER między zawodem a użytkownikiem? Uważam, że mapowanie ManyToMany w tym przypadku można użyć EAGER FETCH w samym kodzie, a otrzymacie bezpośrednio obiekt Users in Profession. – pratikpawar

+0

Czy możesz wyjaśnić w tym przypadku, jaka jest prośba o REST? – gabrielgiussi

+0

Czy odpowiesz na kontrolę. – ozgur

Odpowiedz

2

Pobieranie Strategie

Istnieją cztery rozróżniając strategie

  1. fetch- „join” = Wyłącz leniwy załadunku, zawsze załadować wszystko kolekcje i podmioty.
  2. fetch- "select" (domyślnie) = Lazy wczytuje wszystkie kolekcje i encje.
  3. partia-size = "N" = Pobieranie do kolekcji lub jednostek "N", Nie rejestruje się.
  4. fetch- "subselect" = Pogrupuj jego kolekcję w wyciągu podrzędnym.

Aby uzyskać szczegółowe objaśnienia, można sprawdzić dokumentację stanu hibernacji.

FetchType.LAZY jest na żądanie

FetchType.EAGER jest natychmiastowe

@SuppressWarnings({ "unchecked", "rawtypes" }) 
public List<User> getProfessionById(long id) throws Exception { 
    session = sessionFactory.openSession(); 
     Criteria cr = session.createCriteria(Profession.class, "pro") 
          .setFetchMode("user", FetchMode.JOIN); 
     cr.add(Restrictions.eq("uid", id)); 
     Profession pro = cr.uniqueResult(); 
     tx = session.getTransaction(); 
     session.beginTransaction(); 
     tx.commit(); 
    return pro.getUsers(); 
} 
+0

gdzie będzie używany? –

+0

nie rozumiem .. – ozgur

+0

gdzie powinienem napisać ten kod? –

0

Możesz być w stanie korzystać z następujących powodów:

Criteria cr = session.createCriteria(Profession.class); 
cr.setFetchMode("user", FetchMode.EAGER); 
cr.add(Restrictions.eq("uid", id)); 
List results = cr.list(); 

Nie używałem kryteria hibernacji, ale szybkie google wymyślił coś podobnego.

1

Według kwerendy tabela zawód ma kolumnę uid który jest prawdopodobnie FK do tabeli Users i myślę, że tabela Users powinien mieć FK do zawodu zamiast.

więc tabela Users będzie miał many-to-one stowarzyszenie do Profession:

@ManyToOne 
private Profession profession; 

i Profession można skojarzyć wszystkich użytkowników posiadających ten konkretny zawód, więc w jednostce Profession masz odwrotną stronę tego stowarzyszenia:

@OneToMany(mappedBy = "profession") 
private List<Users> users = new ArrayList<>(); 

teraz, aby dostać wszyscy użytkownicy mający zawód można uruchomić proste zapytanie tak:

List<Users> users = (ist<Users>) session.createQuery(
    "select u " + 
    "from Profession p " + 
    "join fetch p.users u " + 
    "where p.id = :id") 
.setParameter("id", proffesionId) 
.list(); 
+0

Myślę, że użytkownik może mieć wiele zawodów, ponieważ zwraca listę , gdy szuka przez user_id w tabeli zawodów . – gabrielgiussi

2

Najpierw trzeba dodać poniżej mapowania w zawodzie, a podmiot użytkownika klasy

W zawodzie klasy

//bi-directional many-to-one association to Users 
@OneToMany(mappedBy="profession", cascade=CascadeType.ALL) 
private List<User> users; 

public List<User> getUsers() { 
    return this.users; 
} 

public void setUsers(List<User> users) { 
    this.users = users; 
} 

W użytkownika klasy podmiotu

@ManyToOne(fetch=FetchType.EAGER) 
@JoinColumn(name="profession_id") 
private Profession profession; 

Następnie można pobrać obiekt zawód przez id przy użyciu Twojego istniejącego kodu klasy DAO.

@SuppressWarnings({ "unchecked", "rawtypes" }) 
public List<Profession> getProfessionById(long id) throws Exception { 
    session = sessionFactory.openSession(); 
    Profession profession = (Profession) session.get(Profession.class, id); 

    // here you can get list of users for this profession 
    // List<User> users = profession.getUsers(); 
    return profession; 
} 
2

myślę, że robią coś złego w swojej klasie DAO, ponieważ jesteś otwarta sesja i transakcji wewnątrz metody i prawdopodobnie wewnątrz każdej z metod w DAO.

Takie postępowanie wiąże się z wysokimi kosztami, a koncepcja jest zła, dlatego transakcja musi pogrupować zestaw operacji, który zakończy się sukcesem lub porażką. W większości przypadków transakcja bazy danych będzie powiązana z całą operacją biznesową, w tym przypadku z żądaniem REST. Coś jak

POST/user

@ RestController.createUser

otwarta transakcja

UserDAO.saveUser

popełnić transakcja

odpowiedź

Ponadto, jeśli spojrzysz na swój kod, otwierasz transakcję, a następnie zamykasz.

tx = session.getTransaction(); 
session.beginTransaction(); 
tx.commit(); 

W tym przypadku użytkownik wysyła zapytanie do bazy danych, więc transakcja nie jest wcale potrzebna.

Transakcje stanowią problem przekrojowy w Twojej aplikacji, a biorąc pod uwagę, że już używasz Springa, powinieneś spojrzeć na adnotację @Transactional (lub jego odpowiednik xml), aby uzyskać transakcyjność z AOP (Spring tworzy aroud aspect). Dzięki temu twój kod stanie się bardziej czytelny i łatwiejszy w utrzymaniu.

Odpowiedź @ManojP ma dobrą i złą rzecz. Myślę, że powinieneś unikać dwukierunkowych relacji, kiedy tylko możesz, bo to sprawia, że ​​projekt jest trudniejszy. Moja rada jest taka: zaczynaj zawsze z jednokierunkowymi relacjami i jeśli znalazłeś przypadek, w którym nie możesz tego uniknąć, użyj go.Dobrą rzeczą jest to, że pokazuje wam użycie lazyness gdy robi:

List<User> users = profession.getUsers(); 

Ta linia kodu powinna być poza DAO. Co się dzieje, to, że masz listę użytkowników oznaczonych jako leniwi (jest to ustawienie domyślne), a następnie, po pobraniu zawodów z zapytaniem o kryterium, uruchamiany jest wybór ponad zawodami ze stołu, a każdy z obiektów Zawodu jest skonstruowany z kolekcja proxy zamiast prawdziwej kolekcji. Gdy wywołujemy metodę profession.getUsers(), nowy selektor jest przesuwany nad tabelą użytkowników, gdzie profession_id = profession.getId(). A więc:

  1. Lista wyników = kryteria.list();
  2. Wybierz * z zawodu
  3. professionA.getUsers();
  4. select * from Użytkownika gdzie profession_id =: professionA.getId()

Ale uwaga! jeśli masz zbiór zawodów (jak sądzę masz bo jesteś powrocie listy) i iteracyjne nad każdego zawodu i poprosić o listę użytkowników chcesz robić:

  1. wyników list = criteria.list();
  2. select * from Zawód
  3. dla każdego zawodu -> profession.getUsers
  4. select * from Użytkownika gdzie profession_id =: professionA.getId()
  5. select * from Użytkownika gdzie profession_id =: professionB.getId()

To będzie źle działać.

Odpowiedź @farvilain jest dobra. Ale w tym przypadku zawsze będziesz pobierał Zawód z tą kolekcją Użytkowników, ponieważ w twoim DAO zawsze używasz FetchMode.JOIN, a wtedy tracisz korzyści z lenistwa. Tak więc, jeśli zawsze chcesz mieć listę użytkowników, gdy pytasz o zawody, użyj lazy = true dla kolekcji użytkowników, ale pamiętaj o kosztach, jakie mogą mieć (jeśli użytkownik ma niezaplanowaną kolekcję A, gdy pytasz o zawody otrzymasz także listę użytkowników plus kolekcję A). Jeśli nie chcesz tego, to może mieć to sygnatura:

public List<Profession> getProfessionById(Long id, FetchMode fetchMode) throws Exception 

To nie jest bardzo ładne, ale pokazuje, że klient DAO mógł wybrać tryb fecth.

1

Począwszy od kodu

@SuppressWarnings({ "unchecked", "rawtypes" }) 
public List<Profession> getProfessionById(long id) throws Exception { 
    session = sessionFactory.openSession(); 
    Criteria cr = session.createCriteria(Profession.class); 
    cr.add(Restrictions.eq("uid", id)); 
    List results = cr.list(); 
    tx = session.getTransaction(); 
    session.beginTransaction(); 
    tx.commit(); 
    return results; 
} 

raz pierwszy zaproponuje korzystanie z Transactionnal adnotacji jeśli używasz Wiosna. Dzięki temu kod staje się bardziej przejrzysty. Następnie przestań używać SuppressWarnings, to jest brzydkie i bezużyteczne, możesz skonfigurować IDE, aby je ukryć.

@Transactionnal(readonly = true) 
public List<Profession> getProfessionById(long id) throws Exception { 
    Criteria cr = session.createCriteria(Profession.class); 
    cr.add(Restrictions.eq("uid", id)); 
    List results = cr.list(); 
    return results; 
} 

Nie ma zastosowania fakt, że kryteria są płynne API i nie tworzą zmiennej lokalnej, teraz jest to zbędne.

@Transactionnal(readonly = true) 
public List<Profession> getProfessionById(long id) throws Exception { 
    return session 
     .createCriteria(Profession.class) 
     .add(Restrictions.eq("uid", id)) 
     .list(); 
} 

Załóżmy teraz rozwiązać problem

@Transactionnal(readonly = true) 
public List<Profession> getProfessionById(long id) throws Exception { 
    return session 
     .createCriteria(Profession.class) 
     .add(Restrictions.eq("uid", id)) 
     .setFetchMode("users", FetchMode.JOIN) 
     .list(); 
} 

Wymaga to oczywiście ta linia w zawodzie;

public class Profession { 
    [...] 

    @OneToMany(mappedBy = "profession") 
    private Set<Users> users = new HashSet<>(); 

    [...] 
} 

Warning

Nie trzeba getter/setter, nie nazywamy reverse mapping gdy uneeded.

Nie używaj listy, hibernacja naprawdę nie lubi listy bez deklaracji zamówienia.

Nie deklaruj użytkowników jako pobranych Z entuzjazmem w podmiocie, będziesz mieć przerażający problem za każdym razem, gdy załadujesz zawód będący własnością wielu użytkowników, nawet jeśli nie jesteś dobry, aby je pobrać samodzielnie.

Nie używaj FetchMode.EAGER, jest przestarzały!

0

Skorzystaj z mapowania ManyToMany, aby uzyskać zawody, aby również użytkownicy z assosoated również przyszli. Sprawdź to link.

Powiązane problemy