永続化中にJPAフィールドを無視する最も簡単な方法は何ですか?


281

私は基本的に、特定のフィールドの永続化を停止できる「@Ignore」タイプのアノテーションを探しています。どうすればこれを達成できますか?

回答:


450

@Transient あなたのニーズを満たします。


20
しかし、JacksonはJSONに変換するときにフィールドをシリアル化しません...どのように解決しますか?
MobileMon、2016年

それはあなたのアプリのデザインに依存します。エンティティクラスに注釈を付けると、どこにでも適用されます。しかし、エンティティを使用するdaoに注釈を付けると、別の話になります。つまり、複数のストレージがある場合はDAOを使用します
Andrii Plotnikov

リストフィールドでは機能しません。コレクション型フィールドの場合、hibernateは実際に新しい中間テーブルを作成します。そのための回避策はありますか?
zulkarnain shah 2017

あなたはそれを解決することができ@MobileMon、私の答えをチェックしstackoverflow.com/a/41850392/3871754
カミルNekanowicz

@MobileMonできれば、データベースとAPIの目的(単一の責任)の両方に同じエンティティを使用しないでください。
JacekŚlimok

126

フィールドを無視するには、フィールドに注釈を付けて、@TransientHibernateによってマップされないようにします。

しかし、Jacksonは JSONへの変換時にフィールドをシリアル化ません

JPAとJSONを混在させる必要がある場合(JPAでは省略、ただしJacksonには含まれます)を使用します@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 2017

2
私はスプリングブートでそれをしました
カミルネカノビッツ2018

こんにちは、私はこれを使用しようとしていますが、@ Transientフィールドでシリアル化されていません
Mohammad

@Mohammadアノテーションを追加@JsonInclude()
Kamil Nekanowicz

これをSpring Boot 2.1.7で試してみましたが、機能しませんでした。しかし、それでも他のシナリオで機能する可能性があるので、あなたは私の投票を得ます。
Aditya Ekbote


19

この回答は少し遅れますが、回答は完了です。

エンティティのフィールドがDBに永続化されないようにするには、次の2つのメカニズムのいずれかを使用できます。

@Transient-フィールドを永続化できないものとしてマークするJPAアノテーション

Javaの一時的なキーワード。注意-このキーワードを使用すると、フィールドがJavaのシリアル化メカニズムで使用されなくなります。したがって、フィールドをシリアル化する必要がある場合は、 @ Transientアノテーションのみを使用することをお勧めします。


1
getメソッドの永続性を無視したいだけですか?例:myjparepository.save()は通常どおりモデルを保存し、myjparepository.find(id)は必要なフィールドを無視しますか?
xtiger 2016

@xtigerカスタムクエリを作成して、その機能をアーカイブできます。ここでは例だリンク
モハレス

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
}

基本的なエンティティの属性は、プロパティが好きなので、テーブルの列にマッピングされidibancents基本的な属性です。

しかしdollarsinterestCentsinterestDollarsあなたがそれらに注釈を付けるので、計算されたプロパティです@TransientSELECT、INSERT、UPDATE、およびDELETE SQLステートメントからそれらを除外します。

したがって、基本的な属性については@Transient、特定のプロパティが永続化されないように除外するために使用する必要があります。

計算されたエンティティ属性の詳細については、こちらの記事をご覧ください。

協会

次のものpostpost_commentテーブルがあると仮定します。

postおよび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です。

たとえば、次の1対1のテーブルの関係を考えます。

postおよび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アノテーションは、post関連付けがテーブルの主キー列の値を処理することをHibernateに通知します。

したがって、エンティティ識別子と関連付けが同じ列を共有している場合は、を使用@MapsIdしてエンティティ識別子属性を無視し、代わりに関連付けを使用できます。

の詳細については@MapsIdこちらの記事をご覧ください。

使用する insertable = false, updatable = false

別のオプションは使用することです insertable = false, updatable = false、Hibernateによって無視されるにする関連付けことです。

たとえば、以前の1対1の関連付けを次のようにマッピングできます。

@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に指示します。postpost_id


id属性とpostアソシエーションの両方が同じデータベース列、つまりpost_comment Primary Key列をマップすることに注意してください。 これは例としてpostpost_details表のコンテキストでは正しいですか?
デビッド

1
見つけてくれてありがとう。それを私が直した。
Vlad Mihalcea

3

上記の回答を完了するために、XMLマッピングファイルを使用するケースがありましたが、どちら@Transienttransient機能しませんでした...一時的な情報を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アプリケーションの機能を無効にすることでした。

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 2017年

1

時々あなたがしたい:

  1. 列をシリアル化する
  2. 列の永続化を無視:

使用する @Column(name = "columnName", insertable = false, updatable = false)

良いシナリオは、特定の列が他の列の値を使用して自動的に計算される場合です

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.