Il modo migliore per gestire la relazione Hibernate 1-Many su REST / JSON

4

Problema: una relazione One-to-Many bidirezionale di Hibernate non è facilmente associabile a JSON. Se viene utilizzata la mappatura predefinita (Jackson), esiste un problema di ricorsione infinito in quanto il genitore contiene i figli, ognuno dei quali contiene un riferimento al genitore. Il modo più semplice che ho trovato per risolvere questo problema è contrassegnare l'attributo Many end come @JsonIgnore. Ciò significa che il JSON viene generato come previsto. Tuttavia, quando gli oggetti vengono inviati su un POST REST (ad esempio), ciò significa che i riferimenti ManyToOne vengono persi. Al momento ho solo il servizio REST per ricostruire i riferimenti prima di mantenere i dati, ma mi chiedo se c'è un modo consigliato per farlo automaticamente (usando l'architettura Spring REST).

Per illustrare, ecco alcuni frammenti di codice:

L'entità padre:

@Entity
@Table(name = "wine_case")
public class WineCase {
    @Id @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "wineCase")
    private List<CaseContent> caseContents;
}

L'entità figlio:

@Entity
@Table(name = "case_content")
public class CaseContent {
    @Id @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "wine_case_id", nullable = false)
    @JsonIgnore
    private WineCase wineCase;
}

Il metodo nel controller REST che riceve la richiesta e deve ricostruire le relazioni da CaseContent a WineCase:

@RequestMapping(method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
public @ResponseBody
WineCase createWineCase(@RequestBody WineCase wineCase, HttpServletResponse response) {
    wineCase.setId(null);
    wineCase.getCaseContents().forEach((cc) -> cc.setWineCase(wineCase));
    entityManager.persist(wineCase);
    response.setHeader("Location", "/winecases/" + wineCase.getId());
    return wineCase;
}
    
posta BarrySW19 17.10.2015 - 15:06
fonte

3 risposte

4

Dato che stai usando jackson dovresti dare un'occhiata a questo: link

Con questo modulo, jackson non serializzerà i campi LAZY se non li hai inizializzati, quindi potresti avere i campi LAZY su entrambi i lati per non avere problemi.

Se questa soluzione non si adatta alle tue esigenze (forse vuoi un po 'impaziente). Quindi, anziché usare @JsonIgnore, usa @JsonIgnoreProperties in questo modo:

@Entity
@Table(name = "wine_case")
public class WineCase {
    @Id @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "wineCase")
    @JsonIngoreProperties("wineCase", allowSetters=true)
    private List<CaseContent> caseContents;
}

@Entity
@Table(name = "case_content")
public class CaseContent {
    @Id @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "wine_case_id", nullable = false)
    @JsonIgnoreProperties("caseContents", allowSetters=true)
    private WineCase wineCase;
}

L'esclusione delle proprietà assicurerà di non serializzare i cicli ovunque inizi (cioè da un bambino o da un genitore). I allowSetters consentiranno la deserializzazione delle proprietà, solo la serializzazione ignorerà le proprietà definite nell'annotazione (i allowSetters / Getters esistono dal 2.6).

Tieni presente che attualmente esiste un bug con jsonignoreproperties e oggetti pigri: link . Speriamo che venga risolto per 2.8.

    
risposta data 15.02.2016 - 09:30
fonte
0

Dai un'occhiata alle opzioni della tua libreria JSON - dovrebbe esserci un modo per far sì che identifichi gli oggetti che è già serializzato ed emette un riferimento all'indietro invece di serializzare nuovamente l'oggetto. Questa è quasi certamente la soluzione più semplice al tuo problema.

    
risposta data 18.10.2015 - 05:03
fonte
0

Invece di provare direttamente a restituire tutti i dati, perché non serializzare solo i bambini come identificatori? Questo è il modo in cui i database dei documenti consigliano (o almeno MongoDB ).

Quindi puoi anche includere l'entità completa quando viene richiesta. La specifica jsonapi.org descrive questo comportamento.

Quindi, per impostazione predefinita, restituisce l'entità WineCase con una matrice di identificatori a CaseContent , e solo se la richiesta del client di includere CaseContent include l'intera entità in cui il campo wineCase viene serializzato come un semplice identificatore .

    
risposta data 15.02.2016 - 07:44
fonte

Leggi altre domande sui tag