วิธีที่ง่ายที่สุดในการละเว้นฟิลด์ JPA คืออะไรในระหว่างการคงอยู่


281

ฉันกำลังมองหาคำอธิบายประกอบแบบ "@Ignore" เป็นหลักซึ่งฉันสามารถหยุดฟิลด์ใดฟิลด์หนึ่งจากการถูกยืนยันได้ จะประสบความสำเร็จได้อย่างไร?

คำตอบ:


450

@Transient สอดคล้องกับความต้องการของคุณ


20
แต่แล้วแจ็คสันจะไม่ทำให้เป็นอันดับเขตข้อมูลเมื่อแปลงเป็น JSON ... วิธีแก้ปัญหา?
MobileMon

ขึ้นอยู่กับการออกแบบแอปของคุณ ถ้าคุณใส่คำอธิบายประกอบคลาสเอนทิตี้ของคุณ - มันใช้กับทุกที่ แต่ถ้าคุณใช้ dot ที่ใช้เอนทิตี้ - มันเป็นอีกเรื่องหนึ่ง กล่าวโดยย่อ: ใช้ DAO เมื่อคุณมีคลังเก็บหลายแห่ง
Andrii Plotnikov

ไม่ทำงานในฟิลด์รายการ สำหรับเขตข้อมูลประเภทชุดข้อมูลไฮเบอร์เนตจะสร้างตารางกลางขึ้นมาใหม่ มีวิธีแก้ปัญหาอะไรบ้าง?
zulkarnain shah

@MobileMon คุณสามารถแก้ไขได้ตรวจสอบคำตอบของฉันstackoverflow.com/a/41850392/3871754
Kamil Nekanowicz

@MobileMon ยิ่งคุณไม่ควรใช้เอนทิตีเดียวกันสำหรับทั้งฐานข้อมูลและวัตถุประสงค์ API (ความรับผิดชอบเดี่ยว)
Jacek Ślimok

127

หากต้องการละเว้นฟิลด์ให้ใส่คำอธิบายประกอบด้วย@Transientดังนั้นจะไม่ถูกแมปโดยไฮเบอร์เนต

แต่จากนั้นแจ็คสันจะไม่ทำให้เป็นอันดับเขตข้อมูลเมื่อแปลงเป็น JSON

หากคุณต้องการผสม JPA กับ JSON (ละเว้นโดย JPA แต่ยังรวมอยู่ในแจ็คสัน) ใช้@JsonInclude:

@JsonInclude()
@Transient
private String token;

เคล็ดลับ:

คุณยังสามารถใช้JsonInclude.Include.NON_NULLและซ่อนฟิลด์ใน JSON ระหว่างการดีซีเรียลไลซ์ชันเมื่อ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 และ@JsonIncludeไม่จำเป็นต้องใช้สแต็ก: @Transientเขตข้อมูลยังคงรวมอยู่ใน JSON (คุณยังได้รับคะแนนของฉัน: เทคนิคเองอาจมีประโยชน์มากในสถานการณ์อื่น ๆ )
DKroot

2
ฉันทำมันใน Spring Boot
Kamil Nekanowicz

สวัสดีครับผมกำลังพยายามที่จะใช้นี้ แต่ก็ไม่ได้รับการต่อเนื่องที่มีเขต @Transient
โมฮัมหมัด

@Mohammad เพิ่มคำอธิบายประกอบ @JsonInclude ()
Kamil Nekanowicz

ลองใช้วิธีนี้ใน Spring Boot 2.1.7 แต่ไม่ได้ผล แต่คุณยังคงได้รับคะแนนของฉันเพราะมันอาจทำงานในสถานการณ์อื่น ๆ
Aditya Ekbote

25

หากต้องการละเว้นฟิลด์ให้ใส่คำอธิบายประกอบด้วย@Transientดังนั้นจะไม่ถูกแมปโดยไฮเบอร์เนต
ที่มา: Hibernate คำอธิบายประกอบ


19

คำตอบนี้มาช้านิดหน่อย แต่มันตอบสนองเสร็จสมบูรณ์

