JPA OneToOne ассоциация, где 2 сущности используют составные первичные ключи, но используют разные имена столбцов? - PullRequest
2 голосов
/ 07 января 2011

Мы пытаемся использовать Hibernate с базой данных, которая использует lot составных ключей, и это вызывает у нас много головной боли. К сожалению, мы не можем изменить схему, поэтому мы должны сделать много дополнительных сопоставлений между нашими полями. Мы ограничены в использовании JPA 1.0 и Hibernate 3.3.

Самая большая проблема до сих пор мы имели дело с взаимно-однозначной связью между двумя сущностями, используя составной ключ из 2 значений, где таблицы имеют разные имена для этих столбцов (БД имеет соглашение об именах, содержащее префикс для каждой таблицы в каждом столбце.)

Всякий раз, когда мы выполняем наш запрос, мы получаем это исключение:

Caused by: org.hibernate.TypeMismatchException Provided id of the wrong type for class com.business.entity.InvestorIssuerEmailEntity.  
Expected: class com.business.entity.InvestorIssuerEmailEntityPK, got class com.business.entity.InvestorIssuerEntityPK; 

Два класса для этих таблиц, InvestorIssuerEntity и InvestorIssuerEmailEntity, имеют необязательную связь @OneToOne (в некоторых случаях у InvestorIssuer нет соответствующей записи в InvestorIssuerEmail):

@IdClass(InvestorIssuerEntityPK.class)
@Table(name = "T090_INVESTOR_ISSUER")
@Entity
InvestorIssuerEntity
    @Column(name = "T090_091_INVESTOR_ID", nullable = false, insertable = true, 
updatable = true, length = 18, precision = 0)
    @Id
    private Long investorId;

    @Column(name = "T090_102_ISSUER_ID", nullable = false, insertable = true, 
updatable = true, length = 18, precision = 0)
    @Id
    private Long issuerId;

(other fields omitted)

    @OneToOne(optional = true)
    @JoinColumns(value = {
            @JoinColumn(name="T090_091_INVESTOR_ID", referencedColumnName = "T284_INVESTOR_ID", nullable = false, insertable = false, updatable = false),
            @JoinColumn(name = "T090_102_ISSUER_ID", referencedColumnName = "T284_ISSUER_ID", nullable = false, insertable = false, updatable = false)
    })
    @NotFound(action = NotFoundAction.IGNORE)
    private InvestorIssuerEmailEntity investorIssuerEmail;

... 

InvestorIssuerEntityPK 
    @Id
    @Column(name = "T090_091_INVESTOR_ID", nullable = false, insertable = true, 
updatable = true, length = 18, precision = 0)
    private Long investorId;

    @Id
    @Column(name = "T090_102_ISSUER_ID", nullable = false, insertable = true, 
updatable = true, length = 18, precision = 0)
    private Long issuerId;

...

@IdClass(InvestorIssuerEmailEntityPK.class)
@Table(name = "T284_INVESTOR_ISSUER_EMAIL")
@Entity
InvestorIssuerEmailEntity
    @Column(name = "T284_INVESTOR_ID", nullable = false, insertable = true, 
updatable = true, length = 18, precision = 0)
    @Id
    private Long investorId;

    @Column(name = "T284_ISSUER_ID", nullable = false, insertable = true, 
updatable = true, length = 18, precision = 0)
    @Id
    private Long issuerId;

...

InvestorIssuerEmailEntityPK 

    @Column(name = "T284_INVESTOR_ID", nullable = false, insertable = true, 
updatable = true, length = 18, precision = 0)
    @Id
    private Long investorId;

    @Column(name = "T284_ISSUER_ID", nullable = false, insertable = true, 
updatable = true, length = 18, precision = 0)
    @Id
    private Long issuerId;  

Я пытался обойти проблему несоответствия типов, используя тот же класс, что и @EmbeddableId для двух сущностей, а затем используя @AttributeOverrides, например:

@Id
@EmbeddedId
@AttributeOverrides({
        @AttributeOverride(name = "investorId",
                column = @Column(name = "T284_INVESTOR_ID", nullable = false, insertable = true, updatable = true, length = 18, precision = 0)),
        @AttributeOverride(name = "issuerId",
                column = @Column(name = "T284_ISSUER_ID", nullable = false, insertable = true, updatable = true, length = 18, precision = 0))
})
private InvestorIssuerId investorIssuerId;

Я сделал изменения только для этих двух сущностей, все еще используя подход @IdClass для других сущностей (это только случай использования @IdClass ИЛИ @EmbeddableId для ваших сущностей, а не для обеих?)

