Backend Development 8 min read

Mastering JPA with @Formula, @SQLRestriction, and @Filter in Spring Boot 3

This article explains how three Hibernate annotations—@Formula, @SQLRestriction (or @Where), and @Filter—solve common JPA pain points such as scattered query logic, duplicated calculations, and hard‑coded filters, providing code‑reduction, better performance, and easier maintenance in Spring Boot 3 projects.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Mastering JPA with @Formula, @SQLRestriction, and @Filter in Spring Boot 3

1. Introduction

When using Spring Boot with JPA, developers often encounter scattered query logic, duplicated calculation logic, and hard‑coded data‑permission filters, which cause code bloat, high maintenance cost, and tight coupling between business logic and database operations.

2. Solution

The three Hibernate annotations @Formula , @SQLRestriction (or @Where ) and @Filter directly address these issues.

@Formula

Maps a derived property to a native SQL expression, eliminating the need for redundant fields. Example:

<code>private String name;
private BigDecimal price;

@Formula("(concat(name, '/', price))")
private String info;
</code>

When queried, the generated SQL includes concat(name, '/', price) as a selected column.

It can also embed sub‑queries, e.g.:

<code>@Formula("(select sum(s.sale_price * s.quantity) from sales_detail s where s.product_id = id)")
private BigDecimal salePrice;
</code>

The resulting SQL selects the computed value alongside other columns.

@SQLRestriction

Adds a static native SQL condition to the entity or collection. Example adds deleted = 0 to every query on Product :

<code>@Entity
@Table(name = "product")
@SQLRestriction("deleted = 0")
public class Product {
    // ... other fields
    @Column(columnDefinition = "int default 0")
    private Integer deleted;
}
</code>

The generated SQL automatically contains WHERE (deleted = 0) . The annotation can also be placed on collection mappings.

@Filter

Provides a parameterized, session‑level filter. Define the filter:

<code>@FilterDef(name = "filterByDeletedAndStock",
    parameters = {
        @ParamDef(name = "state", type = Integer.class),
        @ParamDef(name = "stock", type = Integer.class)
    })
@Filters({
    @Filter(name = "filterByDeletedAndStock",
            condition = "deleted = :state and stock > :stock")
})
public class Product {
    // ... fields
}
</code>

Enable it at runtime (e.g., via an AOP aspect) and set parameters to dynamically add the condition:

<code>@Component
@Aspect
public class FilterAspect {
    @PersistenceContext
    private EntityManager entityManager;

    @Around("@annotation(com.pack.formula.annotation.EnableFilter)")
    public Object doProcess(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            int state = 0;
            int stock = 80;
            org.hibernate.Filter filter = entityManager.unwrap(Session.class)
                .enableFilter("filterByDeletedAndStock");
            filter.setParameter("state", state);
            filter.setParameter("stock", stock);
            return joinPoint.proceed();
        } finally {
            entityManager.unwrap(Session.class).disableFilter("filterByDeletedAndStock");
        }
    }
}
</code>

When the annotated method is executed, the generated SQL includes the dynamic condition; without the annotation, the original SQL is used.

Using these three annotations can reduce code volume by up to 60 % while improving maintainability and performance.

Backend DevelopmentSpring BootAnnotationsHibernateDynamic QueriesJPA
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.