5

Używamy dziedziczenia pojedynczego stołu dla każdego stołu w naszej aplikacji. Dzięki temu różne instancje tego samego stosu aplikacji działają z tymi samymi obiektami DAO, podczas gdy ich jednostki mogą się nieznacznie różnić potencjalnie zawierające informacje unikalne dla tej instancji. Abstrakcyjna klasa definiuje podstawową strukturę tabeli i rozszerzenie definiuje dodatkowe kolumny, w razie potrzeby przez ten przykład:Czy mogę usunąć kolumnę dyskryminatora w dziedziczeniu pojedynczej tabeli w trybie hibernacji?

@Entity 
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) 
@Table(name = "client") 
public abstract class Client extends AbstractPersistable<Long> { 
    // ... 
} 

aplikacja A:

@Entity 
public class ClientSimple extends Client { 
    private String name; 
    // getter, setter 
} 

aplikacja B:

@Entity 
public class ClientAdvanced extends Client { 
    private String description; 
    // getter, setter 
} 

Teraz DAO może pracować z obiektami A i B w obiektach Client, ale aplikacja B może definiować dodatkowe informacje dla obiektu klienta, które mogą być odczytywane przez metodę zarządzania e do aplikacji B:

aplikacji A:

Client client = new ClientSimple(); 
clientDao.save(client); 

aplikacja B:

Client client = new ClientAdvanced(); 
clientDao.save(client); 

Niestety to oznacza, że ​​jest to kolumna dtype w każdej tabeli (lub inna nazwa, że ​​mogę wybrać) . Czy istnieje sposób, aby się tego pozbyć? Nie potrzebujemy go i korzysta z przestrzeni DB ...

Dzięki!


EDIT

Ważne, aby pamiętać: @MappedSuperclass nie będzie działać. Używamy QueryDSL jako naszej warstwy abstrakcji HQL. Wymaga to automatycznie generowanych klas typu zapytania dla kwerend typu zapisywania. Zostaną one jednak wygenerowane poprawnie tylko wtedy, gdy klasa abstrakcyjna opatrzona jest adnotacją @Entity.

To neccessairy ponieważ chcemy kwerendy przeciwko abstrakcyjnej klasy Client podczas gdy w rzeczywistości zapytań ClientSimple w zgłoszeniu A i ClientAdvanced w zgłoszeniu B:

więc w dowolnej aplikacji, to będzie działać:

