Your browser (Internet Explorer 7 or lower) is out of date. It has known security flaws and may not display all features of this and other websites. Learn how to update your browser.

X

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.

<!-- 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:


<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:

@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:

@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:

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:

@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

  • Oliver Gierke

    Very cool one! We have an open JIRA request for functionality like this (see [0]). Would you be interested in contributing this via a pull request or be okay if we come up with something similar inside the framework ourselves? I though about looking for the javax.validation API and implementation being present on ApplicationContext startup and auto-registering the according event listener implementation.

    Cheers,
    Ollie

    [0] https://jira.springsource.org/browse/DATAMONGO-36

    • MaciejWalkowiak

      Thanks Oliver. I am definitely interested in contributing this via pull request. Spring-Data-MongoDB already forked – I will prepare change after I am done with my daily business.

      I am not convinced to auto-registering validation event listener by default. I think it should be consistent with other Spring projects that uses JSR-303 validation. In Spring MVC its done in a way that mvc:annotation-driven has to be registered in context – I think similar solution could have been in Spring Data MongoDB. What do you think about registering it by defining mongo:validation-enabled ?

      • Oliver Gierke

        Actually, Spring MVC *does* auto-enable validation (see [0] for details) in case you have an validation implementation on the classpath. So I think we will align to that and maybe add a flag to the namespace to opt-out of this behavior to given users more control.

        [0] http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/validation.html#validation-mvc-jsr303

        • http://maciejwalkowiak.pl/ Maciej Walkowiak

          Yes it is auto-enabled after mvc:annotation-driven is set. Correct me if I am wrong but I don’t see similar XML tag for Spring Data MongoDB for enabling annotation magic (maybe because annotation magic is enabled by default :-) ). 

          Anyway – I have just made pull request just for validator https://github.com/SpringSource/spring-data-mongodb/pull/2 . If you will approve that I would like also to pull auto registration by defining mongo:validation-enabled XML tag (changes can be found in https://github.com/maciejwalkowiak/spring-data-mongodb/tree/DATAMONGO-36-namespace). I find it more developer friendly to turn something on on demand than turning off default config if its not needed. Especially when I don’t see really good place where to put flag to opt-out from this behavior.

          If you have any advices let me know please.