Quelle est la façon la plus simple d'ignorer un champ JPA pendant la persistance?

281

Je recherche essentiellement une annotation de type "@Ignore" avec laquelle je peux empêcher la persistance d'un champ particulier. Comment cela peut il etre accompli?

m2o
la source

Réponses:

450

@Transient répond à vos besoins.

cletus
la source
20
Mais alors jackson ne sérialisera pas le champ lors de la conversion en JSON ... comment résoudre?
MobileMon
cela dépend de la conception de votre application. si vous annotez votre classe d'entité - elle s'applique partout; mais si vous annotez dao qui utilise l'entité - c'est une autre histoire. en bref: utilisez DAO lorsque vous avez plusieurs stockages
Andrii Plotnikov
Ne fonctionne pas sur les champs de liste. Pour les champs de type Collection, hibernate crée en fait une nouvelle table intermédiaire. Une solution pour cela?
zulkarnain shah
@MobileMon Vous pouvez le résoudre, consultez ma réponse stackoverflow.com/a/41850392/3871754
Kamil Nekanowicz
@MobileMon De préférence, vous ne devriez pas utiliser la même entité à la fois pour la base de données et l'API (responsabilité unique).
Jacek Ślimok
127

Pour ignorer un champ, annotez-le avec @Transientafin qu'il ne soit pas mappé en hibernation.

mais jackson ne sérialisera pas le champ lors de la conversion en JSON.

Si vous avez besoin de mélanger JPA avec JSON (omis par JPA mais toujours inclus dans Jackson), utilisez @JsonInclude:

@JsonInclude()
@Transient
private String token;

POINTE:

Vous pouvez également utiliser JsonInclude.Include.NON_NULL et masquer les champs dans JSON pendant la désérialisation lorsque token == null:

@JsonInclude(JsonInclude.Include.NON_NULL)
@Transient
private String token;
Kamil Nekanowicz
la source
3
J'utilise JAX-RS 2.0.1 / Jersey 2.25.1 / Jackson 2.8.7 et avec cette pile @JsonIncluden'est pas nécessaire: les @Transientchamps sont toujours inclus dans JSON. (Vous avez quand même obtenu mon vote: la technique elle-même pourrait être très utile dans d'autres circonstances).
DKroot
2
Je l'ai fait sur Spring Boot
Kamil Nekanowicz
salut, j'essaye d'utiliser ceci, mais il n'est pas sérialisé avec le champ @Transient
Mohammad
@Mohammad ajouter une annotation @JsonInclude ()
Kamil Nekanowicz
J'ai essayé cela dans Spring Boot 2.1.7 mais cela n'a pas fonctionné. Mais vous obtenez toujours mon vote car cela peut fonctionner dans d'autres scénarios.
Aditya Ekbote
25

Pour ignorer un champ, annotez-le avec @Transientafin qu'il ne soit pas mappé en hibernation.
Source: Hibernate Annotations .

Jonathan
la source
19

Cette réponse arrive un peu tard, mais elle complète la réponse.

Afin d'éviter qu'un champ d'une entité ne persiste dans la base de données, on peut utiliser l'un des deux mécanismes:

@Transient - l'annotation JPA marquant un champ comme non persistant

mot-clé transitoire en java. Attention - l'utilisation de ce mot-clé empêchera le champ d'être utilisé avec tout mécanisme de sérialisation de java. Donc, si le champ doit être sérialisé, vous feriez mieux d'utiliser uniquement l'annotation @Transient .

Olimpiu POP
la source
1
que diriez-vous que je veux juste ignorer la persistance sur la méthode get?. Par exemple: myjparepository.save () sauvegardera le modèle comme d'habitude, et myjparepository.find (id) ignorera le champ que je veux?
xtiger
@xtiger, vous pouvez créer une requête personnalisée pour archiver cette fonctionnalité. Voici un exemple de lien
Mohale
7

Il existe plusieurs solutions selon le type d'attribut d'entité.

Attributs de base

Considérez que vous avez le accounttableau suivant :

La table des comptes

Le accounttableau est mappé à l' Accountentité comme ceci:

@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
}

Les attributs des entités de base sont mis en correspondance avec des colonnes de table, donc des propriétés telles que id, iban, centssont des attributs de base.

Mais dollars, interestCentset interestDollarssont des propriétés calculées, afin que vous les annoter avec @Transientles exclure de SELECT, INSERT, UPDATE et DELETE SQL.

