2011-08-11 11 views
8

Mamy problem z wyborem n + 1 z Hibernate 3.3.Hibernate OneToOne automatyczne pobieranie (rozwiązywanie problemu n + 1)

Dla uproszczenia po prostu wykonam krótki abstrakcyjny przykład.

Załóżmy, że mamy następujące proste ćwiczenia:

class MainEntity { 
    @Id 
    public Long id; //we have a table generator create this id 

    @OneToOne (mappedBy ="main") 
    public SubEntity subEntity; 
} 

class SubEntity { 
@Id 
@Column(name = "mainId") //note that this is the same column as the join column below 
public Long mainId; //in order to have the exact same id as the corresponding MainEntity 

@OneToOne (fetch = FetchType.LAZY) 
@JoinColumn (name = "mainId", insertable = false, updatable = false, nullable = false) 
public MainEntity main; //this is used for navigation and queries (" ... subentity.main = :x") 
} 

Więc jak widać SubEntity ma związek z MainEntity która wyraża się przez dwie właściwości, których właściwość mainId jest jedynym odpowiedzialnym za zarządzanie relację/klucz obcy.

Działa to całkiem dobrze i idealnie pasuje do naszych potrzeb.

Jest jednak jeden problem z niecierpliwym ładowaniem SubEntity wraz z MainEntity.

Załóżmy, że mam zapytanie, które zwraca kolekcję MainEntity. Przy obecnej konfiguracji Hibernacja wyda n + 1 wybiera: samo zapytanie + n wybiera dla każdego SubEntity.

Oczywiście mógłbym dodać do zapytania zapytanie join fetch, ale wolałbym, żeby Hibernate zrobił to automatycznie. Dlatego próbowałem dodać @Fetch(FetchMode.JOIN), ale to nic nie dało.

Chciałbym również nie ma problemu przy użyciu @Fetch(FetchMode.SUBSELECT), co powinno zmniejszyć wybierz oświadczeń 2 - oryginalne zapytanie i select dla jednostek podrzędnych (przynajmniej to, co dzieje się w innym miejscu, z dopiskiem @CollectionOfElements i @Fetch(FetchMode.SUBSELECT)).


Więc pytanie brzmi: w jaki sposób mówię do hibernacji automatycznie join pobierać lub korzystać z jednego select w celu niecierpliwością załadować podmioty sub? Czy czegoś brakuje?

Dzięki z góry,

Thomas

PS: Jedna rzecz, która może być problem może być mappedBy = "main" który nie odnosi rzeczywistą kolumnę id, ale nie mogę go zmienić na mappedBy = "id" .

Odpowiedz

6

Jeśli chcesz współdzielonych kluczy podstawowych między MainEntity i SubEntity używać PrimaryKeyJoinColumn i MapsId adnotacji.

Korzystając PrimaryKeyJoinColumn podmiot jest ładowany dołączając tabelę MainEntity z tabelą SubEntity przy użyciu tego samego klucza podstawowego. Powinien rozwiązać problemy n + 1.

MapsId adnotacja zapytać Hibernate skopiować identyfikator z inny podmiot powiązany w naszym przykładzie będzie skopiować SubEntity.mainEntity.id do SubEntity.id.

@Entity 
public class MainEntity { 

    @Id 
    @GeneratedValue(strategy=GenerationType.IDENTITY) 
    @Column(name = "main_Id") 
    private Long id; 

    @OneToOne(cascade = CascadeType.ALL) 
    @PrimaryKeyJoinColumn 
    private SubEntity subEntity ; 
} 


@Entity 
public class SubEntity 
{ 
    @Id @Column(name="main_Id_FK") Long id; 

    @MapsId 
    @OneToOne 
    @JoinColumn(name = "main_Id_FK")  
    @PrimaryKeyJoinColumn 
    private MainEntity mainEntity;   

} 

Hibernate referencyjny Dokumentacja:

PrimaryKeyJoinColumn
MapsId

+0

Nie wiedziałem o "@ MapsId", dzięki za to. Wypróbuję to. Nawet w przypadku, gdy problem n + 1 nie zostanie rozwiązany, nadal wolałbym takie podejście do naszego obecnego. Tylko pytanie: czy pozwoliłoby mi to po prostu ustawić identyfikator bez odniesienia do "MainEntity"? Mamy przypadki, w których brakuje instancji 'SubEntity' i mamy tylko identyfikator odpowiadającej' MainEntity'. Jeśli to możliwe, wolałbym raczej nie ładować 'MainEntity' tylko dla ustawienia odniesienia. Innymi słowy: co by się stało, gdyby 'id' miał wartość, a' mainEntity' było 'null'? – Thomas

+0

Właśnie zapoznałem się z dokumentacją '@ MapsId', którą połączyłeś i niestety wydaje się, że ta adnotacja została wprowadzona przy użyciu Hibernate 3.5 (a tym samym JPA 2). Jednak obecnie utknęliśmy w JBoss 4.2.3 (który nie obsługuje JPA 2), a zatem nie możemy używać Hibernate 3.5 (pracujemy nad migracją do JBoss 6, ale to zajmie trochę czasu). – Thomas

+0

@Thomas To Odpowiedź na swój pierwszy komentarz. Jak stworzyć 'SubEntity' z tylko identyfikatorem' MainEntity'. Musisz użyć 'EntityManager.getReference', aby uzyskać odniesienie do' MainEntity' bez ładowania go i ustawić na relację 'SubEntiy.main' bez ładowania Main. –

1

Istnieją trzy sposoby, aby uniknąć pytania n +1:

Lot size 

subselect 

Make a LEFT JOIN in the query 

Here FAQ1 Here FAQ2

+0

Oba nazwiska prowadzi mnie do tej samej strony, jakieś bardziej konkretne linki? Dodatkowo: 'Rozmiar partii': Myślę, że masz na myśli' Rozmiar partii '- nie działa tutaj, 'subselect' - próbowałem tego, ale jak już wspomniano, to nie działało,' left join' - Wolałbym robić połączenie pobierania, ale zgodnie z tym, co powiedziałem, nie chciałbym dodawać go do każdego zapytania. – Thomas

Powiązane problemy