2010-02-19

Dlaczego trochę mniej lubię Hibernate

Używając Hibernate nigdy nie trafiłem na ewidentny bug. Owszem zdarzało się walczyć z oporem materii (głównie chodziło o zasięg sesji i LazyInitializationException), ale koniec końców okazywało się że biblioteka zachowuje się tak jak powinna się zachowywać, a błąd robiłem ja.
Dlatego, gdy po próbie zamapowania encji na dwie tabele, zobaczyłem:
org.hibernate.AssertionFailure: Table ... not found
zacząłem szukać ewentualnych literówek, brakujących adnotacji czy błędów w konfiguracji. I nic, absolutnie nic nie znalazłem. Pozostało - oczywiście - zapytać Google. Dość szybko trafiłem na zgłoszenie ANN-376 dotyczące Hibernate Annotations: nie można dodać drugiej tabeli do klasy, która jest częścią hierarchii zamapowanej z opcją InheritanceType.JOINED. A dokładnie to w sytuacji gdy mamy tabele powiązane kluczami obcymi:
CREATE TABLE A(
    INT id PRIMARY KEY NOT NULL
)

CREATE TABLE B(
    INT id NOT NULL,
    VARCHAR(100) b
)
ALTER TABLE B ADD CONSTRAINT FK1 FOREIGN KEY (id) REFERENCES A

CREATE TABLE C(
    INT id NOT NULL,
    VARCHAR(100) c
)
ALTER TABLE C ADD CONSTRAINT FK2 FOREIGN KEY (id) REFERENCES A
i próbujemy na nie zamapować klasy:
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@SecondaryTable(name = "C")
public class A {
    @Id
    Long id;

    @Column(table = "C")
    String c;
}

@Entity
public class B extends A {
    String b;
}
dostajemy wyjątek:
org.hibernate.AssertionFailure: Table C not found
Co ciekawe, wystarczy zmiana sposobu dziedziczenia (i struktury tabel) na InheritanceType.TABLE_PER_CLASS lub InheritanceType.SINGLE_TABLE i wszystko jest ok.
Niestety, w moim przypadku takie rozwiązanie było nie do przyjęcia - system już działał, w tabelach były dane i zmiana struktury bazy nie wchodziła w rachubę. Ani w wspomnianym wcześniej zgłoszeniu, ani w powiązanych z nim HHH-4240 i HHH-2053 nikt nie opisał sensownego obejścia, które nie wymagałoby modyfikacji bazy. Ostatecznie zrobiłem tak:
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class A {
    @Id
    Long id;
}

@Entity
public abstract class C extends A {
    String c;
}

@Entity
public class B extends C {
    String b;
}
Niezbyt ładne, ale skuteczne (klasa C jest abstrakcyjna aby nikomu nie przyszło do głowy tworzenie jej instancji).
Zgłoszenia w Hibernate'owej JIRZe(*) związane z tym błędem nie są przypisane do żadnej konkretnej wersji, więc będą poprawione w bliżej nieokreślonej przyszłości. A najstarsze z nich zostało zarejestrowane 20. czerwca 2006 roku.
(*) tak swoją drogą, jak należy odmieniać nazwę JIRA?

2 komentarze

bnt pisze...

W miarę na temat napiszę, że "encja" to ohydne słowo, nie? Na pewno brzydsze niż jira.

Szczepan Kuźniarz pisze...

Niezbyt fajne, ale dość typowe i ze wstydem przyznaję, że mnie za bardzo nie razi.

Ale co do JIRY: jak się powinno odmieniać takie nazwy? To nie jest skrót (pochodzi podobno od gojiry), ale pisze się toto w całości dużymi literami. Czyli JIRZE, JIRZe czy jeszcze jakoś inaczej?

© The Useful Pot To Keep Things In
Maira Gall