2017-12-23 15 views
0

JPA 다 대다 단방향 관계에 어려움이 있습니다. 문제는 동일한 레이블 엔티티가 DB에 작성된다는 것입니다. 성능상의 이유로 @Id는 Label.label이 아닌 생성 된 번호입니다. 연결된 문제는 findByLabel()이 두 개의 노트를 찾지 못한다는 것입니다.JPA 다 대다 저장 중복 엔티티

많은 기사와 예제를 읽었으며 비슷한 코드가 있다고 생각하지만 작동하지 않습니다. 어떤 도움을 주셔서 감사합니다.

스프링 부트, H2, 스프링 데이터 JPA를 사용하고 있습니다. 나는 비 XML, 순수 자바 주석 솔루션을 찾고있다.

저장소 :

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

두 개의 별도 Label 인스턴스를 만들고 동일한 "labelA"문자열 값을 제공합니다. 그것들이 동일하게되기를 원한다면 동일한 인스턴스를 전달하거나 save 메소드를 변경하여 새로운 노트가 생기면 먼저 레이블 문자열을 사용하여 참조 된 모든 레이블을 찾습니다. 그렇지 않으면 전달 된 인스턴스에 ID가없는 경우 삽입이 발생합니다. 이 경우에 예외가 필요하면 레이블 문자열을 고유 한 것으로 표시하십시오 - @Column (name = "LABEL", unique = true). 중복이 삽입되면 데이터베이스에서 예외가 발생합니다. – Chris

+0

힌트를 보내 주셔서 감사합니다. 그러나 나는 어떻게이 솔루션을 달성 할 수 있는지 이해하지 못한다. 저장 방법을 변경하여 지속 된 라벨을 찾는다. noterepository.merge를 호출하거나 다른 계단식 설정을 추가하여 자동으로 수행 할 수 있습니까? 나는 스프링 데이터 jpa에 익숙하지 않다. 그러나 B를 가진 데이터 모델은 매우 공통적 일 것이기 때문에, Hibernate는 기성 템플릿을 가지고 있어야한다고 생각한다. ...? – KrystofP

+0

@ 크리스 당신이 도와 드릴까요? – 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()); 
    }); 

}