Spring / JPA Сохранение однокомпонентного, дочернего составного ключа с ключом от родителя равно нулю - PullRequest
0 голосов
/ 28 ноября 2018

У меня есть следующий «Родитель»:

@Entity
@Table(name = "messages")
@SequenceGenerator(name = "messageSequence", sequenceName = "message_sequence")
public class Message {
    @Id
    @Column(name = "message_id")
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "messageSequence")
    Long messageId;

    @OneToMany(mappedBy = "message", cascade=CascadeType.ALL)
    List<Recipient> recipients = new ArrayList<>();

    public void addRecipient(Recipient r) {
        r.setMessage(this);
        recipients.add(r);
    }

    @Column(name = "message_body")
    String body;

    // Constructors/Getters/Setters
    ...
}

И получатель «Дочерний»:

@Entity
@Table(name = "message_recipients")
public class Recipient {
    @EmbeddedId
    private RecipientId recipientId;

    @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name = "message_id")
    private Message message;
    public void setMessage(Message msg) {
        this.message = msg;
    }

    public Recipient(Message msg, String name) {
        this.id = new RecipientId(msg, name);
        this.message = msg;
    }

    @Override
    public boolean equals(Object o) { ... }

    @Override
    public int hashCode(Object o) { return key.hashCode(); }
}

С составным ключом:

@Embeddable
public class RecipientId implements Serializable {
    @Column(name = "message_id")
    Long messageId;

    @Column(name = "message_to")
    String messageTo;

    @Column(name = "message_timestamp")
    LocalDateTime messageTimestamp;

    public RecipientId(Message msg, String messageTo) {
        this.messageId = msg.messageId;
        this.messageTo = messageTo;
        this.messageTimestamp = LocalDateTime.now();
    }

    @Override
    public boolean equals(Object o) { ... }

    @Override
    public int hashCode() {
        return Objects.hash(messageId, messageTo, messageTimestamp);
    }
}

У меня есть следующий тестовый код:

@Bean
public void makeMessage(MessageRepository messages) {
    Message newMsg = new Message();
    newMsg.addRecipient(new Recipient(newMsg, "recipientName"));
    newMsg.setBody("This is a test message");
    messages.save(newMsg);
}

При выполнении makeMessage () я вижу в выходных данных отладки, что Message получает сгенерированное значение MessageId, но при сохранении получателей messageId составного ключа имеет значение null,вызывая сбой транзакции.

2018-11-27 14:49:54.158 DEBUG 2791 --- [  restartedMain] org.hibernate.SQL                        : 
Hibernate: 
values
    nextval for amessage_sequence
2018-11-27 14:49:54.190 DEBUG 2791 --- [  restartedMain] org.hibernate.SQL                        : 
Hibernate: 
    insert 
    into
        messages
        (body, message_code) 
    values
        (?, ?)
2018-11-27 14:49:54.191 TRACE 2791 --- [  restartedMain] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [VARCHAR] - [This is a test message]
2018-11-27 14:49:54.193 TRACE 2791 --- [  restartedMain] o.h.type.descriptor.sql.BasicBinder      : binding parameter [2] as [BIGINT] - [2922079]

2018-11-27 14:49:54.196 DEBUG 2791 --- [  restartedMain] org.hibernate.SQL                        : 
Hibernate: 
    insert 
    into
        message_recipient
        (timestamp, message_id, message_to) 
    values
        (?, ?, ?)

2018-11-27 14:49:54.196 TRACE 2791 --- [  restartedMain] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [TIMESTAMP] - [2018-11-27 14:49:54.141]
2018-11-27 14:49:54.196 TRACE 2791 --- [  restartedMain] o.h.type.descriptor.sql.BasicBinder      : binding parameter [2] as [BIGINT] - [null]
2018-11-27 14:49:54.196 TRACE 2791 --- [  restartedMain] o.h.type.descriptor.sql.BasicBinder      : binding parameter [3] as [VARCHAR] - [RecipientName]
2018-11-27 14:49:54.199  WARN 2791 --- [  restartedMain] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: -407, SQLState: 23502
2018-11-27 14:49:54.199 ERROR 2791 --- [  restartedMain] o.h.engine.jdbc.spi.SqlExceptionHelper   : DB2 SQL Error: SQLCODE=-407, SQLSTATE=23502, SQLERRMC=TBSPACEID=7, TABLEID=21, COLNO=1, DRIVER=4.19.26

Я исчерпал все возможности, которые я могу придумать, чтобы попытаться заставить это работать, включая @IdClass, но всегда нулевой.Из всего, что я могу найти в Интернете, это должно работать.Я нашел один или два похожих SO вопроса, но ни один из них не касался специфики составного ключа, где одно из составных полей - это родительский идентификатор.

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

1 Ответ

0 голосов
/ 28 ноября 2018

Это потому, что вам нужно сопоставить message_id с классом RecipientId с @MapsId("messageId"), вы можете сделать следующее:

@Entity
@Table(name = "message_recipients")
public class Recipient {
    @EmbeddedId
    private RecipientId recipientId;

    @MapsId("messageId")
    @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name = "message_id")
    private Message message;
    public void setMessage(Message msg) {
        this.message = msg;
    }

    public Recipient(Message msg, String name) {
        this.id = new RecipientId(msg, name);
        this.message = msg;
    }

    @Override
    public boolean equals(Object o) { ... }

    @Override
    public int hashCode(Object o) { return key.hashCode(); }
}
...