Qual è il modo più semplice per ignorare un campo JPA durante la persistenza?


281

Sto essenzialmente cercando un'annotazione di tipo "@Ignore" con la quale posso fermare la persistenza di un determinato campo. Come si può ottenere questo?

Risposte:


450

@Transient conforme alle tue esigenze.


20
Ma poi Jackson non serializzerà il campo durante la conversione in JSON ... come risolvere?
MobileMon,

dipende dalla progettazione dell'app. se annoti la tua classe di entità - si applica ovunque; ma se anochi il dao che usa l'entità - è un'altra storia. in breve: usa DAO quando hai più depositi
Andrii Plotnikov

Non funziona sui campi Elenco. Per i campi di tipo Collection, l'ibernazione crea effettivamente una nuova tabella intermedia. Qualche soluzione per questo?
zulkarnain shah,

@MobileMon Si può risolverlo, controllare la mia risposta stackoverflow.com/a/41850392/3871754
Kamil Nekanowicz

@MobileMon Preferibilmente non dovresti usare la stessa entità per scopi sia di database che di API (singola responsabilità).
Jacek Ślimok,

127

Per ignorare un campo, annotalo in @Transientmodo che non venga mappato dall'ibernazione.

ma poi jackson non serializzerà il campo durante la conversione in JSON.

Se hai bisogno di mescolare JPA con JSON (ometti da JPA ma includi ancora in Jackson) usa @JsonInclude:

@JsonInclude()
@Transient
private String token;

MANCIA:

Puoi anche usare JsonInclude.Include.NON_NULL e nascondere i campi in JSON durante la deserializzazione quando token == null:

@JsonInclude(JsonInclude.Include.NON_NULL)
@Transient
private String token;

3
Sto eseguendo JAX-RS 2.0.1 / Jersey 2.25.1 / Jackson 2.8.7 e con quello stack @JsonIncludenon è necessario: i @Transientcampi sono ancora inclusi in JSON. (Hai ancora ottenuto il mio voto: la tecnica stessa potrebbe essere molto utile in altre circostanze).
DKroot,

2
L'ho fatto su Spring Boot
Kamil Nekanowicz il

ciao, sto cercando di usare questo, ma non si sta serializzando con il campo @Transient
Mohammad

@Mohammad aggiungi annotazione @JsonInclude ()
Kamil Nekanowicz

Ho provato questo in Spring Boot 2.1.7 ma non ha funzionato. Ma ottieni ancora il mio voto perché potrebbe funzionare in altri scenari.
Aditya Ekbote,


19

Questa risposta arriva un po 'in ritardo, ma completa la risposta.

Per evitare che un campo di un'entità sia persistente nel DB, è possibile utilizzare uno dei due meccanismi:

@Transient : l'annotazione JPA che contrassegna un campo come non persistente

parola chiave transitoria in Java. Attenzione: utilizzando questa parola chiave, il campo non verrà utilizzato con alcun meccanismo di serializzazione da Java. Quindi, se il campo deve essere serializzato, è meglio usare solo l'annotazione @Transient .


1
che ne dite voglio solo ignorare la persistenza sul metodo get ?. Ad esempio: myjparepository.save () salverà il modello come al solito e myjparepository.find (id) ignorerà il campo che desidero?
xtiger,

@xtiger è possibile creare una query personalizzata per archiviare tale funzionalità. Ecco un link di
Mohale,

7

Esistono più soluzioni a seconda del tipo di attributo dell'entità.

Attributi di base

Considera di avere la seguente accounttabella:

La tabella degli account

La accounttabella è mappata Accountall'entità in questo modo:

@Entity(name = "Account")
public class Account {

    @Id
    private Long id;

    @ManyToOne
    private User owner;

    private String iban;

    private long cents;

    private double interestRate;

    private Timestamp createdOn;

    @Transient
    private double dollars;

    @Transient
    private long interestCents;

    @Transient
    private double interestDollars;

    @PostLoad
    private void postLoad() {
        this.dollars = cents / 100D;

        long months = createdOn.toLocalDateTime()
            .until(LocalDateTime.now(), ChronoUnit.MONTHS);
        double interestUnrounded = ( ( interestRate / 100D ) * cents * months ) / 12;
        this.interestCents = BigDecimal.valueOf(interestUnrounded)
            .setScale(0, BigDecimal.ROUND_HALF_EVEN).longValue();

        this.interestDollars = interestCents / 100D;
    }

    //Getters and setters omitted for brevity
}

Gli attributi di base dell'entità sono mappati colonne della tabella, così immobili come id, iban, centssono attributi di base.

Ma la dollars, interestCentse interestDollarsvengono calcolati proprietà, in modo da poterli annotare con @Transientescluderle dal SELECT, INSERT, UPDATE e DELETE istruzioni SQL.

Pertanto, per gli attributi di base, è necessario utilizzare @Transientper escludere la persistenza di una determinata proprietà.

Per ulteriori dettagli sugli attributi di entità calcolati, consulta questo articolo .

associazioni

Supponendo di avere quanto segue poste le post_commenttabelle:

Le tabelle post e post_comment

Si desidera mappare l' lastestCommentassociazione Postnell'entità all'ultima PostCommententità che è stata aggiunta.

Per fare ciò, puoi usare l' @JoinFormulaannotazione:

@Entity(name = "Post")
@Table(name = "post")
public class Post {

