Dank JPQL oder der Criteria API sind Queries keine große Sache. Doch oft entsteht in Projekten die Anforderung, Daten basierend auf z.B. Berechtigungen zu filtern. Alle Abfragen mit entsprechenden where-Clauseln zu versehen mag pragmatisch sein, ist aber sehr anfällig für Fehler und die Wartbarkeit leidet.
Hibernate bietet für genauso diesen Zweck @Filter Annotation an, die es uns erlauben auf Entity-Ebene Filter zu deklarieren, die bei Bedarf aktiviert werden können. Die Definition solcher Filter geschieht per Annotation auf z.B. der Entity selbst oder aber auch auf Package Ebene:
@FilterDefs(
@FilterDef(
name = FilterNames.BY_MATERIAL_NUMBER,
parameters = @ParamDef(name = FilterNames.BY_MATERIAL_NUMBER_P_PREFIX, type = "string"),
defaultCondition = "material_number like :" + FilterNames.BY_MATERIAL_NUMBER_P_PREFIX
)
)
Unser Filter definiert einen Namen (der später dazu verwendet, wird die, Aktivierung durchzuführen), ein oder mehrere Parameter und natürlich die konkrete SQL Kondition.
Dieser Filter muss nun lediglich auf Entity-Ebene durch eine entsprechende Annotation konfiguriert werden
@Entity
@Filter(name = FilterNames.BY_MATERIAL_NUMBER)
public class Material {...}
Solche Filter sind per Default deaktiviert und müssen nun für die aktive Hibernate Session aktiviert werden. Dies lässt sich über den Entitymanager ganz leicht durchführen
Session session = em.unwrap(Session.class);
Filter filter = session.enableFilter(FilterNames.BY_MATERIAL_NUMBER);
filter.setParameter(FilterNames.BY_MATERIAL_NUMBER_P_PREFIX, "99%");
em.createQuery(...)
In der laufenden Session werden jetzt alle Queries, die direkt „Material“ adressieren, zusätzlich mit unserem Filter versehen. Wichtig: das betrifft lediglich alle Filter Queries, ein direktes Laden über die Id mittels z.B. „load“ führt keine Filterung durch und auch das Laden abhängiger Entities (OneToMany, ManyToMany) bedarf zusätzlicher Annotationen (@FilterJoinTable).
Filter by Default
Oft wollen wir solche Filter immer aktivieren, um z.B. über die Berechtigungen des Users Einschränkungen durchzuführen. In Spring Boot bietet sich dafür eine Customizer Methode an
@Configuration
public class MaterialFilter {
@Bean
@ConditionalOnMissingBean
public PlatformTransactionManager transactionManager(
ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
JpaTransactionManager transactionManager = new JpaTransactionManager() {
@Override
protected EntityManager createEntityManagerForTransaction() {
final EntityManager entityManager = super.createEntityManagerForTransaction();
Session session = entityManager.unwrap(Session.class);
activateMaterialFilterByRole(session); // read roles, activate filter, set parameters
return entityManager;
}
};
transactionManagerCustomizers.ifAvailable((customizers) -> customizers.customize(transactionManager));
return transactionManager;
}
(als Alternative wäre auch eine Lösung mittels AOP Methoden möglich)
Und wie immer hier noch mal alles Live und in Farbe: https://github.com/GEDOPLAN/hibernate-filter








