SpEL in Spring Data queries

Spring Expression Language (SpEL) est is used often in Spring Security project. But Spring Data also supports it.

In this article we'll discover how to implement the queries containing SpEL fragments. In the first part we'll see some theoretical aspects with the classes participating in handling of SpEL expressions. The next part will show how to use SpEL fragments withing Spring Data JPA queries.

SpEL and Spring Data queries

Thanks to SpEL we can write the expressions able to be evaluated by Spring. They can be used in several situations. As we mentioned earlier, they can be used in Spring Security project to check if given user has the rights to access protected element. But they can also be employed to save expressions in the database and evaluate them dynamically, at runtime.

In Spring Data, SpEL can be used to String-based queries. We can refer to current entity thanks to following expression #{#entityName}. It refers to entity handled by given repository. To see what is implemented, let's take a look at org.springframework.data.jpa.repository.query.ExpressionBasedStringQuery. This class extends from StringQuery and represents a query based on SpEL. The query is parsed in a standard way, through instance of org.springframework.expression.spel.standard.SpelExpressionParser class. After the parsing, generated query returned as a String and used inside @Query annotation as a normal, no-expression containing query String. Code in charge of this transformation looks like that:

StandardEvaluationContext evalContext = new StandardEvaluationContext();
evalContext.setVariable(ENTITY_NAME, metadata.getEntityName());

SpelExpressionParser parser = new SpelExpressionParser();
Expression expr = parser.parseExpression(query, ParserContext.TEMPLATE_EXPRESSION);

Object result = expr.getValue(evalContext, String.class);
return result == null ? query : String.valueOf(result);

Implement SpEL with Spring Data queries

As you see, the only supported value in SpEL queries is entityName (at least for the current, 1.6.0 version). We can use it to, for example, do not make a typo error, less application compile and see this mistake only after. So, let's write a simple JUnit case and execute it against #{#entityName} expression. First, it's our repository:

public interface ProductRepository  extends CrudRepository<Product, Integer> {

  @Query("SELECT p FROM #{#entityName} p WHERE p.name = :name")
  public Product getByName(@Param("name") String name);
}

As you can imagine, in our test case we'll want to get one product by its name. Make it to "milk" for example (before launching this test, a product with this name must be inserted into database) :

public class ProductRepositoryTest {
  @Autowired
  private ProductRepository productRepository;

  @Test
  public void milkTest() {
    Product milk = productRepository.getByName("milk");
    assertTrue("Milk can't be null but it is", milk != null);
    assertTrue("Retreived product should be called 'milk', but is "+milk.getName(), 
      milk.getName().equals("milk"));
  }

}

The test should pass well. If it's not, maybe you don't have an appropriate version of Spring Data JPA project.

In this short article we saw the way of integrate SpEL expressions inside Spring Data queries. Actually it supports only the expression representing repository's entity. Thanks to it, we can avoid typos which are detectable only at runtime.


If you liked it, you should read:

📚 Newsletter Get new posts, recommended reading and other exclusive information every week. SPAM free - no 3rd party ads, only the information about waitingforcode!