Ainsi, pour les attributs de base, vous devez utiliser @Transientafin d'exclure une propriété donnée de la persistance.

Pour plus de détails sur les attributs d'entité calculés, consultez cet article .

Les associations

En supposant que vous disposez des éléments suivants postet des post_commenttableaux:

Les tables post et post_comment

Vous souhaitez mapper l' lastestCommentassociation dans l' Postentité à la dernière PostCommententité ajoutée.

Pour ce faire, vous pouvez utiliser l' @JoinFormulaannotation:

@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
}

Lors de la récupération de l' Postentité, vous pouvez voir que le latestCommentest récupéré, mais si vous souhaitez le modifier, le changement va être ignoré.

Ainsi, pour les associations, vous pouvez utiliser @JoinFormulapour ignorer les opérations d'écriture tout en autorisant la lecture de l'association.

Pour plus de détails sur les associations calculées, consultez cet article .

@MapsId

Une autre façon d'ignorer les associations déjà mappées par l'identifiant d'entité est d'utiliser @MapsId.

Par exemple, considérez la relation de table un-à-un suivante:

Les tables post et post_details

L' PostDetailsentité est mappée comme ceci:

@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
}

Notez que l' idattribut et l' postassociation mappent la même colonne de base de données, qui est la post_detailscolonne Clé primaire.

Pour exclure l' idattribut, l' @MapsIdannotation indiquera à Hibernate que l' postassociation prend en charge la valeur de la colonne Clé primaire de la table.

Ainsi, lorsque l'identifiant d'entité et une association partagent la même colonne, vous pouvez utiliser @MapsIdpour ignorer l'attribut d'identifiant d'entité et utiliser l'association à la place.

Pour plus de détails sur @MapsId, consultez cet article .

En utilisant insertable = false, updatable = false

Une autre option consiste à utiliser insertable = false, updatable = falsepour l'association que vous souhaitez ignorer par Hibernate.

Par exemple, nous pouvons mapper l'association biunivoque précédente comme ceci:

@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();
        }
    }
}

Les attributs insertableet updatablede l' @JoinColumnannotation indiqueront à Hibernate d'ignorer l' postassociation car l'identifiant d'entité prend en charge la post_idcolonne Clé primaire.

Vlad Mihalcea
la source
Notez que l'attribut id et la post-association mappent la même colonne de base de données, qui est la colonne de clé primaire post_comment. Est-ce correct dans le contexte des tableaux postet post_detailscomme exemple?
David
1
Merci de l'avoir repéré. Je l'ai corrigé.
Vlad Mihalcea
3

Pour compléter les réponses ci-dessus, j'ai eu le cas en utilisant un fichier de mappage XML où ni le @Transientni ne transientfonctionnait ... J'ai dû mettre les informations transitoires dans le fichier xml:

<attributes>
(...)
    <transient name="field" />
</attributes>
Vinze
la source
2

Aucune des réponses ci-dessus n'a fonctionné pour moi en utilisant Hibernate 5.2.10, Jersey 2.25.1 et Jackson 2.8.9. J'ai finalement trouvé la réponse (en quelque sorte, ils font référence à hibernate4module mais cela fonctionne aussi pour 5) ici . Aucune des annotations Json n'a fonctionné du tout @Transient. Apparemment, Jackson2 est suffisamment «intelligent» pour ignorer les choses marquées par, à @Transientmoins que vous ne le lui disiez explicitement. La clé était d'ajouter le module hibernate5 (que j'utilisais pour gérer d'autres annotations Hibernate) et de désactiver la USE_TRANSIENT_ANNOTATIONfonctionnalité dans mon application Jersey:

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

Voici la dépendance du Hibernate5Module:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-hibernate5</artifactId>
    <version>2.8.9</version>
</dependency>
Hodglem
la source
Après avoir essayé toutes les autres méthodes mentionnées ci-dessus et d'autres, @JsonProperty @JsonInclude @JsonSerialize + @JsonDeserialize mapper.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, false));cette solution a finalement fonctionné. Merci!
Aceonline
1

Parfois, vous voulez:

  1. Sérialiser une colonne
  2. Ignorer la persistance de la colonne:

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

Un bon scénario est lorsqu'une certaine colonne est automatiquement calculée en utilisant d'autres valeurs de colonne

Obothlale
la source