Getting duplicate items when querying a collection with Spring Data Rest -


i'm having duplicate results on collection simple model: entity module , entity page. module has set of pages, , page belongs module.

this set spring boot spring data jpa , spring data rest.

the full code accessible on github

entities

here's code entities. setters removed brevity:

module.java

@entity @table(name = "dt_module") public class module {   private long id;   private string label;   private string displayname;   private set<page> pages;    @id   public long getid() {     return id;   }    public string getlabel() {     return label;   }    public string getdisplayname() {     return displayname;   }    @onetomany(mappedby = "module")   public set<page> getpages() {     return pages;   }    public void addpage(page page) {     if (pages == null) {       pages = new hashset<>();     }     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) && objects.equals(displayname, module.displayname);   }    @override   public int hashcode() {     return objects.hash(label, displayname);   } } 

page.java

@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) &&         objects.equals(action, page.action) &&         objects.equals(description, page.description) &&         objects.equals(module, page.module);   }    @override   public int hashcode() {     return objects.hash(name, action, description, module);   } } 

repositories

now code spring repositories, simple:

modulerepository.java

@repositoryrestresource(collectionresourcerel = "module", path = "module") public interface modulerepository extends pagingandsortingrepository<module, long> { } 

pagerepository.java

@repositoryrestresource(collectionresourcerel = "page", path = "page") public interface pagerepository extends pagingandsortingrepository<page, long> { } 

config

the configuration comes 2 files:

application.java

@enablejparepositories @springbootapplication public class application {   public static void main(string[] args) {     springapplication.run(application.class, args);   } } 

application.properties

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 

database

finally db schema , test data:

schema.sql

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) ); 

data.sql

insert dt_module (label, display_name) values ('mod1', 'module 1'), ('mod2', 'module 2'), ('mod3', 'module 3'); insert dt_page (name, action, description, module_id) values ('page1', 'action1', 'desc1', 1); 

that's it. now, run command line start application: mvn spring-boot:run. after application starts, can query it's main endpoint this:

api
$ curl http://localhost:8080/api 
response
{   "_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"     }   } } 
modules
curl http://localhost:8080/api/module 
response
{   "_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   } } 
pages 1 module
curl http://localhost:8080/api/module/1/pages 
response
{   "_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"         }       }     } ]   } } 

so can see, i'm getting same page twice here. what's going on?

bonus question: why works?

i cleaning code submit question, , in order make more compact, moved jpa annotations on page entity field level, this:

page.java

@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;   ... 

all rest of class remains same. can seen on same github repo on branch field-level.

as turns out, executing same request change api render expected result (after starting server same way did before):

pages 1 module
curl http://localhost:8080/api/module/1/pages 
response
{   "_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"         }       }     } ]   } } 

this causing issue (page entity):

  public void setmodule(module module) {     this.module = module;     this.module.addpage(this); //this line right here   } 

hibernate uses setters initialize entity because put jpa annotations on getters.

initialization sequence causes issue:

  1. module object created
  2. set module properties (pages set initialized)
  3. page object created
  4. add created page module.pages
  5. set page properties
  6. setmodule called on page object , adds (addpage) current page module.pages second time

you can put jpa annotations on fields , work, because setters won't called during initialization (bonus question).


Comments

Popular posts from this blog

OpenCV OpenCL: Convert Mat to Bitmap in JNI Layer for Android -

android - org.xmlpull.v1.XmlPullParserException: expected: START_TAG {http://schemas.xmlsoap.org/soap/envelope/}Envelope -

python - How to remove the Xframe Options header in django? -