Jaki jest najłatwiejszy sposób zignorowania pola JPA podczas trwałości?


281

Zasadniczo szukam adnotacji typu „@Ignore”, dzięki której mogę zatrzymać utrwalenie określonego pola. Jak można to osiągnąć?

Odpowiedzi:


450

@Transient odpowiada Twoim potrzebom.


20
Ale wtedy Jackson nie serializuje pola podczas konwersji na JSON ... jak rozwiązać?
MobileMon,

zależy to od zaprojektowania Twojej aplikacji. jeśli dodasz adnotację do swojej klasy bytu - ma ona zastosowanie wszędzie; ale jeśli zanotujesz dao używającego bytu - to inna historia. w skrócie: używaj DAO, gdy masz wiele magazynów
Andrii Plotnikov

Nie działa na polach listy. W przypadku pól typu Kolekcja hibernacja faktycznie tworzy nową tabelę pośrednią. Jakieś obejście tego?
zulkarnain shah

@MobileMon Możesz to rozwiązać, sprawdź moją odpowiedź stackoverflow.com/a/41850392/3871754
Kamil Nekanowicz

@MobileMon Najlepiej nie powinieneś używać tego samego podmiotu zarówno do celów bazy danych, jak i API (jedna odpowiedzialność).
Jacek Ślimok

127

Aby zignorować pole, opatrz je adnotacją, @Transientaby nie było mapowane przez hibernację.

ale wtedy Jackson nie serializuje pola podczas konwersji na JSON.

Jeśli potrzebujesz wymieszać JPA z JSON (pomiń JPA, ale nadal dołączasz do Jacksona) użyj @JsonInclude:

@JsonInclude()
@Transient
private String token;

WSKAZÓWKA:

Możesz także użyć JsonInclude.Include.NON_NULL i ukryć pola w JSON podczas deserializacji, gdy token == null:

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

3
Używam JAX-RS 2.0.1 / Jersey 2.25.1 / Jackson 2.8.7 i przy tym stosie @JsonIncludenie jest konieczne: @Transientpola są nadal zawarte w JSON. (Nadal masz mój głos: sama technika może być bardzo przydatna w innych okolicznościach).
DKroot

2
Zrobiłem to na Spring Boot
Kamil Nekanowicz,

cześć, próbuję tego użyć, ale nie jest serializowany z polem @Transient
Mohammad

@Mohammad dodaj adnotację @JsonInclude ()
Kamil Nekanowicz

Próbowałem tego w Spring Boot 2.1.7, ale nie działało. Ale wciąż dostajesz mój głos, ponieważ może działać w innych scenariuszach.
Aditya Ekbote


19

Ta odpowiedź przychodzi trochę później, ale uzupełnia odpowiedź.

Aby uniknąć utrwalenia pola z encji w DB, można użyć jednego z dwóch mechanizmów:

@Transient - adnotacja JPA oznaczająca pole jako nietrwałe

przejściowe słowo kluczowe w java. Uwaga - użycie tego słowa kluczowego uniemożliwi użycie pola z dowolnym mechanizmem serializacji z języka Java. Tak więc, jeśli pole musi być zserializowane, lepiej użyć tylkoadnotacji @Transient .


1
a może po prostu chcę zignorować upór przy pobieraniu metody ?. Na przykład: myjparepository.save () zapisze model jak zwykle, a myjparepository.find (id) zignoruje pole, które chcę?
xtiger

@xtiger możesz utworzyć niestandardowe zapytanie w celu zarchiwizowania tej funkcjonalności. Oto przykładowy link
Mohale

7

Istnieje wiele rozwiązań w zależności od typu atrybutu encji.

Podstawowe atrybuty

Rozważ, że masz następującą accounttabelę:

Tabela kont

accountStół jest mapowany do Accountpodmiotu takiego:

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

Podstawowe atrybuty podmiotu są odwzorowane do kolumn tabeli, więc właściwości podoba id, iban, centsto podstawowe atrybuty.

Ale dollars, interestCentsi interestDollarssą obliczane właściwości, więc opisywać je @Transientwyłączyć je z SELECT, INSERT, UPDATE i DELETE SQL.

Zatem w przypadku podstawowych atrybutów należy użyć @Transient, aby wykluczyć utrwalenie danej właściwości.

