Cách dễ nhất để bỏ qua một lĩnh vực JPA trong sự kiên trì là gì?


281

Về cơ bản, tôi đang tìm kiếm một chú thích loại "@Ignore" mà tôi có thể ngăn một trường cụ thể không bị tồn tại. Làm thế nào điều này có thể đạt được?

Câu trả lời:


450

@Transient phù hợp với nhu cầu của bạn.


20
Nhưng sau đó jackson sẽ không tuần tự hóa trường khi chuyển đổi sang JSON ... làm thế nào để giải quyết?
MobileMon

điều đó phụ thuộc vào ứng dụng của bạn nếu bạn chú thích lớp thực thể của bạn - nó áp dụng ở mọi nơi; nhưng nếu bạn anotate dao sử dụng thực thể - đó là một câu chuyện khác. trong ngắn hạn: sử dụng DAO khi bạn có nhiều kho lưu trữ
Andrii Plotnikov

Không hoạt động trên các trường Danh sách. Đối với các trường loại Bộ sưu tập, ngủ đông thực sự tạo ra một bảng trung gian mới. Bất kỳ cách giải quyết cho điều đó?
zulkarnain shah

@MobileMon Bạn có thể giải quyết nó, kiểm tra câu trả lời của tôi stackoverflow.com/a/41850392/3871754
Kamil Nekanowicz

@MobileMon Tốt nhất là bạn không nên sử dụng cùng một thực thể cho cả mục đích cơ sở dữ liệu và API (trách nhiệm đơn lẻ).
Jacek Ślimok

126

Để bỏ qua một trường, chú thích nó với @Transient nó để nó không được ánh xạ bởi chế độ ngủ đông.

nhưng sau đó jackson sẽ không tuần tự hóa trường khi chuyển đổi sang JSON.

Nếu bạn cần trộn JPA với JSON (bỏ qua JPA nhưng vẫn bao gồm trong Jackson), hãy sử dụng @JsonInclude:

@JsonInclude()
@Transient
private String token;

TIỀN BOA:

Bạn cũng có thể sử dụng JsonInclude.Include.NON_NULL và ẩn các trường trong JSON trong quá trình giải tuần tự hóa khi token == null:

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

3
Tôi đang chạy JAX-RS 2.0.1 / Jersey 2.25.1 / Jackson 2.8.7 và với ngăn xếp đó @JsonIncludelà không cần thiết: @Transientcác trường vẫn được bao gồm trong JSON. (Bạn vẫn nhận được phiếu bầu của tôi: kỹ thuật có thể rất hữu ích trong các trường hợp khác).
DKroot

2
Tôi đã làm điều đó trên Spring Boot
Kamil Nekanowicz

xin chào, tôi đang cố gắng sử dụng cái này, nhưng nó không được đăng tuần tự với trường @Transient
Mohammad

@Mohammad thêm chú thích @JsonInclude ()
Kamil Nekanowicz

Đã thử điều này trong Spring Boot 2.1.7 nhưng không hoạt động. Nhưng bạn vẫn nhận được phiếu bầu của tôi vì nó có thể hoạt động trong các tình huống khác.
Aditya Ekbote


19

Câu trả lời này đến hơi muộn, nhưng nó hoàn thành câu trả lời.

Để tránh trường từ một thực thể được duy trì trong DB, người ta có thể sử dụng một trong hai cơ chế:

@Transient - Chú thích JPA đánh dấu một trường là không bền vững

từ khóa thoáng qua trong java. Cẩn thận - sử dụng từ khóa này, sẽ ngăn trường được sử dụng với bất kỳ cơ chế tuần tự hóa nào từ java. Vì vậy, nếu trường phải được tuần tự hóa, bạn chỉ nên sử dụngchú thích @Transient .


1
Làm thế nào về tôi chỉ muốn bỏ qua sự kiên trì trên phương thức get?. Ví dụ: myjparep repository.save () sẽ lưu mô hình như bình thường và myjparep repository.find (id) sẽ bỏ qua trường mà tôi muốn?
xtiger

@xtiger bạn có thể tạo một truy vấn tùy chỉnh để lưu trữ chức năng đó. Đây là một liên kết
Mohale

7

Có nhiều giải pháp tùy thuộc vào loại thuộc tính thực thể.

Thuộc tính cơ bản

Xem xét bạn có accountbảng sau :

Bảng tài khoản

Các accountbảng được ánh xạ tới các Accountthực thể như thế này:

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

Các thuộc tính cơ bản của thực được ánh xạ vào các cột bảng, vì vậy tính chất thích id, iban, centslà các thuộc tính cơ bản.

Nhưng dollars, interestCentsinterestDollarsđược tính toán tài sản, vì vậy bạn có chú thích chúng bằng @Transientloại trừ chúng khỏi SELECT, INSERT, UPDATE, và câu lệnh SQL DELETE.