    @Id
    private Long id;

    private String title;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinFormula("(" +
        "SELECT pc.id " +
        "FROM post_comment pc " +
        "WHERE pc.post_id = id " +
        "ORDER BY pc.created_on DESC " +
        "LIMIT 1" +
    ")")
    private PostComment latestComment;

    //Getters and setters omitted for brevity
}

Quando recuperi l' Postentità, puoi vedere che latestCommentè stata recuperata, ma se vuoi modificarla, la modifica verrà ignorata.

Pertanto, per le associazioni, è possibile utilizzare @JoinFormulaper ignorare le operazioni di scrittura pur consentendo la lettura dell'associazione.

Per maggiori dettagli sulle associazioni calcolate, consulta questo articolo .

@MapsId

Un altro modo per ignorare le associazioni che sono già mappate dall'identificatore di entità è usare @MapsId.

Ad esempio, considera la seguente relazione di tabella uno a uno:

Le tabelle post e post_details

L' PostDetailsentità è mappata in questo modo:

@Entity(name = "PostDetails")
@Table(name = "post_details")
public class PostDetails {

    @Id
    private Long id;

    @Column(name = "created_on")
    private Date createdOn;

    @Column(name = "created_by")
    private String createdBy;

    @OneToOne(fetch = FetchType.LAZY)
    @MapsId
    private Post post;

    public PostDetails() {}

    public PostDetails(String createdBy) {
        createdOn = new Date();
        this.createdBy = createdBy;
    }

    //Getters and setters omitted for brevity
}

Si noti che sia l' idattributo che l' postassociazione mappano la stessa colonna del database, che è la post_detailscolonna Chiave primaria.

Per escludere l' idattributo, l' @MapsIdannotazione dirà a Hibernate che l' postassociazione si occupa del valore della colonna Chiave primaria della tabella.

Pertanto, quando l'identificatore di entità e un'associazione condividono la stessa colonna, è possibile utilizzare @MapsIdper ignorare l'attributo identificatore di entità e utilizzare invece l'associazione.

Per maggiori dettagli @MapsId, consulta questo articolo .

utilizzando insertable = false, updatable = false

Un'altra opzione è utilizzare insertable = false, updatable = falseper l'associazione che si desidera venga ignorata da Hibernate.

Ad esempio, possiamo mappare la precedente associazione one-to-one in questo modo:

@Entity(name = "PostDetails")
@Table(name = "post_details")
public class PostDetails {

    @Id
    @Column(name = "post_id")
    private Long id;

    @Column(name = "created_on")
    private Date createdOn;

    @Column(name = "created_by")
    private String createdBy;

    @OneToOne
    @JoinColumn(name = "post_id", insertable = false, updatable = false)
    private Post post;

    //Getters and setters omitted for brevity

    public void setPost(Post post) {
        this.post = post;
        if (post != null) {
            this.id = post.getId();
        }
    }
}

Gli attributi insertablee dell'annotazione indicano a Hibernate di ignorare l' associazione poiché l'identificatore di entità si occupa della colonna Chiave primaria.updatable@JoinColumnpostpost_id


Si noti che sia l'attributo id che l'associazione post associano la stessa colonna del database, che è la colonna Chiave primaria post_comment. È corretto nel contesto di poste post_detailstabelle come esempio?
David

1
Grazie per averlo individuato. L'ho riparato.
Vlad Mihalcea,

3

Per completare le risposte di cui sopra, ho avuto il caso di utilizzare un file di mapping XML in cui né il @Transientné ha transientfunzionato ... Ho dovuto inserire le informazioni temporanee nel file XML:

<attributes>
(...)
    <transient name="field" />
</attributes>

2

Nessuna delle risposte di cui sopra ha funzionato per me usando Hibernate 5.2.10, Jersey 2.25.1 e Jackson 2.8.9. Ho finalmente trovato la risposta (in qualche modo, fanno riferimento a hibernate4module ma funziona anche per 5) qui . Nessuna delle annotazioni Json ha funzionato affatto @Transient. A quanto pare Jackson2 è abbastanza 'intelligente' da ignorare gentilmente le cose contrassegnate con a @Transientmeno che non gli dica esplicitamente di non farlo. La chiave era aggiungere il modulo hibernate5 (che stavo usando per gestire altre annotazioni Hibernate) e disabilitare la USE_TRANSIENT_ANNOTATIONfunzione nella mia applicazione Jersey:

ObjectMapper jacksonObjectMapper = new ObjectMapper();
Hibernate5Module jacksonHibernateModule = new Hibernate5Module();
jacksonHibernateModule.disable(Hibernate5Module.Feature.USE_TRANSIENT_ANNOTATION);
jacksonObjectMapper.registerModule(jacksonHibernateModule);  

Ecco la dipendenza per il modulo Hibernate5:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-hibernate5</artifactId>
    <version>2.8.9</version>
</dependency>

Dopo aver provato ogni altro metodo menzionato sopra e altri, @JsonProperty @JsonInclude @JsonSerialize + @JsonDeserialize mapper.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, false));questa soluzione ha finalmente funzionato. Grazie!
Aceonline,

1

A volte vuoi:

  1. Serializza una colonna
  2. Ignora la colonna dal persistere:

Uso @Column(name = "columnName", insertable = false, updatable = false)

Un buon scenario è quando una determinata colonna viene calcolata automaticamente usando altri valori di colonna

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.