Aby uzyskać więcej informacji na temat atrybutów jednostki obliczanej, sprawdź ten artykuł .

Wspomnienia

Zakładając, że masz następujące elementy posti post_commenttabele:

Tabele post i post_comment

Chcesz zmapować lastestComment powiązanie w Postencji na najnowszą PostComment, która została dodana.

Aby to zrobić, możesz użyć @JoinFormula adnotacji:

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

Podczas pobierania Post encji widać, że latestCommentjest ona pobierana, ale jeśli chcesz ją zmodyfikować, zmiana zostanie zignorowana.

Tak więc, w przypadku skojarzeń, możesz użyć @JoinFormula aby zignorować operacje zapisu, jednocześnie umożliwiając odczytanie powiązania.

Aby uzyskać więcej informacji o skojarzeniach obliczeniowych, sprawdź ten artykuł .

@MapsId

Innym sposobem na zignorowanie powiązań, które są już zamapowane przez identyfikator jednostki, jest użycie @MapsId .

Rozważmy na przykład następującą relację jeden do jednego:

Tabele post i post_details

PostDetailsJednostka jest odwzorowany tak:

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

Zauważ, że zarówno idatrybut, jak i postpowiązanie odwzorowują tę samą kolumnę bazy danych, która jest post_detailskolumną Klucz podstawowy.

Aby wykluczyć idatrybut, @MapsIdadnotacja poinformuje Hibernację, że postskojarzenie zajmuje się wartością kolumny Klucz podstawowy tabeli.

Tak więc, gdy identyfikator jednostki i powiązanie mają tę samą kolumnę, możesz użyć, @MapsIdaby zignorować atrybut identyfikatora jednostki i zamiast tego użyć powiązania.

Aby uzyskać więcej informacji na temat @MapsId, sprawdź ten artykuł .

Za pomocą insertable = false, updatable = false

Inną opcją jest użycie insertable = false, updatable = false powiązania, które ma być ignorowane przez Hibernację.

Na przykład możemy zmapować poprzednie powiązanie jeden do jednego w następujący sposób:

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

Te insertablei updatableatrybuty @JoinColumnadnotacji poinstruuje Hibernate zignorować poststowarzyszenie ponieważ podmiot identyfikator dba o post_idkolumnie klucz podstawowy.


Zauważ, że zarówno atrybut id, jak i asocjacja post mapują tę samą kolumnę bazy danych, która jest kolumną Klucz podstawowy post_comment. Czy jest to poprawne w kontekście posti post_detailstabel jako przykładu?
David

1
Dzięki za wykrycie tego. Naprawiłem to.
Vlad Mihalcea

3

Aby wypełnić powyższe odpowiedzi, miałem przypadek przy użyciu pliku odwzorowania XML, w którym ani @Transientnor nie transientdziałał ... Musiałem umieścić informacje przejściowe w pliku xml:

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

2

Żadna z powyższych odpowiedzi nie działała dla mnie przy użyciu Hibernacji 5.2.10, Jersey 2.25.1 i Jackson 2.8.9. W końcu znalazłem odpowiedź (w pewnym sensie, odwołują się one do hibernacji4moduła, ale działa również dla 5) tutaj . Żadna adnotacja Jsona w ogóle nie działała @Transient. Najwyraźniej Jackson2 jest „wystarczająco inteligentny”, aby uprzejmie zignorować rzeczy oznaczone, @Transientchyba że wyraźnie to powiesz. Kluczem było dodanie modułu hibernacji5 (którego używałem do obsługi innych adnotacji Hibernacji) i wyłączenie tej USE_TRANSIENT_ANNOTATIONfunkcji w mojej aplikacji Jersey:

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

Oto zależność dla Hibernate5Module:

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

Po wypróbowaniu każdej innej metody wspomnianej powyżej i innych, @JsonProperty @JsonInclude @JsonSerialize + @JsonDeserialize mapper.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, false));to rozwiązanie w końcu zadziałało. Dzięki!
Aceonline,

1

Czasami chcesz:

  1. Serializuj kolumnę
  2. Zignoruj ​​utrwalenie kolumny:

Posługiwać się @Column(name = "columnName", insertable = false, updatable = false)

Dobrym scenariuszem jest automatyczne obliczanie określonej kolumny przy użyciu innych wartości kolumny

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.