Search code examples
javaspringdatetimevalidationentity

Supplying Java Clock to Entity Class for Spring Validation


I'm working on a spring-boot application. I have an Entity class called Message that I want to validate using Spring validation. One of the fields I need to validate is a LocalDateTime that must not be more than 6 days before the current date. My entity class looks like this:

@Entity
@EntityListeners(AuditingEntityListener.class)
public class Message implements Validator {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id", length = 100, nullable = false, unique = true)
    private long id;

    @Column(name = "start", length = 20, nullable = true)
    private LocalDateTime start;

    public Message() {}

    @Override
    public boolean supports(@NotNull Class<?> clazz) {
        return Message.class.equals(clazz);
    }

    @Override
    public void validate(Object target, Errors errors) {
        Message m = (Message) target;

        if(m.getStart() != null && m.getStart().isBefore(LocalDateTime.now(clock).minusDays(6)))
        {
            errors.rejectValue("start", "beforeMinStart");
        }
    }
}

The problem I'm running into is that I want to be able to supply a Clock to the validation so that I can use a fixed datetime for testing. I can't simply use a constructor; the Message could be coming from a database lookup. I would prefer to use dependency injection, but I'm not sure how that would work with an Entity class. Is there some way to do this that I'm just not seeing?


Solution

  • Separate out your Entity and Validator classes:

    @Entity
    @EntityListeners(AuditingEntityListener.class)
    public class Message {
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        @Column(name = "id", length = 100, nullable = false, unique = true)
        private long id;
    
        @Column(name = "start", length = 20, nullable = true)
        private LocalDateTime start;
    
        public Message() {}
    }
    
    public class MessageValidator implements Validator {
        @Autowired private Clock clock;
    
        @Override
        public boolean supports(@NotNull Class<?> clazz) {
            return Message.class.equals(clazz);
        }
    
        @Override
        public void validate(Object target, Errors errors) {
            Message m = (Message) target;
    
            if(m.getStart() != null && m.getStart().isBefore(LocalDateTime.now(clock).minusDays(6)))
            {
                errors.rejectValue("start", "beforeMinStart");
            }
        }
    }
    

    Now you can Autowire any dependency you want.