Reputation: 73
I have the following 'Parent':
@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
...
}
And the recipient 'Child':
@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(); }
}
With the composite key:
@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);
}
}
I have the following test code:
@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);
}
When executing makeMessage() I can see in the debug output that Message gets a generated MessageId value, but when persisting Recipients, the messageId of the composite key is null, causing an failure of the transaction.
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
I've exhausted about every possibility I can think of for trying to get this to work, including @IdClass, but its always null. From everything I can find online, this should work. I found one or two similar SO questions, but none addressed the specifics of a Composite Key where one of the composite fields is the parents Id.
I figure I can work around this by persisting the Message and the Recipients in two separate states (persist Message, get the ID and then create and persist the Recipients) but from what I know, this should work...
Upvotes: 1
Views: 1681
Reputation: 587
That is because you need to map the message_id
into the class RecipientId
with @MapsId("messageId")
you can do the following:
@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(); }
}
Upvotes: 1