query.where(QClient.client.name.equals("something"); 

aw aplikacji B to zadziała:

query.where(QClientSimple.client.description.equals("something else"); 

EDIT2 - sprowadzają

Wydaje sprowadza się do tego: Czy mogę skonfigurować hibernacji w momencie wdrożyć ją ustawić typ dyskryminatora dla inhertited podmiotu do ustalonej wartości. Więc idąc z moim przykładem Client zawsze będzie ClientSimple w jednej aplikacji i ClientAdvanced w drugiej aplikacji, więc nie muszę przechowywać tych informacji w bazie danych?

Tak jak powiedziałem: każda aplikacja będzie instancją podstawowego stosu aplikacji. Każda aplikacja może definiować dodatkowe kolumny dla ich lokalnej bazy danych, ale WSZYSTKIE obiekty będą tego samego typu dla tej instancji, więc gwarantujemy, że dyskryminator jest zawsze taki sam, co czyni go redundantnym w bazie danych i przypadkiem użycia dla konfiguracji hibernacji.

+0

Którą wersję Querydsl używasz? –

+0

Używamy QueryDSL 2.2.3, ale możemy zaktualizować, jeśli nowsze wersje obsługują zmapowane klasy nadrzędne jako cele kwerend: w ten sposób: '@MappedSuperclass @Table publiczna klasa abstrakcyjna Client ...' + '@ Klasa publiczna @Entity ClientSimple extends Client' == > generuj typy zapytań ... ==> zapytanie: 'QClient.client.name' – Pete

+1

Nie jest obsługiwane bezpośrednio, ale możesz dodać do niego bilet na GitHub. Jest łatwo zaimplementowany. –

Odpowiedz

1

Jeśli nigdy nie trzeba używać zarówno ClientSimple i ClientAdvanced w tej samej aplikacji można zadeklarować Client jak @MappedSuperclass zamiast @Entity.

+0

Niestety to nie jest możliwe. Używamy zapytania DSL jako naszej warstwy abstrakcji HQL. Wymaga to adnotacji klas abstrakcyjnych @Entity, aby można było wykonać zapytanie w odniesieniu do klasy abstrakcyjnej, podczas gdy w rzeczywistości zapytano o rozszerzenie. Będę edytować mój post, aby odzwierciedlić to ograniczenie. – Pete

1

W hierarchii pojedynczej tabeli na każdą klasę zawsze potrzebna byłaby kolumna dyskryminacyjna do rozróżniania encji, ponieważ wszystkie klasy w jednej hierarchii są przechowywane w jednej tabeli. Oto przykład Hibernate Single Table per Class Hierarchy.

Ale może warto rozważyć inny schemat hierarchii jak poniżej:

Hibernate Single Table per Subclass

Zalety

  • Stosując tę ​​hierarchię, nie wymaga skomplikowanych zmian w bazie danych schemat, gdy modyfikowana jest klasa pojedynczego nadrzędnego.
  • Działa dobrze z płytką hierarchią.

Wady

  • W hierarchii rośnie, może to spowodować obniżenie wydajności.
  • Liczba połączeń wymaganych do zbudowania podklasy również rośnie.

Hibernate Single Table per Concrete class

Zalety

  • Jest to najprostszy sposób odwzorowywania dziedziczeniem do wykonania.

Wady

  • Dane ów należy do klasy rodzic jest rozrzucone po wielu stołach podklasy, który reprezentuje konkretne zajęcia.
  • Ta hierarchia nie jest zalecana w większości przypadków.
  • Zmiany w klasie nadrzędnej znajduje odzwierciedlenie na dużą liczbę stołów
  • Zapytanie sformułowane w kategoriach klasy nadrzędnej prawdopodobnie spowodowałoby znaczną liczbę wybranych operacji

Proponuję, aby mieć spójrz na schemat Single Table Per Subclass.Chociaż nie jestem pewien co do Twojego dokładnego wymagania. Ale to może pomóc.

+0

Czy jesteś absolutnie pewien, że nie ma możliwości napisania niestandardowego deserializera? Nie jestem ekspertem od hibernacji, ale założę się, jeśli spojrzymy na sposób, w jaki hibernacja przekształca ResultSet w obiekt, istnieje sposób na dostosowanie go tak, jak chce Pete. –

+0

Dzięki za linki viralpatel, dobra lektura, ale niestety to nie pomaga. Być może nie można tego zrobić za pomocą adnotacji, takich jak wskazówki faceta mograbi ...?! – Pete

+0

Cześć Guy, Cóż, jest pewne, że można zastąpić wiele domyślnych funkcji otwartych frameworków, takich jak Hibernate. Ale problem z tym (tabela na Hierarchię) polega na tym, że twoje dane są przechowywane w jednej tabeli. I musi być kolumna, aby zidentyfikować, który wiersz danych należy do jakiej klasy w hierarchii. –

7

Wiem, to bardzo stare pytanie, ale ostatnio spotkałem się z tym problemem i może się to okazać przydatne dla kogoś.

Można to zrobić, korzystając z adnotacji Hibernate z @DiscriminatorFormula. Poniższy opis oparty jest na książce Java Persistence with Hibernate, rozdział 5.1.3; odpowiednia część zaczyna się na stronie ostatni akapit na stronie 202.

Z @DiscriminatorFormula można dostarczyć oświadczenie SQL który określa wartość discriminator podczas pobierania odpowiednich wierszy z bazy danych. W twoim przypadku musiałby to być prosty ciąg, który ocenia dowolnie wybraną wartość. Aby to zadziałało, musisz zdecydować o nazwie, która byłaby użyta dla twojej jednostki Client. Załóżmy, że wybierasz "GenericClient" jako nazwę entity. Jest to nazwa, która powinna pojawiać się w ramach adnotacji @Entity jako wartość atrybutu name. Tak więc pełny przykład w twoim przypadku będzie wyglądał następująco.

@Entity 
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) 
@Table(name = "client") 
@DiscriminatorFormula("'GenericClient'") // *1* 
public abstract class Client extends AbstractPersistable<Long> { 
    // ... 
} 

// Application A 
@Entity 
@DiscriminatorValue("GenericClient") // *2* 
public class SimpleClient extends Client { 
    // ... 
} 


// Application B 
@Entity 
@DiscriminatorValue("GenericClient") // *3* 
public class AdvancedClient extends Client { 
    // ... 
} 

Linia oznaczona przez „” jest częścią fragmentu SQL zawsze powrócić „GenericClient” jako wartości. subclasses z Client powinien zawsze być opatrzony komentarzem z @DiscriminatorValue("GenericClient"). Oznacza to, że gdy Hibernate pobiera wiersze z DB, typ obiektu do skonstruowania będzie zawsze specyficzną podklasą Client.

Jeśli pakiet gdzie podklasy Client zamieszkują, a nazwa podklasy są stałe: nie będzie wymagane

w tym przypadku @DiscriminatorValue("GenericClient") na podklasy, wszystko, czego potrzeba należy wykonać:

@Entity 
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) 
@Table(name = "client") 
@DiscriminatorFormula("'com.example.fixed.path.FixedSubClassName'") 
public abstract class Client extends AbstractPersistable<Long> { 
    // ... 
} 

Podklasy nie będą wymagać adnotacji. Domyślnym discriminator-value jest entity-name, który domyślnie jest w pełni kwalifikowaną nazwą klasy.

Uwaga:SQL oświadczenie wewnątrz @DiscriminatorFormula() może być dowolny ważny SQL oświadczenie dla docelowego serwera DB.

Powiązane problemy