2017-12-23 13 views
0

JPAの多対多の一方向関係には問題があります。問題は、たとえ同じであっても、重複したラベルエンティティがDBに作成されることです。パフォーマンスの理由から、@Idは生成された番号であり、Label.labelではありません。接続された問題は、findByLabel()が2つのノートを見つけられないことです。JPA多対多が重複するエンティティを節約します

私は多くの記事と例を読みましたが、私は同様のコードを持っていると思いますが、うまくいきません。どんな助けもありがとうございます。

私はSpring Boot、H2、Spring Data JPAを使用しています。私は非XML、純粋なJavaアノテーションソリューションを探しています。

リポジトリ:

public interface NoteRepository extends CrudRepository<Note, Long> { 
    @Query("SELECT a from Note a where ?1 member of a.labels") 
    List<Note> findByLabel(Label label); } 

レーベル:

@Entity 
public class Label { 

    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    @Column(name = "label_id") 
    private Long labelId; 

    private String label; 

    protected Label() { 
    } 

    public Label(String label) { 
     this.label = label; 
    } 

    public Long getLabelId() { 
     return labelId; 
    } 

    public void setLabelId(Long labelId) { 
     this.labelId = labelId; 
    } 

    public String getLabel() { 
     return label; 
    } 

    public void setLabel(String label) { 
     this.label = label; 
    } 

    @Override 
    public boolean equals(Object o) { 
     if (this == o) return true; 
     if (!(o instanceof Label)) return false; 

     Label label1 = (Label) o; 

     return getLabel().equals(label1.getLabel()); 
    } 

    @Override 
    public int hashCode() { 

     return Objects.hash(getLabel()); 
    } 

    @Override 
    public String toString() { 
     return "Label{" + 
       "labelId=" + labelId + 
       ", label='" + label + '\'' + 
       '}'; 
    } 
} 

注:

@Entity 
public class Note { 

    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    @Column(name = "note_id") 
    private Long noteId; 

    private String note; 

    @ManyToMany(
      fetch = FetchType.EAGER, 
      cascade = CascadeType.ALL) 
    @JoinTable(
      name = "Note_Label", 
      joinColumns = @JoinColumn(
        name = "NoteId", 
        referencedColumnName = "note_id"), 
      inverseJoinColumns = @JoinColumn(
        name = "LabelId", 
        referencedColumnName = "label_id")) 
    private Set<Label> labels; 


    protected Note() { 
    } 

    public Note(String note, Set<Label> labels) { 
     this.note = note; 
     this.labels = labels; 
    } 

    public Note(String note) { 
     this(note, null); 
    } 

    public String getNote() { 
     return note; 
    } 

    public void setNote(String note) { 
     this.note = note; 
    } 


    public Long getNoteId() { 
     return noteId; 
    } 

    public void setNoteId(Long noteId) { 
     this.noteId = noteId; 
    } 

    public Set<Label> getLabels() { 
     return labels; 
    } 

    public void setLabels(Set<Label> labels) { 
     this.labels = labels; 
    } 

    @Override 
    public String toString() { 
     return "Note{" + 
       "note='" + note + '\'' + 
       ", labels='" + labels + '\'' + 
       '}'; 
    } 

    @Override 
    public boolean equals(Object o) { 
     if (this == o) return true; 
     if (!(o instanceof Note)) return false; 

     Note note1 = (Note) o; 

     if (!getNote().equals(note1.getNote())) return false; 
     return getLabels() != null ? getLabels().equals(note1.getLabels()) : note1.getLabels() == null; 
    } 

    @Override 
    public int hashCode() { 
     int result = getNote() != null ? getNote().hashCode() : 0; 
     result = 31 * result + (getLabels() != null ? getLabels().hashCode() : 0); 
     return result; 
    } 
} 

テストが失敗した:

@SpringBootTest 
@RunWith(SpringRunner.class) 
public class NoteRepositoryTest { 

    @Autowired 
    private NoteRepository repository; 


    @Test 
    public void findByLabel() throws Exception { 

     String labelAString = "labelA"; 

     Label labelA = new Label(labelAString); 
     Label labelA1 = new Label(labelAString); 
     Label labelB = new Label("labelB"); 
     Label labelC = new Label("labelC"); 

     Note note1 = new Note(
       "note1", new HashSet<Label>(Arrays.asList(
       labelA, labelB))); 

     Note note2 = new Note(
       "note2", new HashSet<Label>(Arrays.asList(
       labelA1, labelC))); 

     repository.deleteAll(); 


     repository.save(note1); 
     repository.save(note2); 


     List<Note> actualNotes = repository.findByLabel(labelA); 

     System.out.println(actualNotes); 


     assertTrue(actualNotes.size() == 2); 
     assertTrue(actualNotes.containsAll(Arrays.asList(note1, note2))); 

    } 

} 
+1

2つの別々のラベルインスタンスを作成し、それらに同じ "labelA"文字列値を与えます。それらを同じにしたい場合は、同じインスタンスを渡すか、保存メソッドを変更する必要があります。新しい注釈を取得すると、ラベル文字列を使用して、参照されているラベルをまず探します。それ以外の場合、渡されたインスタンスにIDがない場合は、挿入されます。この場合に例外が必要な場合は、@Column(name = "LABEL"、unique = true)というラベル文字列を一意であるものとしてマークします。重複が挿入された場合、データベースは例外をスローします。 – Chris

+0

ヒントをお寄せいただきありがとうございます。しかし、どのように私はこのソリューションを達成することができます保存方法を変更することができないように持続的なラベルをルックアップするように理解していない。 noterepository.mergeを呼び出すか、別のカスケード設定を追加することで、これを自動的に行うことができますか?私は春データjpaに新しいですが、私は、Bのセットを持つAを持つ私のデータモデルは非常に一般的でなければならないと思うので、Hibernateはいくつかの既製のテンプレートを持っているはずです...? – KrystofP

+0

@Chrisお願いしますか? – KrystofP

答えて

0

右のコード:

@Test 
public void saveAndGet() throws Exception { 

    String labelAString = "labelA"; 

    Label labelA = new Label(labelAString); 
    Label labelA1 = new Label(labelAString); 
    Label labelB = new Label("labelB"); 
    Label labelC = new Label("labelC"); 

    repository.save(labelA); 
    System.out.println(labelA.getLabelId()); 
    repository.save(labelB); 

    Iterable<Label> all = repository.findAll(); 

    all.forEach(p ->{ 
     System.out.println(p.getLabel()); 
     System.out.println(p.getLabelId()); 
    }); 

} 
関連する問題