2011-07-04 11 views
13

Dzień dobry, drodzy towarzysze,Single Table Inheritance BEZ dyskryminacyjne kolumny

To zaczyna być irytujące przyjść - prosta sprawa, ale godziny walki, ja starzeję ??

Próbuję odwzorować dwie klasy do jednej tabeli przy użyciu WZP przez Hibernate. Chodzi o to, aby mieć tylko niewielki podzbiór kolumn w klasie nadrzędnej i większy/pełny zestaw w klasie dziecka. Nie ma tu dziedziczenia NIE TABELA, tylko dziedziczenie klasy. Jak można tego dokonać?

Rozwiązanie to nie będzie działać:

@Entity 
@Table(name = "the_table") 
class Parent implements Serializable { 
} 

@Entity 
@Table(name = "the_table") 
class Child extends Parent implements Serializable { 
} 

Hibernate zakłada strategia domyślna dziedziczenie InheritanceType.SINGLE_TABLE i szuka kolumnie dyskryminatora - dtype domyślnie. Ale czekaj - nie ma dziedziczenia tabel, mając kolumnę dyskryminatora nie ma sensu.

Przyjrzałem się również PolymorphismType.EXPLICIT, które nie miało żadnego znaczenia. Ślad stosu jest:

Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'apprentice0_.DTYPE' in 'where clause' 
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) 
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39) 
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27) 
    at java.lang.reflect.Constructor.newInstance(Constructor.java:513) 
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:411) 
    at com.mysql.jdbc.Util.getInstance(Util.java:386) 
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1052) 
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3597) 
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3529) 
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1990) 
    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2151) 
    at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2625) 
    at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2119) 
    at com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:2281) 
    at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeQuery(NewProxyPreparedStatement.java:76) 
    at org.hibernate.jdbc.AbstractBatcher.getResultSet(AbstractBatcher.java:208) 
    at org.hibernate.loader.Loader.getResultSet(Loader.java:1808) 
    at org.hibernate.loader.Loader.doQuery(Loader.java:697) 
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:259) 
    at org.hibernate.loader.Loader.loadEntity(Loader.java:1881) 

tak, jeszcze jedno:

@MappedSuperclass i @Embeddable są bezużyteczne jako takie nie mogą być używane w połączeniu z @Entity - klasa dominująca musi być sama @Entity ponieważ jest używany do utrzymywania się gdzie indziej.

+0

Po prostu zgadnij ... Usunąć '@ Entity' i' @ Table' z klasy nadrzędnej? Nie chcesz przechowywać go osobno, prawda? – jbrookover

Odpowiedz

2

Zobacz tabelę z ograniczonym zestawem kolumn i zamapuj drugą klasę na tę. Zdefiniuj interfejs z ograniczonym zestawem kolumn i obie klasy implementują interfejs. Prawdopodobnie dostaniesz około 95% tego, czego potrzebujesz. Jeśli potrzebujesz, stwórz metody, aby zdefiniować równość między nimi, a także przekonwertuj większą klasę (poprzez konstruktor?) Na mniejszą klasę.

1

Musisz wybrać typ dziedziczenia dla dwóch jednostek.

To, co próbujesz zrobić, nie jest odpowiednie, ponieważ tryb hibernacji nie będzie wiedział, jakie obiekty utworzyć.

Jeśli po prostu potrzebujesz obiektu, aby mieć mniej pól, nie zamapuj go jako encji - po prostu podaj konstruktor, który skopiuje wszystkie pola z drugiej klasy.

+0

Posiadanie jednostki, która mapuje do podzbioru, jest interesujące, jeśli chodzi o relacje. Nie rozumiem - dlaczego Hibernate nie powinien wiedzieć? Mam DAO i Mappings, które żądają jednego lub drugiego. –

17

@MappedSuperclass to adnotacja, której należy użyć. Jeśli ta sama klasa jest zarówno odwzorowane klasy super i podmiot, a następnie po prostu podzielić ją na dwie klasy:

@MappedSuperclass 
public class Parent { 
    // ... 
} 

@Entity 
public class ParentEntity extends Parent { 
    // no code at all here 
} 

@Entity 
public class Child extends Parent { 
    // additional fields and methods here 
} 
+0

Jak zorganizowałbyś swoje mapowanie? – FMaz008

+0

** @ MappedSuperclass ** lub ** @ MappedSuperClass **? –

+0

@ Lame-up-duck: naprawiono –

3

Istnieje kilka sposobów, każdy z własnym zastrzeżeniami.

1) Dodawanie adnotacji następujące:

@DiscriminatorFormula("0") 
@DiscriminatorValue("0") 
class BaseClass{ } 

@DiscriminatorValue("00") 
class SubClass extends BaseClass{ } 

gdzie wartość podklasy dyskryminator muszą być różne klasy bazowej, ale także ocenić, w takiej samej wartości, kiedy przechodzi się z Integer.valueOf (metoda łańcucha S) .

The caveat - jeśli zwrócisz obiekt ze stanu hibernacji klasy bazowej, a następnie ponownie podczas wywoływania typu podklasy otrzymasz błąd narzucający załadowany obiekt był niewłaściwej klasy. Jeśli najpierw wywołasz zapytanie klasy podklasy, wywołanie klasy podstawowej zwróci podklasę.

2) Użyj widoku w bazie danych, aby zmapować tabelę i użyć jej jako tabeli klasy podrzędnej. W rzeczywistości może to być dowolna inna klasa, która pasuje do odwzorowań kolumn, ponieważ Hibernate uważa ją za całkowicie oddzielną tabelę.

Ostrzeganie - Potencjalnie wystąpi ten sam wiersz, co dwa różne obiekty, które nie zostaną zsynchronizowane i mogą prowadzić do konfliktów/utraconych aktualizacji bazy danych.

Prawdopodobnie lepiej trzymać się jednego typu dla sesji i można go obsługiwać bez ryzyka w czasie wykonywania, używając odwzorowania encji pliku xml, który przesłania wartość DiscriminatorValue żądanej klasy, aby dopasować wartość stałej Discriminator'Formula, którą może przejść do początkowej konfiguracji.