title: Getting duplicate items when querying a collection with Spring Data Rest
body: <p>I'm having duplicate results on a collection with this simple model: an entity <code>Module</code> and an entity <code>Page</code>. A <code>Module</code> has a set of pages, and a <code>Page</code> belongs to the module.</p>
  
  <p>This is set up with <a href="http://projects.spring.io/spring-boot/">Spring Boot</a> with <a href="http://projects.spring.io/spring-data-jpa/">Spring Data JPA</a> and <a href="http://projects.spring.io/spring-data-rest/">Spring Data Rest</a>.</p>
  
  <p>The full code is accessible on <a href="https://github.com/al3jo/duplicate-results">GitHub</a></p>
  
  <h1>Entities</h1>
  
  <p>Here's the code for the entities. Most setters removed for brevity:</p>
  
  <h2>Module.java</h2>
  
  <pre class="lang-java prettyprint-override"><code>@Entity
  @Table(name = "dt_module")
  public class Module {
    private Long id;
    private String label;
    private String displayName;
    private Set&lt;Page&gt; pages;
  
    @Id
    public Long getId() {
      return id;
    }
  
    public String getLabel() {
      return label;
    }
  
    public String getDisplayName() {
      return displayName;
    }
  
    @OneToMany(mappedBy = "module")
    public Set&lt;Page&gt; getPages() {
      return pages;
    }
  
    public void addPage(Page page) {
      if (pages == null) {
        pages = new HashSet&lt;&gt;();
      }
      pages.add(page);
      if (page.getModule() != this) {
        page.setModule(this);
      }
    }
  
    @Override
    public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;
      Module module = (Module) o;
      return Objects.equals(label, module.label) &amp;&amp; Objects.equals(displayName, module.displayName);
    }
  
    @Override
    public int hashCode() {
      return Objects.hash(label, displayName);
    }
  }
  </code></pre>
  
  <h2>Page.java</h2>
  
  <pre class="lang-java prettyprint-override"><code>@Entity
  @Table(name = "dt_page")
  public class Page {
    private Long id;
    private String name;
    private String action;
    private String description;
    private Module module;
  
    @Id
    public Long getId() {
      return id;
    }
  
    public String getName() {
      return name;
    }
  
    public String getAction() {
      return action;
    }
  
    public String getDescription() {
      return description;
    }
  
    @ManyToOne
    public Module getModule() {
      return module;
    }
  
    public void setModule(Module module) {
      this.module = module;
      this.module.addPage(this);
    }
  
    @Override
    public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;
      Page page = (Page) o;
      return Objects.equals(name, page.name) &amp;&amp;
          Objects.equals(action, page.action) &amp;&amp;
          Objects.equals(description, page.description) &amp;&amp;
          Objects.equals(module, page.module);
    }
  
    @Override
    public int hashCode() {
      return Objects.hash(name, action, description, module);
    }
  }
  </code></pre>
  
  <h1>Repositories</h1>
  
  <p>Now the code for the Spring repositories, which is fairly simple:</p>
  
  <h2>ModuleRepository.java</h2>
  
  <pre class="lang-java prettyprint-override"><code>@RepositoryRestResource(collectionResourceRel = "module", path = "module")
  public interface ModuleRepository extends PagingAndSortingRepository&lt;Module, Long&gt; {
  }
  </code></pre>
  
  <h2>PageRepository.java</h2>
  
  <pre class="lang-java prettyprint-override"><code>@RepositoryRestResource(collectionResourceRel = "page", path = "page")
  public interface PageRepository extends PagingAndSortingRepository&lt;Page, Long&gt; {
  }
  </code></pre>
  
  <h1>Config</h1>
  
  <p>The configuration comes from 2 files:</p>
  
  <h2>Application.java</h2>
  
  <pre class="lang-java prettyprint-override"><code>@EnableJpaRepositories
  @SpringBootApplication
  public class Application {
    public static void main(String[] args) {
      SpringApplication.run(Application.class, args);
    }
  }
  </code></pre>
  
  <h2>application.properties</h2>
  
  <pre><code>spring.jpa.database = H2
  
  spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
  spring.jpa.generate-ddl=false
  spring.jpa.hibernate.ddl-auto=validate
  
  spring.datasource.initialize=true
  spring.datasource.url=jdbc:h2:mem:demo;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
  spring.datasource.driverClassName=org.h2.Driver
  spring.datasource.username=sa
  spring.datasource.password=
  
  spring.data.rest.basePath=/api
  </code></pre>
  
  <h1>Database</h1>
  
  <p>Finally the db schema and some test data:</p>
  
  <h2>schema.sql</h2>
  
  <pre class="lang-sql prettyprint-override"><code>drop table if exists dt_page;
  drop table if exists dt_module;
  
  create table DT_MODULE (
    id IDENTITY  primary key,
    label varchar(30) not NULL,
    display_name varchar(40) not NULL
  );
  
  create table DT_PAGE (
    id IDENTITY primary key,
    name varchar(50) not null,
    action varchar(50) not null,
    description varchar(255),
    module_id bigint not null REFERENCES dt_module(id)
  );
  </code></pre>
  
  <h2>data.sql</h2>
  
  <pre class="lang-sql prettyprint-override"><code>INSERT INTO DT_MODULE (label, display_name) VALUES ('mod1', 'Module 1'), ('mod2', 'Module 2'), ('mod3', 'Module 3');
  INSERT INTO DT_PAGE (name, action, description, module_id) VALUES ('page1', 'action1', 'desc1', 1);
  </code></pre>
  
  <p>That's about it. Now, I run thus from the command line to start the application: <code>mvn spring-boot:run</code>.  After the application starts, I can query it's main endpoint like this:</p>
  
  Get API
  
  <pre class="lang-sh prettyprint-override"><code>$ curl http://localhost:8080/api
  </code></pre>
  
  Response
  
  <pre><code>{
    "_links" : {
      "page" : {
        "href" : "http://localhost:8080/api/page{?page,size,sort}",
        "templated" : true
      },
      "module" : {
        "href" : "http://localhost:8080/api/module{?page,size,sort}",
        "templated" : true
      },
      "profile" : {
        "href" : "http://localhost:8080/api/alps"
      }
    }
  }
  </code></pre>
  
  Get all modules
  
  <pre class="lang-sh prettyprint-override"><code>curl http://localhost:8080/api/module
  </code></pre>
  
  Response
  
  <pre><code>{
    "_links" : {
      "self" : {
        "href" : "http://localhost:8080/api/module"
      }
    },
    "_embedded" : {
      "module" : [ {
        "label" : "mod1",
        "displayName" : "Module 1",
        "_links" : {
          "self" : {
            "href" : "http://localhost:8080/api/module/1"
          },
          "pages" : {
            "href" : "http://localhost:8080/api/module/1/pages"
          }
        }
      }, {
        "label" : "mod2",
        "displayName" : "Module 2",
        "_links" : {
          "self" : {
            "href" : "http://localhost:8080/api/module/2"
          },
          "pages" : {
            "href" : "http://localhost:8080/api/module/2/pages"
          }
        }
      }, {
        "label" : "mod3",
        "displayName" : "Module 3",
        "_links" : {
          "self" : {
            "href" : "http://localhost:8080/api/module/3"
          },
          "pages" : {
            "href" : "http://localhost:8080/api/module/3/pages"
          }
        }
      } ]
    },
    "page" : {
      "size" : 20,
      "totalElements" : 3,
      "totalPages" : 1,
      "number" : 0
    }
  }
  </code></pre>
  
  Get all pages for one module
  
  <pre class="lang-sh prettyprint-override"><code>curl http://localhost:8080/api/module/1/pages
  </code></pre>
  
  Response
  
  <pre><code>{
    "_links" : {
      "self" : {
        "href" : "http://localhost:8080/api/module/1/pages"
      }
    },
    "_embedded" : {
      "page" : [ {
        "name" : "page1",
        "action" : "action1",
        "description" : "desc1",
        "_links" : {
          "self" : {
            "href" : "http://localhost:8080/api/page/1"
          },
          "module" : {
            "href" : "http://localhost:8080/api/page/1/module"
          }
        }
      }, {
        "name" : "page1",
        "action" : "action1",
        "description" : "desc1",
        "_links" : {
          "self" : {
            "href" : "http://localhost:8080/api/page/1"
          },
          "module" : {
            "href" : "http://localhost:8080/api/page/1/module"
          }
        }
      } ]
    }
  }
  </code></pre>
  
  <p>So as you can see, I'm getting the same page twice here. What's going on?</p>
  
  <h1>Bonus question: Why this works?</h1>
  
  <p>I was cleaning the code to submit this question, and in order to make it more compact, I moved the JPA Annotations on the <code>Page</code> entity to field level, like this:</p>
  
  <h2>Page.java</h2>
  
  <pre class="lang-java prettyprint-override"><code>@Entity
  @Table(name = "dt_page")
  
  public class Page {
    @Id
    private Long id;
    private String name;
    private String action;
    private String description;
  
    @ManyToOne
    private Module module;
    ...
  </code></pre>
  
  <p>All the rest of the class remains the same. This can be seen on the same github repo on branch <a href="https://github.com/al3jo/duplicate-results/tree/field-level">field-level</a>.</p>
  
  <p>As it turns out, executing the same request with that change to the API will render the expected result (after starting the server the same way I did before):</p>
  
  Get all pages for one module
  
  <pre class="lang-sh prettyprint-override"><code>curl http://localhost:8080/api/module/1/pages
  </code></pre>
  
  Response
  
  <pre><code>{
    "_links" : {
      "self" : {
        "href" : "http://localhost:8080/api/module/1/pages"
      }
    },
    "_embedded" : {
      "page" : [ {
        "name" : "page1",
        "action" : "action1",
        "description" : "desc1",
        "_links" : {
          "self" : {
            "href" : "http://localhost:8080/api/page/1"
          },
          "module" : {
            "href" : "http://localhost:8080/api/page/1/module"
          }
        }
      } ]
    }
  }
  </code></pre>