In the process of learning java programming, I write for myself a small RESTful service that caused the problem.

To develop a RESTful service I use Spring Boot and Hibernate. The essence of the problem: after introducing a one-to-many relationship between database entities, I cannot transfer objects read from the database to the client. When writing to the database there are no problems.

Here is the graphical database schema: graphic database

When executing the object transfer method, an error occurs.

2016-06-15 20:24:36.631 WARN 1372 --- [nio-8080-exec-1] .wsmsDefaultHandler ExceptionResolver : Failed to write HTTP message: org.springframework.http.converter.HttpMessageNotWritableException: Could not write content: could not extract ResultSet (through reference chain: prodinfo.models.Avtor["lkat"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: could not extract ResultSet (through reference chain: prodinfo.models.Avtor["lkat"]) 

The method of request and transfer of the object itself:

 @RequestMapping(value="/getlist") @ResponseBody public Avtor avtorout(String urladdress) { List<Avtor> avtorlist = avtorDao.getByUrladdress(urladdress); Avtor avtor = avtorlist.get(0); return avtor; } 

The getByUrladdress () method from avtorDao:

 public List<Avtor> getByUrladdress(String urladdress) { return entityManager.createQuery( "from Avtor where urladdress = :urladdress") .setParameter("urladdress", urladdress) .getResultList(); } 

Avtor Entity Model:

 @Entity @Table(name="Avtor" ,schema="public" ) public class Avtor implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) private long id; @NotNull @Type(type = "text") private String urladdress; @Type(type = "text") private String avtorname; private String update_avtor; @OneToMany(cascade = CascadeType.ALL, mappedBy = "title_lkat") private Set<Lkat> lkat = new HashSet<>(); public Set<Lkat> getLkat() { return this.lkat; } public void setLkat(Set<Lkat> lkat) { this.lkat = lkat; } public void addLkat(Lkat lkat) { lkat.setAvtor(this); this.lkat.add(lkat); } public Avtor() { } public Avtor(long id) { this.id = id; } public Avtor(String urladdress) { this.urladdress = urladdress; } public Avtor(String avtorname, String update_avtor) { this.avtorname = avtorname; this.update_avtor = update_avtor; } public Avtor(long id, String avtorname, String update_avtor) { this.id = id; this.avtorname = avtorname; this.update_avtor = update_avtor; } public Avtor(String urladdress, String avtorname, String update_avtor) { this.urladdress = urladdress; this.avtorname = avtorname; this.update_avtor = update_avtor; } public Avtor(long id, String urladdress, String avtorname, String update_avtor) { this.id = id; this.urladdress = urladdress; this.avtorname = avtorname; this.update_avtor = update_avtor; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getUrladdress() { return urladdress; } public void setUrladdress(String urladdress) { this.urladdress = urladdress; } public String getAvtorname() { return avtorname; } public void setAvtorname(String avtorname) { this.avtorname = avtorname; } public String getUpdate_avtor() { return update_avtor; } public void setUpdate_avtor(String update_avtor) { this.update_avtor = update_avtor; } } 

Because without a one-to-many connection, objects are transferred without problems, the question arises how to transfer objects from a database with entity connections correctly?

  • Perhaps the problem is that your entities have links to each other. In this case, Jackson needs to additionally mark the corresponding fields with the annotations @JsonManagedReference and @JsonBackReference . But usually no one gives the Entity out, using the DTO instead. - enzo
  • Unfortunately, using @JsonManagedReference and @JsonBackReference did not solve the problem. - Nirax
  • one
    I solved the problem, there was an error in the wrong mapping. Thank you very much for the hint about the correct transfer of objects using DTO. - Nirax

1 answer 1

When adding one-to-many connections, I made a mistake in mapping database entities.

Part of the error code:

 @OneToMany(cascade = CascadeType.ALL, mappedBy = "title_lkat") private Set<Lkat> lkat = new HashSet<>(); 

As a result, the data was written to the Lkat entity without errors, since in the annotation @ManyToOne there was no attribute binding, but could not read from the DB as Hibernate, because The wrong attribute was specified.

Corrected code:

Essence Avtor:

 @OneToMany(cascade = CascadeType.ALL, mappedBy = "avtor") @JsonManagedReference private Set<Lkat> lkat = new HashSet<>(); 

Essence Lkat:

 @ManyToOne() @JoinColumn(referencedColumnName = "id") @JsonBackReference private Avtor avtor; 

UPDATE

In order to send only the necessary data, I entered three DTO classes for each database entity, an example of the AvtorDTO class implementation:

 public class AvtorDTO { private String urladdress; private String avtorname; private String update_avtor; private Set<LkatDTO> lkatDTO = new HashSet<>(); public Set<LkatDTO> getLkatDTO() { return this.lkatDTO; } public void setLkatDTO(Set<LkatDTO> lkatDTO) { this.lkatDTO = lkatDTO; } public AvtorDTO() { } public AvtorDTO(String urladdress) { this.urladdress = urladdress; } public AvtorDTO(String avtorname, String update_avtor) { this.avtorname = avtorname; this.update_avtor = update_avtor; } public AvtorDTO(String urladdress, String avtorname, String update_avtor, Set<LkatDTO> lkatDTO) { this.urladdress = urladdress; this.avtorname = avtorname; this.update_avtor = update_avtor; this.lkatDTO = lkatDTO; } public String getUrladdress() { return urladdress; } public void setUrladdress(String urladdress) { this.urladdress = urladdress; } public String getAvtorname() { return avtorname; } public void setAvtorname(String avtorname) { this.avtorname = avtorname; } public String getUpdate_avtor() { return update_avtor; } public void setUpdate_avtor(String update_avtor) { this.update_avtor = update_avtor; } } 

I also changed the transfer method of the object, in which I immediately implemented the formation of an object of the DTO class (perhaps this is not correct and should be implemented in another way, but in this case everything works), the following code was obtained:

 @RequestMapping(value="/getlist") @ResponseBody public AvtorDTO getAvtorInClient(String urladdress) { List<Avtor> avtorlist = avtorDao.getByUrladdress(urladdress); Avtor avtor = avtorlist.get(0); Set<LkatDTO> lkatsetDTO = new HashSet<>(); Set<Lkat> lkatset = avtor.getLkat(); Iterator<Lkat> itrlkat = lkatset.iterator(); while(itrlkat.hasNext()) { Lkat lkat = itrlkat.next(); Set<Lwork> lworkset = lkat.getLwork(); Iterator<Lwork> itrlwork = lworkset.iterator(); Set<LworkDTO> lworksetDTO = new HashSet<>(); while(itrlwork.hasNext()) { Lwork lwork = itrlwork.next(); LworkDTO lworkDTO = new LworkDTO(lwork.getUrl_lwork(), lwork.getTitle_lwork(), lwork.getDescription_lwork(), lwork.getSize_lwork(), lwork.getUpdate_lwork(), lwork.getAdditional_info(), lwork.getDate_create()); lworksetDTO.add(lworkDTO); } LkatDTO lkatDTO = new LkatDTO(lkat.getTitle_lkat(), lkat.getUrl_lkat(), lworksetDTO); lkatsetDTO.add(lkatDTO); } AvtorDTO avtorDTO = new AvtorDTO(avtor.getUrladdress(), avtor.getAvtorname(), avtor.getUpdate_avtor(), lkatsetDTO); return avtorDTO; }