ORM: progettazione del livello di servizio

3

Attualmente sto scrivendo un'applicazione web di medie dimensioni e mi trovo a fare questa domanda molte volte.

Per esempio, diciamo che ci sono utenti, che hanno file (uno-a-molti). Come dovrebbe l'interfaccia utente accedere ai dati? Un "estremo" sarebbe lasciare che il livello di servizio "solo" si occupi del caricamento pigro, cioè

public interface UserService {
    public User createUser(String loginName, String password);
    public Set<MediaFile> getFilesOfUser(User user);
}

o, d'altra parte, non restituiscono mai entità, solo i loro id

public interface UserService {
    public long createUser(String loginName, String password);
    public Set<Long> getFilesOfUser(long userId);
}

Ciò implica che il servizio avrebbe bisogno di metodi per interrogare (e impostare) anche tutti gli attributi "di base"

public interface UserService {
    public long createUser(String loginName, String password);
    public Set<Long> getFilesOfUser(long userId);
    public String getNameOfUser(long userId);
    public void setNameOfUser(long userId, String name);
}

Questo aggiunge un bel po 'di codice (e probabilmente un carico aggiuntivo al DB), ma protegge anche da StaleData- o LazyInitializationException.

Subquestion: come - se si utilizza il primo caso - l'aggiornamento, il recupero, ecc. devono essere implementati? Mi vengono in mente due metodi

In primo luogo, potrei provare ad aggiornare manualmente il riferimento originale (perché cose come i proxy di raccolta, la versione ecc. si aggiornano solo sull'oggetto restituito, non sul riferimento originale). Questo semplifica l'utilizzo di tali entità direttamente nell'interfaccia utente

public Set<MediaFile> getFilesOfUser(User user) {
    Set<MediaFile> files = user.getFiles();
    if(!Hibernate.isInitialized(files)) {
        user.setFiles(userDao.findOne(user.getId()).getFiles()); // findOne to re-attach to the session
    return ImmutableSet.<MediaFile> copyOf(files);
}

ma anche

public Set<MediaFile> getFilesOfUser(User user) {
    return ImmutableSet.<MediaFile> copyOf(getUser(user.getId()).getFiles());
}

La maggiore differenza in queste viene mostrata quando si utilizza il controllo delle versioni. Nel primo caso, devo aggiornare manualmente la versione ecc. ( user.setVersion(attachedUser.getVersion()) ) dopo ogni aggiornamento, nel secondo caso, una chiamata di aggiornamento deve recuperare l'entità ogni volta solo per impostare il valore:

public void update(User user) {
    User attachedUser = getUser(user.getId());
    attachedUser.setName(user.getName());
    attachedUser.setPasswordHash(user.getPasswordHash());
    // etc etc
    userDao.save(attachedUser);
}

Questo non sembra molto efficiente / non intuitivo.

Qual è il modo corretto di andare?

Modifica

Solo un'altra idea riguardava il metodo del "solo ID di ritorno": i servizi potevano esporre un metodo pubblico "get

posta incaseoftrouble 07.10.2013 - 14:48
fonte

1 risposta

1

Quindi, ho modificato la maggior parte del mio codice nell'approccio "usa solo l'id". Durante la riscrittura ho capito qual era il mio errore: ho interpretato i riferimenti alle entità come una rappresentazione del grafico dell'oggetto, mentre (penso) questi rappresentano solo uno snapshot di (un sottoinsieme di) il grafico corrente , con nessuna garanzia da aggiornare. Progettare il codice "UI-Level" attorno a questo risolve la maggior parte dei miei problemi con poco o nessun lavoro:

Tutti gli eventi, i costruttori, ecc. passano semplicemente gli id delle entità a cui si riferiscono. Anche i metodi del livello di servizio accettano solo ID come argomenti. Se un'istanza ha bisogno di informazioni di un'entità, la recupera su richiesta e scarta l'oggetto entità il più presto possibile , il che significa che tali riferimenti non sono mai mantenuti al di fuori dell'ambito di un metodo (si possono fare eccezioni se sono sicuro che i valori che stai leggendo non vengono mai cambiati, ma poi, perché vorresti leggerli di nuovo, comunque). Ciò implica anche: il livello di servizio restituisce le entità dopo una modifica, ma anche quegli oggetti non dovrebbero mai essere salvati più a lungo del necessario.

Esempio:

public MediaFile addFileToUser(long userId, File file, FileMetaData fileMetadata);
public User createUser(String loginName, String password, String fullName, String company);
public Set<MediaFile> getFilesOfUser(long userId);

Non sono sicuro che questo sia l'approccio raccomandato, ma dal mio punto di vista sembra ok. Non ho idea dell'impatto sulle prestazioni (non so come funzioni il caching in ibernazione, ma sembra logico assumere il caching per id, quindi forse questo approccio è anche abbastanza veloce)

    
risposta data 07.10.2013 - 16:17
fonte

Leggi altre domande sui tag