Должен ли Hibernate обрабатывать перекрывающиеся внешние ключи? - PullRequest
10 голосов
/ 20 ноября 2010

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

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 и код страны @_JoinColumn только для чтения (inserttable = 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? Это ошибка?

Ответы [ 4 ]

22 голосов
/ 31 октября 2012

Существует способ обойти проверку и заставить ее работать, указав, таким образом, столбец как «@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;

С уважением,

10 голосов
/ 05 марта 2012

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

2 голосов
/ 07 июля 2016

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

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

1 голос
/ 15 ноября 2017

Это все еще не решено в 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;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...