Kalıcılık sırasında bir JPA alanını yok saymanın en kolay yolu nedir?


281

Temelde ben belirli bir alan kalıcı olmasını engelleyebilir bir "@Ignore" türü ek açıklama arıyorum. Bu nasıl başarılabilir?

Yanıtlar:


450

@Transient ihtiyaçlarınızı karşılar.


20
Ama sonra jackson JSON dönüştürürken alan seri hale getirmez ... nasıl çözmek?
MobileMon

Bu, uygulama tasarımınıza bağlıdır. varlık sınıfınıza ek açıklama eklediğinizde - her yerde geçerlidir; ama varlık kullanan dao'ya açıklama yaparsanız - bu başka bir hikaye. Kısacası: birden fazla depolamanız olduğunda
DAO'yu

Liste alanlarında çalışmaz. Koleksiyon türü alanları için hazırda bekletme modu aslında yeni bir ara tablo oluşturur. Bunun için bir çözüm var mı?
zulkarnain shah

@MobileMon Çözebilirsin, cevabımı kontrol et stackoverflow.com/a/41850392/3871754
Kamil Nekanowicz

@MobileMon Tercihen aynı varlığı hem veritabanı hem de API amaçları için kullanmamalısınız (tek sorumluluk).
Jacek Ślimok

127

Bir alanı yok saymak için, @Transienthazırda bekletme modu ile eşlenmeyecek şekilde ek açıklama ekleyin.

ancak jackson JSON'a dönüştürürken alanı serileştirmez .

JPA'yı JSON ile karıştırmanız gerekiyorsa (JPA tarafından atlayın, ancak yine de Jackson'a ekleyin) kullanın @JsonInclude:

@JsonInclude()
@Transient
private String token;

İPUCU:

Ayrıca, aşağıdaki durumlarda seriyi kaldırma işlemi sırasında JsonInclude.Include.NON_NULL kullanabilir ve JSON'daki alanları gizleyebilirsiniz token == null:

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

3
JAX-RS 2.0.1 / Jersey 2.25.1 / Jackson 2.8.7 kullanıyorum ve bu yığın @JsonIncludeile gerekli değildir: @Transientalanlar hala JSON'a dahildir. (Hala benim oyumu aldım: tekniğin kendisi diğer durumlarda çok yararlı olabilir).
DKroot

2
Spring Boot üzerinde yaptım
Kamil Nekanowicz

merhaba, bunu kullanmaya çalışıyorum, ama @Transient alanı ile seri hale getirilmiyor
Mohammad

@Mohammad ek açıklama ekle @JsonInclude ()
Kamil Nekanowicz

Bunu Spring Boot 2.1.7'de denedim ama işe yaramadı. Ama yine de oyumu alıyorsunuz çünkü başka senaryolarda da işe yarayabilir.
Aditya Ekbote


19

Bu cevap biraz geç gelir, ancak cevabı tamamlar.

Bir kuruluştaki bir alanın DB'de kalıcı olmasını önlemek için, iki mekanizmadan biri kullanılabilir:

@Transient - JPA ek açıklaması bir alanı kalıcı olarak işaretliyor

geçici anahtar kelime. Dikkat - bu anahtar kelimenin kullanılması, alanın java herhangi bir serileştirme mekanizması ile kullanılmasını önleyecektir. Bu nedenle, alanın serileştirilmesi gerekiyorsa, yalnızca @Transient ek açıklamasınıkullanmanız daha iyi olur.


1
nasıl olsun sadece yöntemi kalıcılığını yoksaymak istiyorum ?. Örneğin: myjparepository.save () modeli her zamanki gibi kaydeder ve myjparepository.find (id) istediğim alanı yoksayar mı?
xtiger

@xtiger bu işlevselliği arşivlemek için özel bir sorgu oluşturabilirsiniz. İşte bir örnek bağlantı
Mohale

7

Obje öznitelik türüne bağlı olarak birden fazla çözüm vardır.

Temel özellikler

Aşağıdaki accounttabloya sahip olduğunuzu düşünün :

Hesap tablosu

accountTablo eşleştirilir Accountböyle varlık:

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

Temel varlık nitelikleri özellikleri seversin, tablo sütunları eşlenir id, iban, centstemel özelliklerdir.

Ama dollars, interestCentsve interestDollarssen onları açıklama yüzden, özellikler hesaplanır @TransientSELECT, INSERT, UPDATE ve DELETE SQL ifadelerinin dışında tutmak.

Bu nedenle, temel özellikler için, @Transientbelirli bir mülkün kalıcı olmasını engellemek için kullanmanız gerekir .

