Fix the Error of Null Value for Field With @Creationtimestamp
Background
In a Spring Boot project, an entity class has a @CreationTimestamp annotation for the field of started. Since this field is essential for our business logic, there is a NotNull restriction in the database column for it.
This is the code for this entity (Review.java):
@Entity(name = "review")
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
@TypeDef(name = "pgsql_enum", typeClass = PostgreSQLEnumType.class)
@Getter
@Setter
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Review {
@Id
@EqualsAndHashCode.Include
private UUID id;
@NotNull
@Size(max = 255)
@Column(name = "description")
private String description;
@NotNull
@Enumerated(EnumType.STRING)
@Column(name = "state", nullable = false)
@Type(type = "pgsql_enum")
private ReviewState state;
@NotNull
@Enumerated(EnumType.STRING)
@Column(name = "outcome")
@Type(type = "pgsql_enum")
private ReviewOutcome outcome;
@CreationTimestamp
@Column(name = "started", columnDefinition = "TIMESTAMPTZ")
private LocalDateTime started;
@Column(name = "completed", columnDefinition = "TIMESTAMPTZ")
private LocalDateTime completed;
@UpdateTimestamp
@Column(name = "update_date_time", columnDefinition = "TIMESTAMPTZ")
private LocalDateTime updatedAt;
}
To persist the data of the Review domain, we create a repository class for it (ReviewRepository.java):
@Repository
public interface ReviewRepository extends JpaRepository<Review, UUID> {
}
Meanwhile, we have a service layer to create a review and save it in the database:
@Service
@RequiredArgsConstructor
@Slf4j
public class ReviewServiceImpl implements ReviewService{
private final ReviewRepository reviewRepository;
@Override
@Transactional
public ReviewResponseDTO createReview(ReviewDTO data){
// Review Entity creation
Review review = data.toEntity();
review.setId(UUID.randomUUID());
review.setState(ReviewState.OPEN);
review.setOutcome(ReviewOutcome.PENDING);
// Review entity persisting to db
Review dbReview = reviewRepository.saveAndFlush(review);
return dbReview.toResponseDto(Boolean.TRUE);
}
Error
However, when we do the testing of this, we got the error of this:
org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [started\" of relation \"review]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement] with root cause","context":"default","exception":"org.postgresql.util.PSQLException: ERROR: null value in column \"started\" of relation \"review\" violates not-null constraint\n Detail: Failing row contains (83aa2605-cc66-4029-854b-5f70a166c875, Review hold rules for client TEST, null, null, OPEN, PENDING, 2024-01-19 03:30:21.100613+00).\n\
From the logs, we can tell that the SQL statement which is generated by Hibernate, has a null value for the started field. This would be violated the not-null constraint. As a result, the review entity cannot persist to the database.
Solution
After some research, I’ve found this is caused by the Hibernate. We have to add @Column(updatable = false)
on to the started field to make it work. This additional annotation avoids the started to be updated by the SQL statement from Hibernate. So it would be executed successfully by ignoring the null value for this field.
The updated code for Review.java:
@Entity(name = "review")
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
@TypeDef(name = "pgsql_enum", typeClass = PostgreSQLEnumType.class)
@Getter
@Setter
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Review {
// ... [other fields remain unchanged]
@CreationTimestamp
@Column(name = "started", columnDefinition = "TIMESTAMPTZ", updatable = false)
private LocalDateTime started;
// ... [other fields remain unchanged]
}
With the updated version of entity class, the review entity can be persisted to the database without any issues.