title: JPA many-to-many saves duplicate entities
body: <p>I have difficulties with JPA many-to-many unidirectional relationship. The problem is that duplicate Label entities are created in DB even if they are the same. For performance reason the @Id is a generated number and not Label.label. The connected problem is that findByLabel() does not find the two notes. </p>
  
  <p>I read many articles and examples and I think I have similar code, but it doesn't work. Any help is appreciated. </p>
  
  <p>I'm using Spring Boot, H2, Spring Data JPA. I am looking for non-XML, pure Java annotation solution. </p>
  
  <p>Repository:</p>
  
  <pre class="lang-java prettyprint-override"><code>public interface NoteRepository extends CrudRepository&lt;Note, Long&gt; {
      @Query("SELECT a from Note a where ?1 member of a.labels")
      List&lt;Note&gt; findByLabel(Label label); }
  </code></pre>
  
  <p>Label:</p>
  
  <pre class="lang-java prettyprint-override"><code>@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 + '\'' +
                  '}';
      }
  }
  </code></pre>
  
  <p>Note:</p>
  
  <pre class="lang-java prettyprint-override"><code>@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&lt;Label&gt; labels;
  
  
      protected Note() {
      }
  
      public Note(String note, Set&lt;Label&gt; 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&lt;Label&gt; getLabels() {
          return labels;
      }
  
      public void setLabels(Set&lt;Label&gt; 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;
      }
  }
  </code></pre>
  
  <p>Test fails:</p>
  
  <pre class="lang-java prettyprint-override"><code>@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&lt;Label&gt;(Arrays.asList(
                  labelA, labelB)));
  
          Note note2 = new Note(
                  "note2", new HashSet&lt;Label&gt;(Arrays.asList(
                  labelA1, labelC)));
  
          repository.deleteAll();
  
  
          repository.save(note1);
          repository.save(note2);
  
  
          List&lt;Note&gt; actualNotes = repository.findByLabel(labelA);
  
          System.out.println(actualNotes);
  
  
          assertTrue(actualNotes.size() == 2);
          assertTrue(actualNotes.containsAll(Arrays.asList(note1, note2)));
  
      }
  
  }
  </code></pre>