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.