Должен ли Hibernate обрабатывать перекрывающиеся внешние ключи?

У меня есть таблица с двумя внешними ключами для двух разных таблиц с обоими внешними ключами, использующими один столбец:

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)
)

Как видите, есть два FK, совместно использующих код страны (по совпадению ссылающиеся на один и тот же столбец в конце пути ссылки). Класс сущности выглядит так (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;

    ...
}

Как видите, я пометил свойство countryCode и country_code города @JoinColumn как доступные только для чтения (insertable = false, updatable = false). Hibernate терпит неудачу со следующим высказыванием:

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

Честно говоря, мне это кажется довольно простым. «Смешивание вставляемых и невставляемых столбцов в свойстве запрещено» - такое слабое «оправдание», не так ли?

Если Hibernate сможет справиться с этим, например согласно спецификации JPA? Это ошибка?


person Kawu    schedule 20.11.2010    source источник
comment
hibernate не любит составные первичные ключи - в некоторых ситуациях, подобных этой, он портится. попробуйте реорганизовать базу данных, чтобы использовать первичные ключи с одним столбцом.   -  person Andreas Petersson    schedule 15.05.2011
comment
Да, Hibernate - отстой с составными клавишами. В любом случае об этом никогда не сообщалось, теперь это: opensource.atlassian. ru / projects / hibernate / browse / HHH-6221   -  person Kawu    schedule 16.05.2011


Ответы (4)


Есть способ обойти проверку и заставить ее работать, указав, таким образом, столбец «@JoinColumnsOrFormulas», а затем введите решение:

Ошибка:

@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;

С уважением,

person Manu Navarro    schedule 30.10.2012
comment
Я считаю, что это должен быть принятый ответ, потому что он предоставляет рабочее решение прямо сейчас, а не где-то в будущем, когда hibernate 5 станет GA. - person azerole; 28.01.2013
comment
@ManuNavarro Почему в хорошей выборке столбцов с недвижимостью больше, чем в плохой? - person banterCZ; 30.01.2013
comment
Здравствуйте @banterCZ, переместитесь влево и посмотрите третье соотношение - person Manu Navarro; 01.02.2013
comment
В моем случае я получаю невозможный переход от формулы к столбцу. Формула фактически будет обеспечивать состояние только для чтения, но, похоже, здесь она не работает. - person Ben; 22.07.2016
comment
Из того, что я прочитал, является то, что ClassCastException из формулы в столбец не работает, если формула используется в столбце @Id. Формула приведет к тому, что он будет явно не вставляемым, в то время как Hibernate хочет, чтобы они были вставляемыми: stackoverflow.com/questions/19519781/ - person Ben; 25.07.2016

Будет поддерживаться Hibernate 5, см. https://hibernate.atlassian.net/browse/HHH-6221

person Kawu    schedule 05.03.2012
comment
Похоже, это было перемещено в Hibernate 6. - person Daniel Scott; 22.12.2015
comment
Вот проблема на странице проблем с атласным спящим режимом: hibernate.atlassian.net/browse/HHH-6221 - person Ben; 25.07.2016
comment
Создано в 2011 году. Обновлено в конце 2017 года. Что делает эту функцию такой сложной для включения? - person Jordan Mackie; 03.08.2018

Это все еще не решено в Hibernate 5. Однако, если я использую @JoinColumnsOrFormulas, я получаю ClassCastException. Добавление insertable = false, updatable = false ко всем столбцам соединения решило мою проблему:

Пример:

@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;
person suomi-dev    schedule 15.11.2017

Я использую hibernate 5, и это все еще исключение. Если вы добавите insert = "false", update = "false" только к одному, вы получите исключение, в котором будет указано, что вы смешали вставляемые и невставляемые столбцы и что это недопустимо. Это проблема, которая уже есть в трекере, но, похоже, не решена. Hibernate генерирует исключение AnnotationException в столбце, используемом несколькими перекрывающимися внешними ключами

В нашем случае это означало, что мы перешли на EclipseLink, что на самом деле довольно просто, учитывая, что вам в основном нужно заменить persistence.xml и переписать HSQL (Hibernate SQL) на JPQL (JPA SQL). Также вам может потребоваться заменить собственные стратегии именования (Eclipse называет их SessionCustomizer). Конечно, это может быть труднее сделать, если вы используете специальные функции спящего режима, такие как поиск в спящем режиме и т. Д. Но в нашем случае мы пытались исправить перекрывающиеся внешние ключи в течение нескольких недель, в то время как в конце миграция заняла всего несколько часов.

person Ben    schedule 07.07.2016