2014-12-14 14 views
5

Sto iniziando a utilizzare l'API di java 8 stream. Mi piacerebbe convertire una lista di "sql result set" in oggetti di dominio, cioè la struttura composita.Java 8 Stream: creazione di oggetti multilivello/compositi

Oggetti dominio: un utente dispone di una raccolta di autorizzazioni, ogni autorizzazione ha una raccolta di anni di applicazioni. Ad esempio, John ha 2 permessi (MODERATORE e DEV). suo permesso moderatore è applicabile solo per il 2014 e il 2015 il suo permesso di dev applicabile solo per 2014.

class User { 
    // some primitives attributes 
    List<Permission> permission; 
} 

class Permission { 
    // some primitives attributes 
    List<Integer> years; 
} 

ora faccio una query e ha ottenuto un elenco di risultati piatte, qualcosa di simile a:

[1, "moderator", 2014] 
[1, "moderator", 2015] 
[1, "dev", 2014] 
[2, "dev", 2010] 
[2, "dev", 2011] 
[2, "dev", 2012] 

I numeri 1 e 2 sono ID utente.

Ho provato varie costruzioni ma alla fine è più complesso che fluente. E non ha funzionato :)
Ho letto in un libro di Java 8 che è "semplice" costruire oggetti dompain con i collezionisti.
Ho pianto un po 'quando ho letto che:' (

ho provato

sb.collect(
    collectingAndThen(
     groupingBy(
      Mybean::getUserId, 
      collectingAndThen(
       groupingBy(Monbean::getPermissionId, mapping(convertPermission, toList())), 
       finisherFonction) 
      ), 
     convertUser) 
); 

e ottenuto un inferno di fallimento generici compilation

  • qual è il modo migliore per costruire più composito di livello. oggetti di dominio che utilizzano java 8 streams?
  • è collectionAndThen/finisher una buona idea?
  • oppure utilizzare solo il raggruppamento, seguito da una funzione di mappatura?
  • si fa a trasformare il classificatore ad un oggetto (una specie di prima funzione di mappatura livello?)

Perché alla fine voglio sbarazzarsi della mappa e ottenuto un risultato List<User> (credo di poter aggiungere una chiama la mappa sulla voce Set per terminare la trasformazione).

+0

Quali sono i tuoi "risultati piatti"? Matrici di oggetti? – mkobit

risposta

4

Lasciate che vi offra alcune opzioni e decidiate quale vi sembra più chiaro. Suppongo che il costruttore utente sia User(int userId, List<Permission> permissions) e il costruttore di autorizzazioni sia Permission(String permissionId, List<Integer> years)

Opzione 1: l'approccio diretto. Raggruppa per userid, costruisci un elenco di permessi per ogni userid e crea oggetti User. Personalmente, trovo che questo molto nidificante nei collezionisti sia difficile da seguire.

List<User> users = beans.stream() 
    .collect(
     groupingBy(
      MyBean::getUserid, 
      collectingAndThen(
       groupingBy(
        MyBean::getPermission, 
        mapping(MyBean::getYear, toList()) 
       ), 
       t -> t.entrySet().stream() 
        .map(e -> new Permission(e.getKey(), e.getValue())) 
        .collect(toList()) 
      ) 
     ) 
    ).entrySet().stream() 
    .map(e -> new User(e.getKey(), e.getValue())) 
    .collect(toList()); 

Opzione 2: Idem come sopra, ma fare il collezionista permessi divisi per maggiore chiarezza.

Collector<MyBean, ?, List<Permission>> collectPermissions = collectingAndThen(
    groupingBy(MyBean::getPermission, mapping(MyBean::getYear, toList())), 
    t -> t.entrySet().stream() 
     .map(e -> new Permission(e.getKey(), e.getValue())) 
     .collect(toList()) 
); 

List<User> users = beans.stream() 
    .collect(groupingBy(MyBean::getUserid, collectPermissions)) 
    .entrySet().stream() 
    .map(e -> new User(e.getKey(), e.getValue())) 
    .collect(toList()); 

Opzione 3: In primo luogo lanciare i fagioli in una mappa di userid alla mappa della PermissionID all'elenco di anni (Map<Integer, Map<String, List<Integer>>). Quindi costruisci gli oggetti del dominio fuori dalla mappa

List<User> users = beans.stream().collect(
    groupingBy(
     MyBean::getUserid, 
     groupingBy(
      MyBean::getPermission, 
      mapping(MyBean::getYear, toList()) 
     ) 
    ) 
).entrySet().stream() 
    .map(u -> new User(
      u.getKey(), 
      u.getValue().entrySet().stream() 
       .map(p -> new Permission(p.getKey(), p.getValue())) 
       .collect(toList()) 
     ) 
    ).collect(toList()); 
+0

Ciao, grazie per la risposta. Ho giocato con i collezionisti ieri sera e ho capito meglio l'API :) Non ho provato la tua soluzione ma mi sembra che questo possa fare il lavoro! Un grande ringraziamento per il tuo aiuto. Sono d'accordo con te: raggruppamento nidificatoDi seguito sono difficili da seguire e difficili da scrivere (compilazione del tipo di errore). Sembra più facile (almeno per me) scrivere metodi unitari di trasformazione e comporli. Darò l'opzione 3 una prova. Qual è la tua preferenza? – Archange

+0

Posso aggiungere un po 'di complessità: se voglio gestire "valori nulli". Qual è il tuo schema preferito? mappatura su un nuovo accumulatore Hashmap? qualcos'altro ? Il suecase è Utente può avere autorizzazioni 0 o n e un'autorizzazione può avere 0 o n anni. addio – Archange

2

Il combinatore collectingAndThen è progettato per quando la forma intermedia per l'accumulazione differisce dalla forma finale desiderata. Questo è il caso del collector joining() (in cui il modulo intermedio è un StringBuilder, ma il modulo finale è un String) e può anche essere utilizzato per avvolgere raccolte mutabili con wrapper immutabili dopo che la raccolta è terminata. Quindi non penso che questo sia lo strumento che stai cercando.

Se siete alla ricerca di "permessi per utente", questo farà il trucco:

Map<UserId, List<Permission>> 
    queryResults.stream() 
       .collect(qr -> qr.getId(), 
         mapping(qr -> qr.getPermission() + ":" + qr.getDate(), 
           toList())); 

questo si tradurrebbe in:

1 -> [ moderator:2014, moderator:2015, dev:2014 ] 
2 -> [ dev:2010, dev:2011, dev:2012 ] 

L'idea è: - si esegue il raggruppamento da "id" (il primo campo della query) - Per ogni record, si seleziona una funzione dei campi rimanenti (qui, ho usato stringa concat per chiarezza, ma è possibile creare un oggetto con il permesso e l'anno), e invia a al collettore a valle - Utilizzare i servizi di raccolta.toList() come collector downstream, che sarà quindi il "valore" dello Map creato da groupingBy.

+0

Ciao, grazie per il tuo post. Capisco questa risposta ma non è esattamente quello che stavo cercando. Alla fine i valori della mappa non sono oggetti di dominio compositi. – Archange