2010-11-20 23 views
7

Mam tabeli, które ma dwa klucze obce do dwóch różnych tabel z obu kluczy obcych dzieląc jedną kolumnę:Czy funkcja Hibernate powinna obsługiwać nakładające się klucze obce?

CREATE TABLE ZipAreas 
(
    country_code CHAR(2) NOT NULL, 
    zip_code VARCHAR(10) NOT NULL, 
    state_code VARCHAR(5) NOT NULL, 
    city_name VARCHAR(100) NOT NULL, 
    PRIMARY KEY (country_code, zip_code, state_code, city_name), 
    FOREIGN KEY (country_code, zip_code) REFERENCES Zips (country_code, code), 
    FOREIGN KEY (country_code, state_code, city_name) REFERENCES Cities (country_code, state_code, name) 
) 

Jak widać, istnieją dwa FKS dzielące COUNTRY_CODE (przypadkowo przedstawieniu tej samej kolumny w koniec ścieżki referencyjnej). Klasa podmiot wygląda (JPA 1.0 @IdClass):

@Entity 
@Table(name = "ZipAreas") 
@IdClass(value = ZipAreaId.class) 
public class ZipArea implements Serializable 
{ 
    @Id 
    @Column(name = "country_code", insertable = false, updatable = false) 
    private String countryCode; 

    @Id 
    @Column(name = "zip_code", insertable = false, updatable = false) 
    private String zipCode; 

    @Id 
    @Column(name = "state_code", insertable = false, updatable = false) 
    private String stateCode; 

    @Id 
    @Column(name = "city_name", insertable = false, updatable = false) 
    private String cityName; 

    @ManyToOne 
    @JoinColumns(value = {@JoinColumn(name = "country_code", referencedColumnName = "country_code"), @JoinColumn(name = "zip_code", referencedColumnName = "code")}) 
    private Zip zip = null; 

    @ManyToOne 
    @JoinColumns(value = {@JoinColumn(name = "country_code", referencedColumnName = "country_code", insertable = false, updatable = false), @JoinColumn(name = "state_code", referencedColumnName = "state_code"), @JoinColumn(name = "city_name", referencedColumnName = "name")}) 
    private City city = null; 

    ... 
} 

Jak widać ja oflagowany właściwość CountryCode i kod_kraju @JoinColumn miasta jako tylko do odczytu (do wkładania = false, aktualizowalny = false). Hibernacja kończy się niepowodzeniem z następującym powiedzeniem:

Exception in thread "main" javax.persistence.PersistenceException: [PersistenceUnit: geoinfo] Unable to configure EntityManagerFactory 
    at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:374) 
    at org.hibernate.ejb.HibernatePersistence.createEntityManagerFactory(HibernatePersistence.java:56) 
    at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:48) 
    at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:32) 
    at tld.geoinfo.Main.main(Main.java:27) 
Caused by: org.hibernate.AnnotationException: Mixing insertable and non insertable columns in a property is not allowed: tld.geoinfo.model.ZipAreacity 
    at org.hibernate.cfg.Ejb3Column.checkPropertyConsistency(Ejb3Column.java:563) 
    at org.hibernate.cfg.AnnotationBinder.bindManyToOne(AnnotationBinder.java:2703) 
    at org.hibernate.cfg.AnnotationBinder.processElementAnnotations(AnnotationBinder.java:1600) 
    at org.hibernate.cfg.AnnotationBinder.processIdPropertiesIfNotAlready(AnnotationBinder.java:796) 
    at org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:707) 
    at org.hibernate.cfg.Configuration$MetadataSourceQueue.processAnnotatedClassesQueue(Configuration.java:3977) 
    at org.hibernate.cfg.Configuration$MetadataSourceQueue.processMetadata(Configuration.java:3931) 
    at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1368) 
    at org.hibernate.cfg.Configuration.buildMappings(Configuration.java:1345) 
    at org.hibernate.ejb.Ejb3Configuration.buildMappings(Ejb3Configuration.java:1477) 
    at org.hibernate.ejb.EventListenerConfigurator.configure(EventListenerConfigurator.java:193) 
    at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:1096) 
    at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:278) 
    at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:362) 
    ... 4 more 

Wygląda to całkiem podstawowo dla mnie szczerze. "Mieszanie kolumn, które można wstawiać i których nie można wstawiać w nieruchomości jest niedozwolone" jest tak słabym "wymówką", nieprawdaż?

Czy Hibernate może sobie z tym poradzić, np. zgodnie ze specyfikacją JPA? Czy to błąd?

+0

Oto JavaSE, HSQLDB, Ant aplikacja samodzielna: http://www.kawoolutions.com/media/geoinfo-hib-overlapping -fks.zip – Kawu

