2014-07-09 16 views
5

Sto cercando di capire il modo migliore per utilizzare JPA nel contesto di un servizio Web riposante. L'input arriva come JSON e io posso usare Jackson/JAX-RS per convertirlo in POJO. Questo viene passato a un servizio in cui ho bisogno di unire in qualche modo un'entità JPA.Come unire l'input da un servizio Web a un'entità JPA

Queste sono le opzioni che ho trovato finora con pro e contro.

1. JPA merge()
La prima cosa che ho provato è stato probabilmente il più semplice. L'azione GET restituisce l'entità JPA che può essere serializzata facilmente in JSON. Nell'aggiornamento l'oggetto è passato indietro è JSON che può essere usato per popolare un'entità distaccata. Questo può essere salvato nel DB usando il metodo JPA merge().

Pro
architettura semplice con meno la duplicazione di codice (cioè di nessuno DTO)

Contro
Per quanto posso dire questo funziona solo se si passa l'intero modello intorno. Se si tenta di nascondere determinati campi, ad esempio la password su un'entità utente, l'unione ritiene che si stia tentando di impostare questi campi su null nel DB. Non bene!

2. DTO utilizzando JPA find() e Dozer
successivo ho pensato di guardare con oggetti di trasferimento dei dati. Apparentemente un anti-pattern, ma vale la pena dare un'occhiata. Il servizio ora crea un'istanza DTO basata sull'entità ed è questo DTO serializzato su JSON. L'aggiornamento ottiene quindi l'entità dal DB utilizzando un metodo find() e i valori devono essere copiati dal DTO all'entità. Ho provato ad automatizzare questa mappatura usando il framework dozer.

Pro
Non è necessario per restituire l'intero modello. Se si dispone di determinati campi che non si desidera aggiornare, è possibile lasciarli fuori dal DTO e non possono essere copiati nell'entità per errore. L'uso di dozer significa che non è necessario copiare manualmente gli attributi da dto all'entità e viceversa.

Contro
Ci si sente come a ripetere se stessi quando si scrive il DTO. In qualche modo devi mappare tra entità e DTO. Ho provato ad automatizzare questo con Dozer ma è stato un po 'deludente. Stava annullando le cose che non avrebbe dovuto essere e per avere il pieno controllo è necessario scrivere xml.

3. di DTO mediante strumenti manuali si fondono
Un terzo modo sarebbe quello di abbandonare il dozer e basta copiare le proprietà di fronte al DTO all'entità nel servizio. Tutti sembrano dire anti-pattern ma è praticamente come ha funzionato ogni applicazione non banale che ho visto in passato.

Sommario
Sembra essere una decisione tra mantenere le cose semplici per lo sviluppatore, ma non avere il controllo di input/output o fare un servizio web più robusto, ma dover utilizzare un anti-modello nel processo. ..

Ho perso qualcosa?Forse c'è un'alternativa elusiva?

risposta

0

L'utilizzo di JPA merge sembra il più semplice, più pulito e con uno sforzo minore, ma come correttamente rilevato crea problemi con gli attributi di entità scollegati impostati su null. Un altro problema che si è rivelato essere grande in una delle mie esperienze è che se si fa affidamento sull'operazione di unione JPA, si deve usare anche la funzione Cascade. Per una relazione semplice e meno nidificata, questo funziona abbastanza bene, ma per oggetti di dominio profondamente nidificati e molte relazioni, questo ha un grande impatto sulle prestazioni. Il motivo è che lo strumento ORM (Hibernate nella mia esperienza) memorizza in cache l'SQL per caricare l'entità di fusione ('merge path' in Hibernate parlance) e se il nesting è troppo profondo con i mapping di Cascade i join in SQL diventano troppo grandi. Marcare le realizzazioni Lazy non aiuta qui poiché il percorso di unione è determinato dalle Cascate nelle relazioni. Questo problema diventa evidente lentamente man mano che il modello si evolve. Inoltre, la prospettiva di un DBA arrabbiato che sventolava una query di join enorme sul nostro viso ci ha spinto a fare qualcosa di diverso :-) C'è un interessante issue relativo a Hibernate riguardante l'unione delle relazioni Lazy ancora irrisolto (in realtà è stato rifiutato ma la discussione è molto divertente da leggere) in Hibernate JIRA.

Siamo quindi passati all'approccio DTO in cui ci siamo astenuti dall'utilizzare l'unione e ci siamo basati per farlo manualmente. Sì, era noioso e richiedeva la conoscenza di quale stato proviene effettivamente dall'entità distaccata, ma per noi valeva la pena. In questo modo non tocchiamo le relazioni Lazy e gli attributi che non intendono cambiare. e imposta solo ciò che è richiesto. Il rilevamento automatico dello stato di Hibernate fa il resto sul commit delle transazioni.

1

Questo è l'approccio che sto usando:

  • sopprimere la serializzazione di alcuni campi con XmlTransient annotazione
  • quando si aggiorna il record dal client, ottenere l'entità dal database e utilizzare ModelMapper con la mappatura proprietà personalizzata a copia i valori aggiornati senza modificare i campi che non sono nella rappresentazione JSON.

Ad esempio:

public class User { 
    @Id 
    private long id; 

    private String email; 

    @XmlTransient 
    private String password; 
    ... 
} 

public class UserService { 
    ... 
    public User updateUser(User dto) { 
     User entity = em.find(User.class, dto.getId()); 
     ModelMapper modelMapper = new ModelMapper(); 
     modelMapper.addMappings(new UserMap()); 
     modelMapper.map(userDto, user); 
     return user; 
    } 
} 

public class UserMap extends PropertyMap<User, User> { 
    protected void configure() { 
     skip().setPassword(null); 
    } 
} 

BeanUtils è un'alternativa a ModelMapper.

Sarebbe bello se queste librerie potessero riconoscere l'annotazione XmlTransient in modo che il programmatore possa evitare di creare la mappa delle proprietà personalizzate.

Problemi correlati