เพื่อหลีกเลี่ยงฟิลด์จากเอนทิตีที่จะคงอยู่ในฐานข้อมูลหนึ่งสามารถใช้หนึ่งในสองกลไก:

@Transient - คำอธิบายประกอบ JPA ที่ทำเครื่องหมายฟิลด์ว่าไม่สามารถคงอยู่ได้

คำหลักชั่วคราวใน java ระวัง - การใช้คำสำคัญนี้จะป้องกันฟิลด์ที่จะใช้กับกลไกการทำให้เป็นอนุกรมจากจาวา ดังนั้นหากจะต้องทำให้เป็นอนุกรมคุณควรใช้คำอธิบายประกอบ @Transient


1
แล้วฉันอยากจะเพิกเฉยต่อวิธีการรับข้อมูลด้วยวิธีไหน ตัวอย่างเช่น: myjparepository.save () จะบันทึกโมเดลตามปกติและ myjparepository.find (id) จะละเว้นฟิลด์ที่ฉันต้องการหรือไม่
xtiger

@xtiger คุณสามารถสร้างแบบสอบถามที่กำหนดเองเพื่อเก็บถาวรการทำงานนั้น นี่คือตัวอย่างลิงค์
Mohale

7

มีวิธีแก้ไขหลายวิธีขึ้นอยู่กับประเภทแอตทริบิวต์

คุณสมบัติพื้นฐาน

พิจารณาว่าคุณมีaccountตารางต่อไปนี้:

ตารางบัญชี

accountตารางแมปไปยังAccountนิติบุคคลเช่นนี้

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

แอตทริบิวต์เอนทิตีพื้นฐานถูกแมปกับคอลัมน์ตารางดังนั้นคุณสมบัติเช่นidนั้นibanจึงcentsเป็นแอตทริบิวต์พื้นฐาน

แต่คุณสมบัติdollars, interestCentsและ, interestDollarsถูกคำนวณดังนั้นคุณจึงใส่คำอธิบายประกอบด้วย@Transientเพื่อแยกพวกเขาออกจากคำสั่ง SELECT, INSERT, UPDATE และ DELETE SQL

ดังนั้นสำหรับคุณสมบัติพื้นฐานคุณต้องใช้@Transientเพื่อแยกคุณสมบัติที่กำหนดออกจากการคงอยู่

สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับแอตทริบิวต์นิติบุคคลคำนวณตรวจสอบบทความนี้

สมาคม

สมมติว่าคุณมีดังต่อไปนี้postและpost_commentตาราง:

ตารางการโพสต์และ post_comment

คุณต้องการแมปการlastestCommentเชื่อมโยงในPostเอนทิตีกับPostCommentเอนทิตีล่าสุดที่เพิ่มเข้ามา

ในการทำเช่นนั้นคุณสามารถใช้@JoinFormulaคำอธิบายประกอบ:

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

เมื่อดึงPostเอนทิตีคุณจะเห็นว่าlatestCommentมีการดึงข้อมูล แต่ถ้าคุณต้องการแก้ไขการเปลี่ยนแปลงจะถูกละเว้น

ดังนั้นสำหรับการเชื่อมโยงคุณสามารถใช้@JoinFormulaเพื่อละเว้นการดำเนินการเขียนในขณะที่ยังอนุญาตให้อ่านการเชื่อมโยงได้

สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับสมาคมคำนวณตรวจสอบบทความนี้

@MapsId

@MapsIdอีกวิธีหนึ่งที่จะไม่สนใจความสัมพันธ์ที่ถูกแมปแล้วโดยระบุกิจการคือการใช้งาน

ตัวอย่างเช่นพิจารณาความสัมพันธ์ของตารางแบบหนึ่งต่อหนึ่ง:

ตารางการโพสต์และ post_details

PostDetailsนิติบุคคลถูกแมปเช่นนี้

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

โปรดสังเกตว่าทั้งidแอตทริบิวต์และการpostเชื่อมโยงจะแมปคอลัมน์ฐานข้อมูลเดียวกันซึ่งเป็นpost_detailsคอลัมน์คีย์หลัก

