I've got an unexpected behavior while I have been trying to persist entity with id
strategy = GenerationType.SEQUENCE
and existing column marked as @CreationTimestamp.
Assumption: there are two models Message1 and Message2 defined as follows:
@Entity
@Getter
@Setter
public class Message1 {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", updatable = false)
private Long id;
@Column(name = "payload", columnDefinition = "text")
private String payload;
@Column(name = "created", columnDefinition = "timestamp with time zone")
@CreationTimestamp
private ZonedDateTime created;
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Message1 message1 = (Message1) o;
return Objects.equals(id, message1.id);
}
@Override
public int hashCode() {
return Objects.hash(41);
}
}
and
@Entity
@Getter
@Setter
public class Message2 {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@Column(name = "id", updatable = false)
private Long id;
@Column(name = "payload", columnDefinition = "text")
private String payload;
@Column(name = "created", columnDefinition = "timestamp with time zone")
@CreationTimestamp
private ZonedDateTime created;
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Message2 message2 = (Message2) o;
return id == message2.id;
}
@Override
public int hashCode() {
return Objects.hash(37);
}
}
The only difference between those two models is the definition of the id generation strategy:
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", updatable = false)
private Long id;
for the first model and
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@Column(name = "id", updatable = false)
private Long id;
for the second.
And following tests behaves completely different for the cases on not null assertions on the @CreationTimestamp marked attributes. Test for the Message1 model passes
@Test
@Transactional
void message1SaveCreatedTest() {
Message1 message1 = new Message1();
message1.setPayload("message12");
message1 = message1Repository.save(message1);
Assertions.assertNotNull(message1.getCreated());
}
and test for the Model2 fails
@Test
@Transactional
void message2SaveCreatedTest() {
Message2 message2 = new Message2();
message2.setPayload("message2");
message2 = message2Repository.save(message2);
Assertions.assertNotNull(message2.getCreated());
}
Application code with configurations and tests you may find here https://github.com/rgordeev/creationtimestamp
Comment From: wilkinsona
@CreationTimestamp isn't honoured until the entity is first inserted into the database and the different generated value strategies change when this first insert occurs. In the case of Message2 it is delayed beyond the save and created remains null. You can flush the repository to trigger the insert:
@Test
@Transactional
void message2SaveCreatedTest() {
Message2 message2 = new Message2();
message2.setPayload("message2");
message2 = message2Repository.saveAndFlush(message2);
Assertions.assertNotNull(message2.getCreated());
}
Your test will now pass.
Comment From: rgordeev
@CreationTimestampisn't honoured until the entity is first inserted into the database and the different generated value strategies change when this first insert occurs. In the case ofMessage2it is delayed beyond thesaveandcreatedremainsnull. You can flush the repository to trigger the insert:```java @Test @Transactional void message2SaveCreatedTest() { Message2 message2 = new Message2(); message2.setPayload("message2");
message2 = message2Repository.saveAndFlush(message2); Assertions.assertNotNull(message2.getCreated());} ```
Your test will now pass.
@wilkinsona Could you please explain how @GeneratedValue(strategy influence on insertion to the database?
Comment From: wilkinsona
That's really a Hibernate question. https://thorben-janssen.com/jpa-generate-primary-keys/ contains quite a good explanation.
If you have any further questions, please follow up on Stack Overflow or Gitter. As mentioned in the guidelines for contributing, we prefer to use GitHub issues only for bugs and enhancements.