0

I am trying to create Many to one mapping between two entities in spring. However when I try to fetch the values using a restController I get

Java.lang.IllegalStateException: Cannot call sendError() after the response has been committed

error and an infinite JSON response. Adding JSON ignore solves this issue but then I don't get that column in my response at all. I don't know how to fix this. Please help. Thanks in advance.

Here are my entities:

@Entity
@Table(name="tbl1")
public class Data {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @Column(name = "customer")
    private String customer;



    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name="data_id", insertable = true, nullable = false, updatable = false)
    private DataList dataList1;

}

@Entity
@Table(name="tbl2")
public class DataList {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    @Column(name="data_id")
    private Long dataId;



    @Column(name="user_name")
    private String userName;



    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER,mappedBy = "dataList1")
    private List<Data> data1;


}

Repositories:

@Repository
@Transactional
public interface DataList extends JpaRepository<DataList,Long> {
}
@Repository
@Transactional
public interface Data extends JpaRepository<Data,Long> {
}

My error: Java.lang.IllegalStateException: Cannot call sendError() after the response has been committed

rock11
  • 718
  • 4
  • 17
  • 34

3 Answers3

0

1) In general putting fetch = FetchType.EAGER on @OneToMany is not an advisable practice. Try to set it to LAZY instead.

2) Make sure that Data and DataList entities properly implement equals() and hashCode() methods.

Maciej Kowalski
  • 25,605
  • 12
  • 54
  • 63
0

It is happening as JSON serializer is trying to serialize both the entities recursively again and again.

So, while serializing Data, its member dataList1 is of type DataList which further contains List<Data>, this loop will be infinite.

Ideally, in such scenarios, the entities should be mapped to some other model meant for the serialization and response or one of the model needs to be @JsonIgnore to avoid this recursive loop going.

Saheb
  • 1,392
  • 3
  • 13
  • 33
  • 1
    Adding @Json Ignore completely hides the response.I don't want that. – rock11 Jun 03 '19 at 09:55
  • So, data contains data list and data list further contains data. Do you want this internal data variable as well? – Saheb Jun 03 '19 at 09:56
  • I want that when I get Data List I should get the internal Data as well and when I get Data ,I should get that Data List as well. – rock11 Jun 03 '19 at 09:57
  • when you talk about getting data from datalist and datalist from data. Are you talking about the java code or in the json response? – Saheb Jun 03 '19 at 09:59
  • In the json response as I need to consume this response in a frontend app. – rock11 Jun 03 '19 at 10:03
  • If I am getting you correct, then it is not possible to generate such a response. – Saheb Jun 03 '19 at 10:07
  • JSON is a simple human-readable format used to transfer. In case of any object inside any programming language, it is possible because objects have reference of other objects, which avoids overflow for circular references. On the other hand in JSON, you have simple nested objects which do not have any notion of references, so if we want objects within object with circular reference, we will require a complete object and this will continue till infinity – Saheb Jun 03 '19 at 10:09
  • I would suggest getting the response as a json with one level nesting only, load the json in your frontend and recreate the object structure you want to use by traversing and mapping them correctly. JSON is a format and it is not programming construct/variable/object as such. – Saheb Jun 03 '19 at 10:11
0
  1. EAGER is a bad practice. Use LAZY, and when you need the related entities, use fetch join query.
  2. Most likely the problem here in the bi-directional relation. You fetch DataList with Data list. And each Data in List ListData refers again. More likely here to be a stack overflow when serializing json. So remember one rule: never give in controllers hibernate entities. Write a mapper for map this entities to Dto objects.

You may think that it is extremely boring to map some models to another. But here in another way. Hibernate entities should not be passed to front end. My application uses several types of objects: Entities (when fetch from DB), DTO (When map entities to DTO and give their in service components), Shared (when map DTO to Shared and share as a response to the controller between my microservices) and Model (when map from Response to Model and give to the template engine, for example freemarker). You may not need this hierarchy of models.

Create DTO's:

@AllArgsConstructor
@NoArgsConstructor
@Getter
public class DataListDto {

    private Long dataId;
    private String userName;

    private List<DataDto> data1;


}

@AllArgsConstructor
@NoArgsConstructor
@Getter
public class DataDto {

    private long id;
    private String customer;

}

write your mapper:

 public class DataListMapper {

    public static DataListDto mapToDto(DataList dataList) {
        return new DataListDto(dataList.getDataId(), dataList.getUserName(), dataList.getData1().stream().map(x -> DataListMapper.mapToDto(x).collect(Collectors.toList)))
    }

    public static DataDto mapToDto(Data data) {
        return new DataDto(data.getId(), data.getCustomer());
    }
}

your service:

public class DataListService {
            @Autowired
            private DataListRepository dataListRepository;


            public List<DataListDto> getAllData() {
                List<DataList> dataLists = this.dataListRepository.findAll();
                return dataLists.stream().map(x->DataListMapper.mapToDto(x)).collect(Collectors.toList());
            }
        }
Peter Kozlovsky
  • 633
  • 2
  • 10
  • 28