Составной внешний ключ JPA TypeMismatchException

Я объяснил свой сценарий с помощью простых таблиц Parent Child.

Мой составной первичный ключ также является составным внешним ключом, ссылающимся на родительскую таблицу.

create table parent(
code varchar(10) not null,
id int not null,
parentcol varchar(10),
primary key(code,id)
);

create table child(
code varchar(10) not null,
id int not null,
childcol varchar(10) not null,
primary key(code, id),
foreign key(code, id) references parent(code,id)
);

Созданные объекты (с помощью плагина Eclipse JPA)

@Entity
@Table(name="parent")
@NamedQuery(name="Parent.findAll", query="SELECT p FROM Parent p")
public class Parent implements Serializable {
private static final long serialVersionUID = 1L;

@EmbeddedId
private ParentPK id;

@Column(length=10)
private String parentcol;

//bi-directional one-to-one association to Child
@OneToOne(mappedBy="parent")
private Child child;

public Parent() {
}

/* getters and setters */

}

@Embeddable
public class ParentPK implements Serializable {
//default serial version id, required for serializable classes.
private static final long serialVersionUID = 1L;

@Column(unique=true, nullable=false, length=10)
private String code;

@Column(unique=true, nullable=false)
private int id;

    /* getters and setters */

/** Overridden equals and hashcode **/
}



@Entity
@Table(name="child")
@NamedQuery(name="Child.findAll", query="SELECT c FROM Child c")
public class Child implements Serializable {
private static final long serialVersionUID = 1L;

@EmbeddedId
private ChildPK id;

@Column(nullable=false, length=10)
private String childcol;

//bi-directional one-to-one association to Parent
@OneToOne
@JoinColumns({
    @JoinColumn(name="code", referencedColumnName="code", nullable=false, insertable=false, updatable=false),
    @JoinColumn(name="id", referencedColumnName="id", nullable=false, insertable=false, updatable=false)
    })
private Parent parent;

    /* getters and setters */

}

@Embeddable
public class ChildPK implements Serializable {
//default serial version id, required for serializable classes.
private static final long serialVersionUID = 1L;

@Column(insertable=false, updatable=false, unique=true, nullable=false, length=10)
private String code;

@Column(insertable=false, updatable=false, unique=true, nullable=false)
private int id;

/* overridden equals and hashcode */

Я использую данные Spring для сохранения своих объектов, как показано ниже. Родительская таблица состоит из записи с кодом «код» и идентификатором 1.

Child child = new Child();
ChildPK childPK = new ChildPK();
childPK.setCode("code");
childPK.setId(1);
child.setId(childPK);
child.setChildcol("child1");
childRepository.save(child);

Это удается с 1-го запуска, когда он должен вставить новую запись. Но проблема возникает при втором запуске, когда необходимо обновить, скажем, с помощью

child.setChildcol("child2");

я столкнулся с ошибкой

HHH000327: Error performing load command : org.hibernate.TypeMismatchException: Provided id of the wrong type for class com.xebia.eTechLog.entities.Parent. Expected: class com.xebia.eTechLog.entities.ParentPK, got class com.xebia.eTechLog.entities.ChildPK

Если я попытаюсь указать ссылку ParentPk в дочерней таблице как

@Entity
@Table(name="child")
@NamedQuery(name="Child.findAll", query="SELECT c FROM Child c")
public class Child implements Serializable {
private static final long serialVersionUID = 1L;

@EmbeddedId
private ParentPK id;

@Column(nullable=false, length=10)
private String childcol;

//bi-directional one-to-one association to Parent
@OneToOne
@JoinColumns({
    @JoinColumn(name="code", referencedColumnName="code", nullable=false, insertable=false, updatable=false),
    @JoinColumn(name="id", referencedColumnName="id", nullable=false, insertable=false, updatable=false)
    })
private Parent parent;

Это работает, но не будет, если в родительском классе будет больше полей, что является моим реальным сценарием.


person bitscanbyte    schedule 24.10.2016    source источник


Ответы (1)


Вы должны использовать производную личность. Это означает, что вы должны указать, что ссылка дочернего элемента на его родителя сопоставляет идентификатор дочернего элемента (с аннотацией @MapsId):

@Entity
public class Child implements Serializable {
    @EmbeddedId
    private ChildPK id;

    @Column(nullable=false, length=10)
    private String childcol;

    @OneToOne
    @MapsId  // <<< NB
    @JoinColumns({
        @JoinColumn(name="code", referencedColumnName="code"),
        @JoinColumn(name="id", referencedColumnName="id")
    })
    private Parent parent;

    ...
}

Производные идентификаторы обсуждаются в спецификации JPA 2.1 в разделе 2.4.1.

person Brian Vosburgh    schedule 24.10.2016
comment
Спасибо, Брайан. Я смог найти ответ раньше, и он вполне соответствует тому, что вы упомянули. Вам нужно использовать @MapsId для части составного первичного ключа, которая сопоставляется с родительской таблицей. - person bitscanbyte; 25.10.2016