+0

Hibernacja nie podoba się kluczom pierwotnym złożonym - w niektórych sytuacjach, takich jak ten, zawodzi. spróbuj zmienić swoją bazę danych, aby używać podstawowych kluczy z jedną kolumną. –

+0

Tak Hibernacja zasysa klucze złożone. W każdym razie nie zostało to zgłoszone, teraz jest: http: //opensource.atlassian.com/projects/hibernate/browse/HHH-6221 – Kawu

Odpowiedz

9

będą wspierane z Hibernate 5, zobacz https://hibernate.atlassian.net/browse/HHH-6221

+2

Wygląda na to, że zostało to przeniesione do Hibernate 6. –

+0

Tutaj jest problem na stronie problemów dotyczących atlasu: hibernate: https://hibernate.atlassian.net/browse/HHH-6221 – Ben

18

Jest sposób na ominięcie sprawdzania i zmusić go do pracy, tym samym wskazując, kolumna jest „@JoinColumnsOrFormulas” następnie umieścić rozwiązanie:

błąd:

@ManyToOne 
@JoinColumns(value = { 
    @JoinColumn(name = "country_code", referencedColumnName = "country_code"), 
    @JoinColumn(name = "zip_code", referencedColumnName = "code")}) 
private Zip zip = null; 

@ManyToOne 
@JoinColumns(value = { 
    @JoinColumn(name = "country_code", referencedColumnName = "country_code", insertable = false, updatable = false), 
    @JoinColumn(name = "state_code", referencedColumnName = "state_code"), 
    @JoinColumn(name = "city_name", referencedColumnName = "name")}) 
private City city = null; 

OK:

@ManyToOne 
@JoinColumns(value = { 
    @JoinColumn(name = "country_code", referencedColumnName = "country_code"), 
    @JoinColumn(name = "zip_code", referencedColumnName = "code")}) 
private Zip zip = null; 

@ManyToOne 
@JoinColumnsOrFormulas(value = { 
    @JoinColumnOrFormula(formula = @JoinFormula(value = "country_code", referencedColumnName = "country_code")), 
    @JoinColumnOrFormula(column = @JoinColumn(name = "state_code", referencedColumnName = "state_code")), 
    @JoinColumnOrFormula(column = @JoinColumn(name = "city_name", referencedColumnName = "name")) 
}) 
private City city = null; 

Pozdrowienia,

+6

Uważam, że powinna to być akceptowana odpowiedź, ponieważ zapewnia ona działające rozwiązanie już teraz, a nie gdzieś w przyszłości, gdy hibernacja 5 to GA. – azerole

+0

@ManuNavarro Dlaczego na próbce jest więcej kolumn własności miasta niż na złej próbce? – banterCZ

+0

Witaj @banterCZ, przesuwa się w lewo i widzisz trzecią relację –

0

Używam hibernacji 5 i nadal mam wyjątek. Jeśli dodasz insert = "false", update = "false" tylko do jednego, otrzymasz wyjątek stwierdzający, że mieszałeś kolumny, które można wstawiać i których nie można wstawiać, i że jest to niedozwolone. Jest to problem, który już jest w trackerze, ale wydaje się, że nie zostanie rozwiązany. Hibernate throws AnnotationException on column used by multiple overlapping foreign keys

W naszym przypadku oznaczało to, że przeszliśmy na EclipseLink, co w rzeczywistości jest dość łatwe, biorąc pod uwagę, że musisz przede wszystkim wymienić persistence.xml i przepisać HSQL (Hibernate SQL) na JPQL (JPA SQL). Konieczne może być również zastąpienie niestandardowych strategii nazewnictwa (Eclipse nazywa je SessionCustomizer). Oczywiście może być trudniej zrobić, jeśli korzystasz ze specjalnych funkcji hibernacji, takich jak wyszukiwanie hibernacji itp. Jednak w naszym przypadku próbowaliśmy naprawić nakładające się klucze obce na tygodnie, kiedy migracja trwała tylko godziny.

0

To nadal nie jest rozwiązane w Hibernate 5. Jednak jeśli używam @JoinColumnsOrFormulas otrzymuję ClassCastException. Dołączanie insertable = false, updatable = false na wszystkich dołączyć kolumny rozwiązać mój problem:

Przykład:

@ManyToOne 
@JoinColumns(value = { 
    @JoinColumn(name = "country_code", referencedColumnName = "country_code", insertable = false, updatable = false), 
    @JoinColumn(name = "state_code", referencedColumnName = "state_code", insertable = false, updatable = false), 
    @JoinColumn(name = "city_name", referencedColumnName = "name", insertable = false, updatable = false)}) 
private City city = null;