Contents

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.