A chi non è mai capitato nelle proprie query di dover aggiungere sempre gli stessi filtri per esempio per non caricare righe con stati non logicamente validi o righe valide in certi intervalli di date? EclipseLink permette di inserire questi filtri una volta per tutte, dimenticandoci di doverci portare sempre il fardello.
Questione di stile
Se JPA o in generale gli ORM hanno risolto il mismatching tra il mondo ad oggetti e quello relazionale, il problema dei diversi metodi di progettazione tra i due mondi è un’altra questione.
Una delle caratteristiche ricorrenti nella progettazione dei database è la presenza di tabelle “storicizzate” e di “stati logici” delle righe. Per un DBA è infatti consuetudine non cancellare mai i dati dal database, ma si tende a nasconderli alla logica applicativa inserendo uno stato particolare o un intervallo di date in cui la riga stessa è valida. Questo approccio è molto utile da un punto di vista di integrità e di debug dei dati, ma lato applicativo costringe a riempire le query di filtri aggiuntivi per non caricare i dati indesiderati. In JPA può essere addirittura frustrante una situazione di questo tipo.
Immaginiamo infatti la relazione one-to-many tra le entità Ordine
e DettaglioOrdine
, rappresentata come al solito da una lista di DettaglioOrdine
:
@Entity public class Ordine implements Serializable { //Elementi di testata... @OneToMany(mappedBy = "ordine") private ListdettagliOrdine; public List getDettagliOrdine() { return this.dettagliOrdine; } } @Entity public class DettaglioOrdine implements Serializable { //Elementi di dettaglio... @ManyToOne @JoinColumn(name="id_ordine") private Ordine ordine; @Enumerated(EnumType.STRING) private StatoDettaglioOrdine stato; //Getters + Setters }
Chiamando il metodo getDettagliOrdine()
, JPA caricherà tutti i dettagli dell’ordine, non curante dell’eventuale stato della riga di dettaglio che la rende non logicamente valida per l’applicativo. Questo tipo di gestione, efficace lato database, in JPA impedisce di usare invece mapping di questo tipo, rendendo la programmazione meno pulita e inutilmente più articolata, perché è necessario ricorrere a query JPQL per caricare i dettagli validi di un ordine.
EclipseLink, aiutami tu!
Per mettere d’accordo tutti, EclipseLink non finisce di stupire! Attraverso il meccanismo della customizzazione dei descrittori delle entità, come in un’altra occasione, è possibile fare un sacco di cose, come appunto agganciare sempre uno specifico filtro quando si carica una certa entità! Come fare? Basta estendere l’interfaccia org.eclipse.persistence.sessions.factories.DescriptorCustomizer
come segue:
public class DettaglioOrdineDescriptorCustomizer implements DescriptorCustomizer { @Override public void customize(ClassDescriptor descriptor) throws Exception { ExpressionBuilder orderDetailEb = new ExpressionBuilder(); Expression filterExpression = orderDetailEb.get("stato") .notEqual(StatoDettaglioOrdine.ANNULLATO); descriptor.getQueryManager() .setAdditionalJoinExpression(filterExpression); } }
Annotiamo poi l’entità DettaglioOrdine
con
@Customizer(value = DettaglioOrdineDescriptorCustomizer.class)
in modo da caricare il DescriptorCustomizer all’avvio di EclipseLink e… date un occhiata alle query in console quando chiamate getDettagliOrdine()
da Ordine
!!
Conclusioni
Tutto questo è possibile grazie al metodo setAdditionalJoinExpression
. Lo stesso risultato poteva essere ottenuto tramite la tecnica degli “Amendment Methods” almeno in teoria: il wiki di EclipseLink purtroppo non è molto esaustivo a riguardo.
Comunque sia, come suggerisce il javadoc del metodo setAdditionalJoinExpression
, questo è solo uno dei tanti modi in cui questo metodo può essere usato: visto che prende come argomento una Expression
, lascio alla vostra immaginazione il resto!!