The best way to validate the DDL schema with Spring and Hibernate
Imagine having a tool that can automatically detect JPA and Hibernate performance issues. Wouldn’t that be just awesome?
Well, Hypersistence Optimizer is that tool! And it works with Spring Boot, Spring Framework, Jakarta EE, Java EE, Quarkus, or Play Framework.
So, enjoy spending your time on the things you love rather than fixing performance issues in your production system on a Saturday night!
Introduction
In this article, we are going to see what is the best way to validate the DDL schema and the JPA entity mappings when using Spring and Hibernate.
I decided to write this article after reading this Tweet:
Hi Vlad, shouldn't we set the value of spring.jpa.hibernate.ddl-auto=validate so that we are checking that flyway generated tables and our entities are in sync??
— Raja Dilip Kolli (@rajadilipkolli) June 5, 2023
The hbm2ddl validate strategy
As I explained in this article, Hibernate provides a SchemaManagementTool
that we can use to manage or validate the underlying database schema.
While generating the DDL scripts with this Hibernate tool can be useful only to determine the DB schema that Hibernate expects for the current JPA entity mappings, it’s best to use an automatic schema management tool, like Flyway, to manage your database schema.
However, the Hibernate SchemaManagementTool
also offers a validate
strategy that can verify whether the JPA entity mappings are compatible with the underlying database schema.
In Spring, you can activate this strategy via the spring.jpa.hibernate.ddl-auto
application property:
spring.jpa.hibernate.ddl-auto=validate
Once you provide this setting, the validation will be done every time you bootstrapped Hibernate.
However, since integration tests are meant to run in isolation, it’s common to create and destroy the Hibernate schema for every test execution, and validating the schema for every test execution is just adding an unnecessary overhead.
We can actually measure this overhead like this:
SchemaValidator schemaValidator = getSchemaValidator(sessionFactory); final ExecutionOptions executionOptions = SchemaManagementToolCoordinator .buildExecutionOptions( settings, ExceptionHandlerHaltImpl.INSTANCE ); long startNanos = System.nanoTime(); schemaValidator.doValidation( metadataImplementor, executionOptions ); LOGGER.info( "Schema validation took: [{}] ms", TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanos) );
When running this test on Shopizer, which is a non-trivial Spring Boot project with over 100 JPA entities, this is what I get printed in the log:
SchemaValidationTest : Schema validation took: [138] ms
So, if the validation takes 138 milliseconds on every test execution and we have 1000 tests, then the overall overhead of the spring.jpa.hibernate.ddl-auto=validate
setting is going to be 2 minutes and 18 seconds.
Not nice!
Now, you may wonder why you’d even need such a validation if you’re already using Flyway or Liquibase to manage your schema.
Even if you are using an automated schema management tool, if you are using JPA, then you must make sure that the entity mappings are compatible with the underlying database schema. Otherwise, your application is not going to work as expected, as the JPA provider might assume a different database schema than the actual one.
So, the purpose of this validation check is to ensure that the JPA entity mappings are compatible with the underlying database schema.
The best way to validate the DDL schema with Spring and Hibernate
Now that we have seen that it’s not useful to validate the schema every time Hibernate bootstraps, let’s see how we can disable the global validation.
To disable the global schema validation, we just need to set the spring.jpa.hibernate.ddl-auto
property to the value of none
:
spring.jpa.hibernate.ddl-auto=none
Having disabled the global schema validation, we can now activate the spring.jpa.hibernate.ddl-auto=validate
setting only for a single Spring test like this:
@DataJpaTest( properties = "spring.jpa.hibernate.ddl-auto=validate" ) @AutoConfigureTestDatabase(replace = Replace.NONE) public class SchemaValidationTest { @Test public void testSchemaValidity() {} }
This way, we can still validate the schema, but we do it only once instead of doing it every time the JPA EntityManagerFactory
is bootstrapped.
I'm running an online workshop on the 11th of October about High-Performance SQL.If you enjoyed this article, I bet you are going to love my Book and Video Courses as well.
Conclusion
Validating the DDL schema with the JPA mappings is a very important task, but you should not do that every time Spring runs an integration test because the validation overhead will just add up to the overall time it takes to run your test suite.
By validating the database schema only once, you get exactly the outcome you wanted from the very beginning, but without paying the extra overhead every time you bootstrap JPA and Hibernate.