В итоге у нас возникли другие проблемы, такие как «Повторяющийся столбец в сопоставлении для сущности», поэтому мы вернулись к этому подходу, чтобы посмотреть, есть ли другие обходные пути для этого.

У кого-нибудь есть какие-нибудь решения, чтобы обойти эту проблему? Я просмотрел StackOverflow, но не сталкивался ни с одним случаем, когда составные ключи, используемые в ассоциации, имеют разные имена.

Примечание: Даже после попытки предложенного ниже предложения мы все равно получаем эту ошибку: org.hibernate.MappingException: повторяющийся столбец в сопоставлении для сущности: com.business.entity.InvestorIssuerEntity: T090_091_INVESTOR_ID (должен отображаться с помощью insert = "false" update = "false ")

Я даже удалил ВСЕ ассоциации из InvestorIssuerEntity, и все еще получил ту же проблему. Ошибка исчезла только тогда, когда я удалил аннотацию @Column в классе составного ключа. Конечно, запросы не работали, потому что инвестор не был сопоставлен! Я не понимаю , где Hibernate находил «Повторяющийся столбец в отображении», поскольку я уже удалил все упоминания о T090_091_INVESTOR_ID, кроме составного ключа.

У нас есть другие ассоциации в InvestorIssuerEntity, которые выполняют объединение с теми же первичными ключами, но связанные сущности также имеют дополнительные столбцы в своих составных ключах. Как только вы используете @EmbeddedId, вы должны использовать их для всех сущностей? Мы все еще используем @IdClass для других классов. Но тогда как это повлечет за собой «повторяющуюся колонку»?

Ответы [ 2 ]

5 голосов
/ 07 января 2011

Похоже, у меня есть рабочее решение для вашего случая:

@Entity
public class InvestorIssuerEntity {
    @EmbeddedId 
    private InvestorIssuerEntityPK investorIssuerEntityPK;

    @OneToOne(optional=true, mappedBy="investorIssuerEntity")
    private InvestorIssuerEmailEntity investorIssuerEmailEntity;
}

@Entity
public class InvestorIssuerEmailEntity {
    @EmbeddedId @AttributeOverrides({
        @AttributeOverride(name="investorId", column=@Column(name="T02_INV_ID")),
        @AttributeOverride(name="issuerId", column=@Column(name="T02_ISS_ID"))
    })
    private InvestorIssuerEntityPK investorIssuerEntityPK;

    @OneToOne(optional=true) @PrimaryKeyJoinColumns({
        @PrimaryKeyJoinColumn(name="T02_ISS_ID", referencedColumnName="T01_ISS_ID"), 
        @PrimaryKeyJoinColumn(name="T02_INV_ID", referencedColumnName="T01_INV_ID")
    })
    private InvestorIssuerEntity investorIssuerEntity;
}

@Embeddable
public class InvestorIssuerEntityPK implements Serializable {
    private static final long serialVersionUID = -1176248537673293674L;

    @Column(name="T01_INV_ID")
    private Long investorId;

    @Column(name="T01_ISS_ID")
    private Long issuerId;
}

Он генерирует следующий DDL, который, кажется, то, что вы ищете:

create table InvestorIssuerEmailEntity (
    T02_INV_ID bigint not null,
    T02_ISS_ID bigint not null,
    primary key (T02_INV_ID, T02_ISS_ID)
)

create table InvestorIssuerEntity (
    T01_INV_ID bigint not null,
    T01_ISS_ID bigint not null,
    primary key (T01_INV_ID, T01_ISS_ID)
)

alter table InvestorIssuerEmailEntity 
    add constraint FKC2FBCC4E1E26612E 
    foreign key (T02_INV_ID, T02_ISS_ID) 
    references InvestorIssuerEntity
0 голосов
/ 23 января 2019

Я хочу упомянуть альтернативный подход:

  • pro: обе сущности могут использовать свой собственный класс PK
  • con: отношение 1: 1 проявляется в множестве с 1 записью

Вы можете переключиться на отношение @OneToMany и сохранить связанный объект в наборе.В наборе будет 1 запись, которую можно распаковать на этапе последующей обработки.

@OneToMany
@JoinColumns(value = {
        @JoinColumn(name="T090_091_INVESTOR_ID", referencedColumnName = "T284_INVESTOR_ID", nullable = false, insertable = false, updatable = false),
        @JoinColumn(name = "T090_102_ISSUER_ID", referencedColumnName = "T284_ISSUER_ID", nullable = false, insertable = false, updatable = false)
})
@NotFound(action = NotFoundAction.IGNORE)
private Set<InvestorIssuerEmailEntity> investorIssuerEmails;
...