Hesaplanan varlık özellikleri hakkında daha fazla bilgi için bu makaleye göz atın .

Dernekler

Aşağıdakilere sahip olduğunuzu varsayarsak post ve post_commenttablolara :

Yayın ve yayın sonrası tabloları

Varlıktaki lastestCommentilişkilendirmeyi eklenen Posten son PostCommentvarlıkla eşleştirmek istiyorsunuz .

Bunu yapmak için, @JoinFormula ek açıklamayı :

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

Getirirken PostlatestComment , getirildiğini görebilirsiniz , ancak değiştirmek isterseniz, değişiklik yok sayılır.

Böylece, ilişkilendirmeler için, ilişkilendirmeyi @JoinFormulaokumaya izin verirken yazma işlemlerini yoksayabilirsiniz.

Hesaplanan dernekler hakkında daha fazla bilgi için bu makaleye göz atın .

@MapsId

Varlık tanımlayıcısı tarafından önceden eşleştirilen ilişkilendirmeleri yok saymanın başka bir yolu da @MapsId .

Örneğin, aşağıdaki bire bir tablo ilişkisini düşünün:

Yazı ve yazı_detayları tabloları

PostDetailsVarlık böyle eşleştirilmiş:

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

Hem idözniteliğin hem de postilişkilendirmenin aynı veritabanı sütununu eşlediğine dikkat edin;post_details Birincil Anahtar sütunu eşlediğine dikkat edin.

idÖzniteliği hariç tutmak için @MapsIdek açıklama, Hazırda Beklet'e postilişkilendirmenin Birincil Anahtar sütun değeri tablosuyla ilgilendiğini bildirir .

Bu nedenle, varlık tanımlayıcısı ve ilişkilendirme aynı sütunu paylaştığında @MapsId, varlık tanımlayıcısı niteliğini yok saymak ve bunun yerine ilişkilendirmeyi kullanabilirsiniz.

Hakkında daha fazla bilgi için bu makaleye@MapsId göz atın .

kullanma insertable = false, updatable = false

Başka bir seçenek, insertable = false, updatable = falseHazırda Bekletme tarafından yoksayılmasını istediğiniz ilişkilendirme için kullanmaktır .

Örneğin, önceki bire bir ilişkilendirmeyi şu şekilde eşleştirebiliriz:

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

insertableVe updatablenitelikleri @JoinColumnaçıklama görmezden Hibernate talimat verir postilgilenir tanımlayıcı varlık beri dernek post_idBirincil Anahtar sütununda.


Hem id özelliğinin hem de post ilişkilendirmesinin, post_comment Birincil Anahtar sütunu olan aynı veritabanı sütununu eşlediğine dikkat edin. Bu örnek postve post_detailstablolar bağlamında doğru mu?
David

1
Tespit ettiğiniz için teşekkürler. Onardım.
Vlad Mihalcea

3

Yukarıdaki cevapları tamamlamak için, ne ne ne @Transientde transientçalıştı bir XML eşleme dosyası kullanarak dava vardı ... Ben xml dosyasına geçici bilgi koymak zorunda kaldı:

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

2

Yukarıdaki cevapların hiçbiri Hibernate 5.2.10, Jersey 2.25.1 ve Jackson 2.8.9 kullanarak benim için işe yaramadı. Sonunda cevabı buldum (bir çeşit, hibernate4module'ye referans veriyorlar, ancak 5 için de çalışıyor) burada . Json ek açıklamalarının hiçbiri hiç işe yaramadı @Transient. Görünüşe göre Jackson2, @Transientaçıkça söylemediğiniz sürece işaretlenen şeyleri göz ardı edecek kadar 'akıllı' . Anahtar, hibernate5 modülünü (diğer Hibernate ek açıklamalarıyla uğraşmak için kullandığım) eklemek ve USE_TRANSIENT_ANNOTATIONJersey Uygulamamdaki özelliği devre dışı bırakmaktı :

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

Hibernate5Module bağımlılığı:

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

Yukarıda bahsedilen diğer yöntemlerin hepsini denedikten sonra @JsonProperty @JsonInclude @JsonSerialize + @JsonDeserialize mapper.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, false));bu çözüm nihayet işe yaradı. Teşekkürler!
Ekim'de Aceonline

1

Bazen şunları yapmak istersiniz:

  1. Bir sütunu seri hale getirme
  2. Sütunun kalıcı olmasını yoksay:

kullanım @Column(name = "columnName", insertable = false, updatable = false)

İyi bir senaryo, belirli bir sütunun diğer sütun değerleri kullanılarak otomatik olarak hesaplanmasıdır

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.