Software Develoment Journey
Java, Spring Framework, How-to

JSR 303 validation with Spring Data MongoDB

Solution presented in article was successfully contributed into Spring Data MongoDB project and it is going to be released in version 1.1.0.
Read more in official announcement.

In 2012 there is no need to introduce MongoDB. Even if you have not used it I am sure you have heard a lot about this database.

Recently we have decided to use Mongo as a main database in our project. Mongo is already mature database and there are enterprise-ready Java libraries for it as well. Because I find Spring Framework related libraries the most lightweight and powerful same time we have chosen Spring Data MongoDB as a data layer library.

Because MongoDB is schemaless there is no way to make make any constraints on collection properties like minimum or maximum length, not null and so on. Only unique index is available. Responsibility for checking if objects that are going to be inserted into database are valid is on application side.

The most common way to validate objects in Java world is to use JSR-303 validation annotations. Spring Data MongoDB does not provide out of the box documents validation. I will show below how it can be implemented.

Maven dependencies

You need to add Validation API and one of available implementations. I used Hibernate Validator.

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- JSR-303 Validation -->
<dependency>
  <groupId>javax.validation</groupId>
  <artifactId>validation-api</artifactId>
  <version>1.0.0.GA</version>
  <scope>compile</scope>
</dependency>

<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-validator</artifactId>
  <version>4.2.0.Final</version>
</dependency>

Spring configuration

In order to use Spring Data MongoDB you need to follow instructions from reference. In order to use validation framework you need additionally to register validator bean in your Spring context:

1
<bean class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>

Adding MongoEventListener

Spring Data MongoDB provides easy way to implement Mongo objects lifecycle listener that makes it possible to intercept an object before and after it goes through lifecycle phases: convert, save and load. The only thing you need to do is to extends AbstractMongoEventListener and override one of method that correspond to lifecycle phase.

Because we want to validate object before it is being saved we need to override onBeforeSave method:

1
2
3
4
5
6
7
8
@Component
public class BeforeSaveValidator extends AbstractMongoEventListener {

  @Override
  public void onBeforeSave(Object source, DBObject dbo) {
      // do validation here
  }
}

Validation

In order to validate object we need to call validate) that returns Set of ConstraintViolation objects and if we want to prevent object from being saved we need to wrap it into exception and throw.

Finally lets validate object in BeforeSaveValidator:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Component
public class BeforeSaveValidator extends AbstractMongoEventListener {

  @Autowired
  private Validator validator;

  @Override
  public void onBeforeSave(Object source, DBObject dbo) {
      Set<ConstraintViolation<Object>> violations = validator.validate(source);

      if (violations.size() > 0) {
          throw new ConstraintViolationException(violations);
      }
  }
}

Let’s test it

Time to test if that actually works. I’ve made simple class with two restrictions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class User {
  @Size(min = 10)
  private String name;

  @Min(18)
  private Integer age;

  public User(String name, Integer age) {
      this.name = name;
      this.age = age;
  }

  public String getName() {
      return name;
  }

  public Integer getAge() {
      return age;
  }
}

Spring based integration test where I expect that saving method throws ConstraintViolationException with 2 violations:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class ValidationTest {
  @Autowired
  private MongoTemplate mongoTemplate;

  @Test
  public void shouldThrowValidationException() {
      User user = new User("john", 17);

      try {
          mongoTemplate.save(user);
          fail();
      } catch (ConstraintViolationException e) {
          assertThat(e.getViolations()).hasSize(2);
      }
  }
}

Summary

And thats basically it. Once its implemented it validates all objects that are passed through MongoTemplate or any Spring Data MongoDB repository.

If you have any questions or suggestions how validation for Spring Data MongoDB can be implemented in different way please leave a comment.

If you enjoyed this post, then make sure you subscribe to my RSS feed