составной первичный ключ hibernate содержит составной внешний ключ, как его сопоставить

Я искал там и не нашел похожей темы, поэтому я публикую новый вопрос.

Я работаю с Hibernate над существующей базой данных. Структуру таблицы и данные мы не можем изменить. Приложение считывает данные из базы данных и мигрирует в другое хранилище данных на основе некоторой логики.

Теперь проблема заключается в составном отображении ПК. например

В таблице А есть составной ПК.

Table A
--------
a1 (pk)
a2 (pk)
a3 (pk)
a4 (pk)
foo
bar
========

Таблица B также имеет составной ПК, и одна часть этого составного ПК является ПК А, здесь также работает как FK.

Table B
--------
a1 (fk,pk)
a2 (fk,pk)
a3 (fk,pk)
a4 (fk,pk)
b1 (pk)
b2 (pk)
b3 (pk)
foo
bar
========

Я пробовал несколько способов, и ни один из них не работает. Может ли кто-нибудь сказать рабочее решение для сопоставления Hibernate? лучше в стиле аннотации.


person Kent    schedule 03.02.2010    source источник


Ответы (2)


Настройте объект сущности A как @ManyToOne в классе pk B.

Итак, если у вас есть

Class A
Class APK - A's Primary Key

Class B
Class BPK - B's primary Key.

BPK будет содержать A в качестве атрибута

@Embeddable
public class BPK implements serializable {
  ....
  private A a;

  @ManyToOne(fetch=FetchType.EAGER)
  @JoinColumns ({
    @JoinColumn(name="...", referencedColumnName = "..."),
    @JoinColumn(name="...", referencedColumnName = "..."),
    ...
  })
  public getA() {
    return this.a;
  }
}

Из документации.

@Embeddable наследует тип доступа объекта-владельца, если только не используется аннотация @AccessType, специфичная для Hibernate. Составные внешние ключи (если не используются конфиденциальные значения по умолчанию) определяются в ассоциациях с использованием элемента @JoinColumns, который в основном представляет собой массив @JoinColumn. Считается хорошей практикой явно указывать referencedColumnNames. В противном случае Hibernate будет считать, что вы используете тот же порядок столбцов, что и в объявлении первичного ключа.

person Vinodh Ramasubramanian    schedule 03.02.2010
comment
@Vinodh Ramasubramania Привет, не рекомендуется использовать Entity в качестве первичного ключа. Команда Hibernate рекомендует. Вы не можете ограничить результат запроса в HQL или критериях для объекта, используемого в качестве первичного ключа. Таким образом, есть много способов избежать его использования. - person Arthur Ronald; 03.02.2010
comment
Но я не использую Entity в качестве PK. Я использую его (A) для установления отношения ManyToOne с условиями соединения в PK B. В вашем решении я не вижу, как Composite PK of B (BPK) определяется с Composite PK A (APK) как подмножество к нему. - person Vinodh Ramasubramanian; 03.02.2010
comment
OneToMany в вашем классе BPK мне помогает. Я не думал в этом направлении. Я просто продолжал думать, как сослаться на другой класс APK в BPK. И не знаю, как установить аннотацию. Для удобного a.getBset() я смоделировал еще один One2Many с объединением столбцов в классе A. например частный набор‹TypeB› bSet;. в классе TypeB, а также manyToOne, указывающий на TypeA. конечно нужны вставляемые, обновляемые = ложные. Спасибо вам, ребята. - person Kent; 04.02.2010

Если в составном первичном ключе есть только суррогатные ключи, используйте @EmbeddableId.

@Embeddable
public class CompoundIdA implements Serializable {

    private Integer field0;
    private Integer field1;
    private Integer field2;
    private Integer field3;

    @Column(name="FIELD_0")
    public Integer getField0() {
        return this.field0;
    }

    @Column(name="FIELD_1")
    public Integer getField1() {
        return this.field1;
    }

    @Column(name="FIELD_2")
    public Integer getField2() {
        return this.field2;
    }

    @Column(name="FIELD_3")
    public Integer getField3() {
        return this.field3;
    }

    public boolean equals(Object o) {
        if(o == null)
            return false;

        if(!(o instanceof CompoundIdA))
            return false;

        final CompoundIdA other = (CompoundIdA) o;
        if(!(getField0().equals(other.getField0()))
            return false;

        if(!(getField1().equals(other.getField1()))
            return false;

        if(!(getField2().equals(other.getField2()))
            return false;

        if(!(getField2().equals(other.getField2()))
            return false;

        return true;
    }

    // hashcode impl

}

В классе А у нас есть

@Entity
public class ClassA {

    private CompoundIdA compoundIdA;

    @EmbeddedId
    public CompoundIdA getCompoundIdA() {
        return this.CompoundIdA;
    }

}

Если в составном первичном ключе есть как естественный, так и суррогатный ключи, снова используйте @EmbeddableId.

// Let's suppose field0 and field1 are both natural keys
@Entity
public class ClassA {

    private CompoundIdA compoundIdA;

    private Integer field0;
    private Integer field1;

    @EmbeddedId
    public CompoundIdA getCompoundIdA() {
        return this.CompoundIdA;
    }

    @Column(name="FIELD_0", insertable=false, updateable=false)
    public Integer getField0() {
        return this.field0;
    }

    @Column(name="FIELD_1", insertable=false, updateable=false)
    public Integer getField1() {
        return this.field1;
    }

}

Обратите внимание, вы должны настроить вставки = false и updateable = false, потому что один и тот же столбец используется более чем одним свойством. В противном случае Hibernate будет сообщать об ошибках.

Если ваш составной первичный ключ содержит только натуральные ключи, используйте @IdClass.

@Entity
@IdClass(CompoundIdA.class)
public class ClassA {

    private Integer field0;
    private Integer field1;
    private Integer field2;
    private Integer field3;

    @Id
    @Column(name="FIELD_0")
    public Integer getField0() {
        return this.field0;
    }

    @Id
    @Column(name="FIELD_1")
    public Integer getField1() {
        return this.field1;
    }

    @Id
    @Column(name="FIELD_2")
    public Integer getField2() {
        return this.field2;
    }

    @Id
    @Column(name="FIELD_3")
    public Integer getField3() {
        return this.field3;
    }

}

В ClassB вы можете использовать тот же подход, что и показанный выше, но если вы хотите определить свойство @ManyToOne, вы должны настроить insertable=false и updateable=false следующее

@Entity
public class ClassB {

    private ClassA classA;

    @ManyToOne
    @JoinColumns ({
        @JoinColumn(name="FIELD_0", referencedColumnName="FIELD_0", insertable=false, updateable=false),
        @JoinColumn(name="FIELD_1", referencedColumnName="FIELD_1", insertable=false, updateable=false),
        @JoinColumn(name="FIELD_2", referencedColumnName="FIELD_2", insertable=false, updateable=false),
        @JoinColumn(name="FIELD_3", referencedColumnName="FIELD_3", insertable=false, updateable=false)
    })
    public ClassA getClassA() {
        return this.classA;
    }

}

С уважением,

person Arthur Ronald    schedule 03.02.2010
comment
Я действительно не вижу большой разницы между вашим ответом и моим. За исключением того, что вы использовали альтернативные аннотации для тех же вещей, которые я изложил. - person Vinodh Ramasubramanian; 03.02.2010
comment
как мне написать ClassB.hbm.xml для того же? - person tumbudu; 03.01.2013