2

There are a LOT of posts on S.O. an other sites related to performing Hibernate validation, catching exceptions, and so forth. However, all of them put me up against some very unideal design choices in the situation described below. This seems like such a common situation I can't believe there's not a clean solution.

  • I want to leverage Javax's (and Hibernate's) validation annotations,
  • I want to correct, or throw out, invalid objects,
  • I want to leverage javax's PrePersist, etc annotations to handle repetitive or time-based actions on my objects.

Using the three in concert doesn't seem to work.

What does one do?

To be concrete, I have lots (10,000+) objects, a small handful of which are always going to be invalid.

@Entity
public class myPojo implements Serializable {

  @Id
  @GeneratedValue
  private long id;

  @NotBlank
  private String anotherString;

  @NotNull
  @Temporal(TemporalType.TIMESTAMP)
  private Date createdAt;

  @NotNull
  @Temporal(TemporalType.TIMESTAMP)
  private Date updatedAt;  

  @NotNull
  @Temporal(TemporalType.TIMESTAMP)
  private Date postedDate;  

  @PrePersist
  protected void onPrePersist() {
    this.createdAt = new Date();
    this.updatedAt = this.createdAt;
    if (this.postedDate == null)
      this.postedDate = this.createdAt;
  }
}

I want to save this function, and catch any objects with constraint violations:

void saveResource(MyPojo myPojp) {
  try {
    session.saveOrUpdate(mypojo);
  }
  catch (final ConstraintViolationException ex) {
     System.err.println("Could not save " + myPojo + " because " +
        this.getValidationErrors(myPojo));
  }
}

Where getValidationErrors() returns a String of all the ConstraintViolations that are found by manually creating and using a validation factory.

The only other possibilities I know of are performing a single transaction around each save (which seems like a bad idea since if real exceptions do occur I don't want ANY of the items to persist) or perhaps writing a custom validator to be run manually before I call save that calls the prepersist hooks (maintainability nightmare!).

[1]: I know that's a crummy reference, but I've been doing so much reading I can't find it again. Would be happy for someone to clarify in more technical details what errors cause a transaction to become invalid vs are recoverable.

Community
  • 1
  • 1
milletron
  • 370
  • 1
  • 5
  • 11

2 Answers2

3

Thought 1: why put application level validation annotations on fields you're managing yourself in the PrePersist, that aren't set by users?

Thought 2: You could use Validation groups so that you can pre-check only those fields that are set at the user level and ignore the system managed ones.

Affe
  • 47,174
  • 11
  • 83
  • 83
  • basically I'd rather not keep validation code in multiple places. The fields aren't set by a user, but rather a series of processing algorithms, all similar, but with some minor variants. I'll read up on validation groups, maybe that's the trick! – milletron Jul 10 '12 at 02:36
2
  public interface PersistValidationGroup { }


  @NotBlank
  private String anotherString;

  @NotNull(groups={PersistValidationGroup.class})
  @Temporal(TemporalType.TIMESTAMP)
  private Date createdAt;

  @NotNull(groups={PersistValidationGroup.class})
  @Temporal(TemporalType.TIMESTAMP)
  private Date updatedAt;  

  @NotNull(groups={PersistValidationGroup.class})
  @Temporal(TemporalType.TIMESTAMP)
  private Date postedDate;  

Then, when you validate manually:

validator.validate(object, javax.validation.groups.Default.class);

At this point, only those fields marked with the Default validation group (which by the way is what is used if you leave the "groups" attribute blank) will be validated. Anything in the PersistValidationGroup would not be validated.

However, hibernate will call like this I believe:

validator.validate(object);

So all groups would be validated.

Matt
  • 11,523
  • 2
  • 23
  • 33
  • Awesome, @Matt. You did my homework for me hinted by Affe on validation groups. :) I'll try it out. Pre-screen with a validation group on things that will break a save and cause the transaction to fail, let the rest of the validations (and PrePersist method, etc) get called when saving. – milletron Jul 10 '12 at 02:40
  • NP. I'm very familiar with the framework. I use it quite a bit, and have run into this exact problem. We're saving multiple objects in the same transaction, so validating up front allows us to save only those that pass validation, and we can log any that fail. At the same time we're using a HibernateInterceptor to populate timestamps (we're not on JPA yet, so PrePersist and PreUpdate are not available to us easily). – Matt Jul 10 '12 at 03:04