Vì vậy, đối với các thuộc tính cơ bản, bạn cần sử dụng @Transientđể loại trừ một thuộc tính nhất định khỏi bị tồn tại.

Để biết thêm chi tiết về các thuộc tính thực thể được tính toán, hãy xem bài viết này .

Hiệp hội

Giả sử bạn có những điều sau đây postpost_commentbảng:

Các bảng bài và post_comment

Bạn muốn ánh xạ lastestCommentliên kết trong Postthực thể đến PostCommentthực thể mới nhất đã được thêm vào.

Để làm điều đó, bạn có thể sử dụng @JoinFormulachú thích:

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

Khi tìm nạp Postthực thể, bạn có thể thấy rằng nó latestCommentđược tìm nạp, nhưng nếu bạn muốn sửa đổi nó, thay đổi sẽ bị bỏ qua.

Vì vậy, đối với các hiệp hội, bạn có thể sử dụng @JoinFormulađể bỏ qua các hoạt động ghi trong khi vẫn cho phép đọc liên kết.

Để biết thêm chi tiết về các hiệp hội tính toán, hãy xem bài viết này .

@MapsId

Một cách khác để bỏ qua các liên kết đã được ánh xạ bởi định danh thực thể là sử dụng @MapsId.

Ví dụ, hãy xem xét mối quan hệ bảng một-một sau đây:

Các bảng post và post_details

Thực PostDetailsthể được ánh xạ như thế này:

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

Lưu ý rằng cả idthuộc tính và postliên kết ánh xạ cùng một cột cơ sở dữ liệu, đó là post_detailscột Khóa chính.

Để loại trừ idthuộc tính, @MapsIdchú thích sẽ cho Hibernate biết rằng posthiệp hội sẽ chăm sóc giá trị cột Chính của bảng.

Vì vậy, khi định danh thực thể và liên kết chia sẻ cùng một cột, bạn có thể sử dụng @MapsIdđể bỏ qua thuộc tính định danh thực thể và sử dụng liên kết thay thế.

Để biết thêm chi tiết về @MapsId, hãy xem bài viết này .

Sử dụng insertable = false, updatable = false

Một tùy chọn khác là sử dụng insertable = false, updatable = falsecho liên kết mà bạn muốn bỏ qua bởi Hibernate.

Chẳng hạn, chúng ta có thể ánh xạ liên kết một-một trước đây như sau:

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

Các thuộc tính insertableupdatablethuộc tính của @JoinColumnchú thích sẽ hướng dẫn Hibernate bỏ qua postliên kết do định danh thực thể chăm sóc post_idcột Khóa chính.


Lưu ý rằng cả thuộc tính id và liên kết bài ánh xạ cùng một cột cơ sở dữ liệu, đó là cột Khóa chính post_comment. Điều này có đúng trong ngữ cảnh postpost_detailsbảng làm ví dụ không?
David

1
Cảm ơn vì đã phát hiện ra nó. Tôi sửa nó rồi.
Vlad Mihalcea

3

Để hoàn thành các câu trả lời ở trên, tôi đã gặp trường hợp sử dụng tệp ánh xạ XML trong đó không phải @Transientcũng không transienthoạt động ... Tôi phải đưa thông tin thoáng qua vào tệp xml:

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

2

Không có câu trả lời nào ở trên làm việc cho tôi khi sử dụng Hibernate 5.2.10, Jersey 2.25.1 và Jackson 2.8.9. Cuối cùng tôi đã tìm thấy câu trả lời (loại, họ tham khảo hibernate4module nhưng nó cũng hoạt động cho 5) ở đây . Không có chú thích nào của Json hoạt động cả @Transient. Rõ ràng Jackson2 đủ 'thông minh' để vui lòng bỏ qua những thứ được đánh dấu @Transienttrừ khi bạn nói rõ ràng là không. Chìa khóa là thêm mô-đun hibernate5 (mà tôi đang sử dụng để xử lý các chú thích Hibernate khác) và tắt USE_TRANSIENT_ANNOTATIONtính năng này trong Ứng dụng Jersey của tôi:

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

Đây là phần phụ thuộc cho Hibernate5Module:

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

Sau khi thử mọi phương pháp khác được đề cập ở trên và các phương pháp khác @JsonProperty @JsonInclude @JsonSerialize + @JsonDeserialize mapper.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, false));, giải pháp này cuối cùng đã có hiệu quả. Cảm ơn!
Aceonline

1

Đôi khi bạn muốn:

  1. Nối tiếp một cột
  2. Bỏ qua cột khỏi bị tồn tại:

Sử dụng @Column(name = "columnName", insertable = false, updatable = false)

Một kịch bản tốt là khi một cột nhất định được tự động tính bằng cách sử dụng các giá trị cột khác

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.