Background
It is common that we need to implement similar filter query in our application. For example, we have User
and Post
entities whereby both
contains status
field. We would want to ensure that when these Entity
is retrieved, only ACTIVE
ones will be returned.
In this tutorial we will implement a global filter by utilising Spring Data Jpa repositoryBaseClass
.
Integration Tests
There are two tests which involves two entities - User
and Country
. User
contains a status
field while Country
does not.
When findAll
is triggered for User
then only ACTIVE
users will be returned.
@Testcontainers
@DataJpaTest(
properties = "spring.jpa.hibernate.ddl-auto=create-drop",
includeFilters = @Filter(type = ANNOTATION, classes = EnableJpaRepositories.class)
)
class UserRepositoryTests {
@Container
@ServiceConnection
private static final MySQLContainer<?> mysql = new MySQLContainer<>("mysql:latest");
@Autowired
private UserRepository users;
@BeforeEach
void setup() {
users.saveAll(List.of(
new User("Rashidi Zin", ACTIVE),
new User("John Doe", INACTIVE)
));
}
@Test
@DisplayName("Given there are two users with status ACTIVE and INACTIVE, when findAll is invoked, then only ACTIVE users are returned")
void findAll() {
assertThat(users.findAll())
.extracting("status")
.containsOnly(ACTIVE);
}
}
However, when findAll
is triggered for Country
then all countries will be returned.
@Testcontainers
@DataJpaTest(
properties = "spring.jpa.hibernate.ddl-auto=create-drop",
includeFilters = @Filter(type = ANNOTATION, classes = EnableJpaRepositories.class)
)
class CountryRepositoryTests {
@Container
@ServiceConnection
private static final MySQLContainer<?> mysql = new MySQLContainer<>("mysql:latest");
@Autowired
private CountryRepository countries;
@BeforeEach
void setup() {
countries.saveAll(List.of(
new Country("DE", "Germany"),
new Country("MY", "Malaysia")
));
}
@Test
@DisplayName("Given there are two countries, when findAll is invoked, then both countries are returned")
void findAll() {
assertThat(countries.findAll())
.hasSize(2)
.extracting("isoCode")
.containsOnly("DE", "MY");
}
}
Configuration Class
We will start by defining repositoryBaseClass
class JpaCustomBaseRepository<T, ID> extends SimpleJpaRepository<T, ID> {
public JpaCustomBaseRepository(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
super(entityInformation, entityManager);
}
@Override
public List<T> findAll() {
var hasStatusField = Stream.of(ReflectionUtils.getDeclaredMethods(getDomainClass())).anyMatch(field -> field.getName().equals("status"));
return hasStatusField ? findAll(where((root, query, criteriaBuilder) -> root.get("status").in(ACTIVE))) : super.findAll();
}
}
In JpaCustomBaseRepository
we will define a method findAll
which will be used by all Entity
to retrieve data. This method will filter
any Entity
with status
field and return only ACTIVE
ones.
To recap, User
contains status
field while
Country
does not. Therefore, when findAll
is triggered for User
then
only ACTIVE
users will be returned. However, when findAll
is triggered for Country
then all countries will be returned.
Next we will inform Spring Data Jpa to use JpaCustomBaseRepository
as the base class for all Entity
by defining @EnableJpaRepositories
in JpaConfiguration
.
@Configuration
@EnableJpaRepositories(
basePackages = "zin.rashidi.boot.data.jpa",
repositoryBaseClass = JpaCustomBaseRepository.class
)
class JpaConfiguration {
}
Verification
To ensure that our implementation is working as expected, we will execute tests defined in UserRepositoryTests
and CountryRepositoryTests
.
Both tests should pass.