2017-02-02 26 views
0

SpringデータRESTとSpringデータJPAを使用して、集合ルート上の子エンティティコレクションを更新したいとします。デモの例として、Commentエンティティと1対多の関係を持つPostエンティティがあるとします。 Postには独自のSpringデータリポジトリがあります。 Commentは、Postによってのみアクセスされるため、表示されません。SpringデータREST/JPA - 複合キーを使用したOneToManyコレクションの更新

Commentには、既存のデータベース設計によるPostへの外部キーを含む複合キーがあります。その結果、双方向関係の必要がなくても、双方向関係なしで外部キーをCommentの複合キーの一部にする方法を見つけることができませんでした。

クラスはロンボク島の注釈で、次のようになります。

@Entity 
@Data 
public class Post { 

    @Id 
    @GeneratedValue 
    private long id; 

    @OneToMany(mappedBy = "post", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true) 
    private Set<Comment> comments = new HashSet<>(); 

    private String title; 
} 

コメント:

@Entity 
@IdClass(Comment.CommentPk.class) 
@Data 
@EqualsAndHashCode(exclude = "post") 
@ToString(exclude = "post") 
public class Comment { 

    @Id 
    private long id; 

    @Id 
    @ManyToOne(fetch = FetchType.LAZY) 
    @RestResource(exported = false) 
    @JsonIgnore 
    private Post post; 

    private String content; 

    @Data 
    static class CommentPk implements Serializable { 
     private long id; 

     private Post post; 
    } 
} 

とリポジトリ:

public interface PostRepository extends JpaRepository<Post, Long> { 
} 

私はPostを作成しようCommentの場合、例外は発生します。POST_IDはNULLにすることはできません。つまり、保持しようとしているCommentの親Postへのバックリファレンスが欠落しています。

これは後方参照を保持Postから@PrePersist方法を追加することによって解決することができます。新しいPostを作成するときに

@PrePersist 
private void maintainParentBackreference() { 
    for (Comment comment : this.comments) { 
     comment.setPost(this); 
    } 
} 

以上が正常に動作しますが、Commentを追加しようとすると、それは助けにはなりません既存のPost(例えばPUT要求に)にコメントを挿入しようとすると、次のエラーが発生しますので:要約すると

NULL not allowed for column "POST_ID"; SQL statement: 
insert into comment (content, id, post_id) values (?, ?, ?) [23502-193] 

を、ステップREPROへデュースは以下のとおりです。

で作成した Post 私はCommentを追加/更新することができることを達成することができ、最も簡単な方法は何ですかComment

に無Comment

  • PUTと
    1. POST Post既存のPostにSpring Data RESTを使用していますか?

      これを示すサンプルプロジェクトをここで見つけることができます:https://github.com/shakuzen/aggregate-child-update-sample/tree/composite-key

      この特定の設定は、リポジトリのcomposite-keyブランチです。このコードで、上記の不具合を再現するには、統合テストAggregateCompositeKeyUpdateTests.canAddCommentWithPut

  • 答えて

    0

    あなたが本当に自分の呼び出しなぜならこれらの後方参照を管理するために@PrePersistと​​コールバックを使用してはならないのREADMEで手動再生の手順に従うか、実行することができます多くの場合、Postの状態が実際に操作されているかどうかによって異なります。

    代わりに、これらの関係は、コントローラまたは一部のビジネスサービスが呼び出すドメイン固有のコードの一部として操作するものでなければなりません。あなたがコードからわかるように、彼らが操作したい場合

    public class Post { 
        @OneToMany(mappedBy = "Post", cascade = CascadeType.ALL, ...) 
        private Set<Comment> comments; 
    
        // Allows access to the getter, but it protects the internal collection 
        // from manipulation from the outside, forcing users to use the DDD methods. 
        public Set<Comment> getComments() { 
        return Collections.unmodifiableSet(comments); 
        } 
    
        // Do not expose the setter because we want to control adding/removing 
        // of comments through a more DDD style. 
        private void setComments(Set<Comment> comments) { 
        this.comments = comments; 
        } 
    
        public void addComment(Comment comment) { 
        if (this.comments == null) { 
         this.comments = new HashSet<Comment>(); 
        } 
        this.comments.add(comment); 
        comment.setPost(this); 
        } 
    
        public void removeComment(Comment comment) { 
        if (this.comments != null) { 
         for (Iterator<Comment> i = comments.iterator(); i.hasNext();) { 
         final Comment postComment = i.next(); 
         if (postComment.equals(comment)) { 
          // uses #getCompositeId() equality 
          iterator.remove(); 
          comment.setPost(null); 
          return; 
         } 
         } 
        } 
        throw new InvalidArgumentException("Comment not associated with post"); 
        } 
    

    Postエンティティオブジェクトのユーザーが#addComment#removeCommentを使用するように強制されています。私は、多くの場合、ドメイン駆動設計アプローチ背後関係のこれらのタイプは、抽象的に好みます関連するコメント。これらのメソッドは、バックリファレンスが正しく設定されていることを確認します。

    final Comment comment = new Comment(); 
    // set values on comment 
    final Post post = entityManager.find(Post.class, postId); 
    post.addComment(comment); 
    entityManager.merge(post); 
    

    UPDATE - 春データRESTソリューション

    春データRESTは、直接このロジックを適用させるためには、あなたはどちらか、リスナーまたはコールバッククラスを作成することができます。

    リスナーの例は次のようになります。

    public class BeforeSavePostEventListener extends AbstractRepositoryEventListener { 
        @Override 
        public void onBeforeSave(Object entity) { 
        // logic to do by inspecting entity before repository saves it. 
        } 
    } 
    

    注釈付きのハンドラの例のようになります。

    @RepositoryEventHandler 
    public class PostEventHandler { 
        @HandleBeforeSave 
        public void handlePostSave(Post p) { 
        } 
        @HandleBeforeSave 
        public void handleCommentSave(Comment c) { 
        } 
    } 
    

    次はあなただけの、このBeanがどちらかによってピックアップされていることを確認する必要がありますさまざまな@Componentステレオタイプの1つをスキャンで指定するか、コンフィグレーションクラスに@Beanとして指定する必要があります。

    2つのアプローチの最大の違いは、2つ目のアプローチがタイプセーフであり、エンティティタイプがさまざまな注釈付きメソッドの最初の引数によって決定されることです。

    この詳細については、hereをご覧ください。

    +0

    ありがとうございました。 Spring Data RESTに、対応するHTTPリクエストを処理する際にこれらのメソッドを簡単に使用できるかどうかを示唆してもらえれば幸いです。 –

    +0

    アイデアをSpring Data RESTとリスナまたは注釈付きハンドラコールバックを使用して統合する方法についての情報を追加しました。 – Naros

    関連する問題