2014-09-02 16 views
5

Ho un servizio di entità su cui ho bisogno di filtrare una raccolta di entità figlio, in base a un elenco di ID. Il mio servizio ha un metodo pubblico che riceve l'id dell'entità padre e un elenco di id di alcune delle sue entità figli.Come filtrare le raccolte di entità figlio con predicato?

Per impostazione predefinita, so che JPA recupera tutte le entità correlate e questo è il suo comportamento effettivo. Ma dobbiamo lavorare sull'esecuzione del servizio. Quindi, invece di ottenere tutte le entità correlate e filtrarle con molti loop (filtro su ID e anche su altre proprietà come la proprietà date), voglio ottenere solo le entità interessate dalla mia richiesta.

miei genitori entità

@Entity 
@Table(name = "MyParent") 
public class MyParentEntity { 

    @Id 
    @GeneratedValue(strategy = GenerationType.SEQUENCE, 
     generator = "SEQ_MyParent") 
    @SequenceGenerator(allocationSize = 1, name = "SEQ_MyParent", 
     sequenceName = "SEQ_MyParent") 
    @Column(name = "ID_PARENT") 
    private Long id; 

    @OneToMany(mappedBy = "myParent", cascade = CascadeType.ALL, 
     fetch = FetchType.EAGER, orphanRemoval = true) 
    private final List<MyChildEntity> myChild = new ArrayList<MyChildEntity>(); 

} 

mio bambino Entity

@Entity 
@Table(name = "MyChild") 
public class MyChildEntity { 

    @Id 
    @GeneratedValue(strategy = GenerationType.SEQUENCE, 
     generator = "SEQ_MyChild") 
    @SequenceGenerator(allocationSize = 1, name = "SEQ_MyChild", 
     sequenceName = "SEQ_MyChild") 
    @Column(name = "ID_CHILD") 
    private Long id; 

    @ManyToOne 
    @JoinColumn(name = "ID_PARENT") 
    private MyParentEntity myParent; 
} 

sto usando CrudRepository Primavera-dati per ottenere i dati dal mio DB e mi si estende anche JpaSpecificationExecutor da usare predicato.

public interface MyParentRepository extends CrudRepository<MyParentEntity, Long>, 
    JpaSpecificationExecutor<MyParentEntity> { 
} 

Questo mi ha lasciato usare CrudRepository findOne() metodo, ma con un oggetto specifica al posto del parametro lungo regolare.

Inoltre, combino oggetto multipli di specifica con la seguente chiamata:

this.myParentRepository.findOne(Specifications 
    .where(firstSpecification(parentId)) 
    .and(secondSpecification(childrenIdsList))); 

ho creato un semplice test JUnit con un genitore legato a due entità bambini. Nella mia richiesta, sono in grado di ottenere l'entità padre con l'Id fornito. Ma anche se fornisco l'id del bambino, ottengo sempre entrambe le entità figli nella lista all'interno del genitore.

Nel mio metodo che restituisce un nuovo oggetto Specification, in cui il metodo toPredicate ha precedenza, non sono in grado di creare un predicato che filtrerà la mia raccolta figli e otterrà solo quelli a cui sono interessato. So che Hibernate Criteria ha la possibilità di aggiungere "Restrizioni" ma questo non è disponibile nel CriteriaBuilder fornito con il metodo toPredicate.

public static Specification<MyParentEntite> firstSpecification(final Long id) { 
    return new Specification<MyParentEntite>() { 

     @Override 
     public Predicate toPredicate(Root<MyParentEntite> root, 
      CriteriaQuery<?> query, CriteriaBuilder cb) { 

      Predicate predicate = cb.equal(root.get(MyParentEntity_.id), id); 
      return cb.and(predicate); 
     } 
    }; 
} 

public static Specification<MyParentEntite> secondSpecification(final List<Long> ids) { 
    return new Specification<MyParentEntite>() { 

     @Override 
     public Predicate toPredicate(Root<MyParentEntite> root, 
      CriteriaQuery<?> query, CriteriaBuilder cb) { 

      Root<MyChildEntity> child = query.from(MyChildEntity.class); 
      Expression<Long> exp = child.get(MyChildEntity_.id); 
      Predicate p = exp.in(ids); 
      return cb.and(p); 
     } 
    }; 
} 

Nel metodo secondSpecification(), ho anche cercato di utilizzare ListJoin anziché Root direttamente nell'Entità. Ho cercato in altre domande qui, ma sembra che questa preoccupazione sia risolta con le restrizioni dei Criteri di Hibernate o con un LeftJoin, che ho provato nel mio ListJoin specificando il parametro JoinType.LEFT.

Ecco i link ai già collaudati soluzioni whitout successo:

JPA CriteriaBuilder - How to use "IN" comparison operator

JPA2 Criteria-API: select... in (select from where)

voglio dire che io sono relativamente nuovo con criteri di API e predicato. Forse mi manca qualcosa che è semplice ma è ovvio per gli esperti di sviluppo JPA!

Grazie mille per il vostro aiuto!

risposta

2

Infine, ho trovato un modo per risolvere il mio problema. Richiedere solo raccolte parziali di sub-entità è qualcosa che abbiamo trovato pericoloso in termini di integrità dei dati.Se un servizio remoto chiama per richiedere la mia entità genitore con una raccolta parziale di entità figli all'interno di un get, questo oggetto entità padre può essere restituito per un'operazione di modifica che risulterà in molte chiamate "elimina" sulle istanze rimosse delle entità figli. L'API di persistenza considererà questi bambini mancanti come relazioni rimosse, il che è qualcosa che non vogliamo.

Ho creato un oggetto transfert fittizio che contiene le raccolte parziali di entità figli richieste, quindi questo oggetto fittizio transfert non può essere utilizzato in una chiamata di operazione di modifica futura. La versione completa dell'entità padre verrà utilizzata per lo scopo "modifica".

2

Il tuo provider JPA è in ibernazione? Hai considerato i filtri in sospensione che possono filtrare le entità figlio invece di rimuoverle. Ma l'utilizzo del filtro è in qualche modo molto difficile da capire!

Problemi correlati