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
@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 ofMessage2
it is delayed beyond thesave
andcreated
remainsnull
. 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.