2

Given a Spring Data JPA Repository like so.

public interface ProductRepository extends CrudRepository<Product, Long>, QueryDslPredicateExecutor<Product> {


    @Query("select p from Product p where p.attributes[?1] = ?2")
    List<Product> findByAttributeAndValue(String attribute, String value);
}

How do I get Spring Data JPA to validate each of these queries at startup. For example when I use named queries with hibernate and the query is syntactically incorrect or property name is wrong hibernate complains.

Can spring data be configured to validate queries on start-up?

ams
  • 60,316
  • 68
  • 200
  • 288
  • 1
    You don't want to run your queries without a controlling logic. Imagine you have a `delete from Product` method. Make tests. – Markus Malkusch Jan 14 '14 at 20:23
  • @MarkusMalkusch I just want the quries validated without running them, if a query refers to a non existent property, or entity, or has a syntax error that can't be parsed its good to know at satrtup. – ams Jan 15 '14 at 03:53
  • @ams did not ask to run queries on startup but to *validate* them. If the query contains invalid JPQL or entity/property names that do not map to your code then you want to get fast-fail behaviour so on startup of the server an exception will tell you that your Query is invalid. BTW: hibernate does NOT do what you are assuming to validate named queries. See here: https://stackoverflow.com/questions/27565706/are-hibernate-named-queries-precompiled-in-the-true-sense/27574444 – Jörg Oct 08 '21 at 14:46

2 Answers2

3

Rather than validating this at startup, I suggest that you create a test for this that can be executed by your continues integration server. Since you are already using Spring, it is very simple to create an Embedded Database that you can initialize with known test data.

Create two files schema.sql which is the actual production data schema, and test-data.sql which contains some known test data. Next, create a separate test application context that :

<beans ...>

    <jpa:repositories base-package="com.example.repositories" />

    <jdbc:embedded-database id="dataSource">
        <jdbc:script location="classpath:schema.sql"/>
        <jdbc:script location="classpath:test-data.sql"/>
    </jdbc:embedded-database>

    <tx:annotation-driven/>

    <!-- more beans -->
</beans>

or Java based configuration:

@Configuration
@EnableJpaRepositories("com.example.repositories")
@EnableTransactionManagement
public class TestConfig {

    @Bean(destroyMethod = "shutdown")
    public void EmbeddedDatabase embeddedDatabase() {
        EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
        return builder.setType(H2).addScript("schema.sql").addScript("test-data.sql").build();

    // more beans
}

Next, you create a test for your ProductRepository:

@ContextConfiguration([reference to your xml or Java application context])
@RunWith(SpringJUnit4ClassRunner.class) 
@Transactional
public class ProductRepositoryTest {

    @Autowired
    ProductRepository repository;

    @Test
    public void findProductByAttributeAndValue() {
        List<Product> actual = repository.findByAttributeAndValue("attr", "val");

        // add assertions that 'actual' contains the expected data from the test-data.sql
    }
}

The addition of @Transactional to the test class will cause the database to automatically rollback after each test method has completed. Consequently, all test will have the same database state (as specified by the test-data.sql) even if repository write operations are executed within a test.

matsev
  • 32,104
  • 16
  • 121
  • 156
  • I already have a pretty good test suite but it takes a while to run :) would prefer a way to just syntax check the queries at start-up. – ams Jan 15 '14 at 03:52
0

You can create a method decorated with @PostCreate annotation on your bean and invoke the repository method so any exceptions will happen immediately. It is cumbersome if you have many.

Probably a more appropriate method is to unit tests those repository methods.

gerrytan
  • 40,313
  • 9
  • 84
  • 99
  • and also, just calling the methods won't be enough, because if the result is null, there may be still potential casting exceptions. –  Jan 14 '14 at 02:01