หากต้องการยกเว้นidแอตทริบิวต์@MapsIdคำอธิบายประกอบจะบอก Hibernate ว่าการpostเชื่อมโยงดูแลค่าคอลัมน์คีย์หลักของตาราง

ดังนั้นเมื่อตัวระบุเอนทิตีและการเชื่อมโยงแชร์คอลัมน์เดียวกันคุณสามารถใช้@MapsIdเพื่อละเว้นแอททริบิวต์ตัวระบุเอนทิตีและใช้การเชื่อมโยงแทน

สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับการ@MapsIdตรวจสอบบทความนี้

การใช้ insertable = false, updatable = false

อีกทางเลือกหนึ่งคือใช้insertable = false, updatable = falseสำหรับการเชื่อมโยงที่คุณต้องการละเว้นโดยไฮเบอร์เนต

ตัวอย่างเช่นเราสามารถแมปการเชื่อมโยงแบบหนึ่งต่อหนึ่งก่อนหน้านี้:

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

insertableและupdatableคุณลักษณะของ@JoinColumnคำอธิบายประกอบจะสั่ง Hibernate จะไม่สนใจpostสมาคมตั้งแต่ระบุกิจการดูแลpost_idคอลัมน์คีย์หลัก


ขอให้สังเกตว่าทั้งแอตทริบิวต์ id และการเชื่อมโยงการโพสต์จะแมปคอลัมน์ฐานข้อมูลเดียวกันซึ่งเป็นคอลัมน์คีย์หลัก post_comment สิ่งนี้ถูกต้องในบริบทของpostและpost_detailsตารางเป็นตัวอย่างหรือไม่?
เดวิด

1
ขอบคุณที่จำได้ ฉันซ่อมมัน.
Vlad Mihalcea

3

เพื่อให้ได้คำตอบข้างต้นฉันมีกรณีที่ใช้ไฟล์การแมป XML ที่ไม่ทำงาน@Transientหรือtransient... ฉันต้องใส่ข้อมูลชั่วคราวในไฟล์ xml:

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

2

คำตอบข้างต้นไม่ได้ผลสำหรับฉันที่ใช้ Hibernate 5.2.10, Jersey 2.25.1 และ Jackson 2.8.9 ในที่สุดผมก็พบคำตอบ (การจัดเรียงของพวกเขาอ้างอิง hibernate4module แต่การทำงานเป็นเวลา 5 เกินไป) ที่นี่ ไม่มีคำอธิบายประกอบ Json ที่ใช้งาน@Transientได้เลย เห็นได้ชัดว่า Jackson2 นั้น 'ฉลาด' พอที่จะเพิกเฉยต่อสิ่งที่ทำเครื่องหมายด้วย@Transientเว้นแต่ว่าคุณจะบอกอย่างชัดเจนว่าไม่ควรทำ กุญแจสำคัญคือการเพิ่มโมดูล hibernate5 (ซึ่งฉันใช้เพื่อจัดการกับหมายเหตุประกอบ Hibernate อื่น ๆ ) และปิดการใช้งานUSE_TRANSIENT_ANNOTATIONคุณสมบัติใน Jersey Application ของฉัน:

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

นี่คือการอ้างอิงสำหรับ Hibernate5Module:

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

หลังจากลองใช้วิธีอื่นทุกวิธีที่กล่าวถึงข้างต้นและวิธีอื่น ๆ วิธี @JsonProperty @JsonInclude @JsonSerialize + @JsonDeserialize mapper.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, false));นี้ก็ใช้ได้ ขอบคุณ!
Aceonline

1

บางครั้งคุณต้องการ:

  1. ทำให้เป็นอันดับคอลัมน์
  2. ไม่ต้องสนใจคอลัมน์จากการคงอยู่:

ใช้ @Column(name = "columnName", insertable = false, updatable = false)

สถานการณ์ที่ดีคือเมื่อคอลัมน์ใดคอลัมน์หนึ่งคำนวณโดยอัตโนมัติโดยใช้ค่าคอลัมน์อื่น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.