Home > Software design >  How do I solve the stackoverflow problem? BeanSerializer error
How do I solve the stackoverflow problem? BeanSerializer error

Time:01-04

I want to start a relationship but I get an error that I can't understand.

Person.java

@Entity
@Getter
@Setter
@RequiredArgsConstructor
public class Personal implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String personName;
    private String personLastName;

    @OneToOne(cascade = CascadeType.ALL)
    private Address address;
}

Address.java

@Entity
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
public class Address implements Serializable {

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

    @Column(name = "Street")
    private String street;
    @Column(name = "Alley")
    private String alley;
    @Column(name = "District")
    private String district;


    @OneToOne(fetch = FetchType.EAGER, mappedBy = "address")
    @MapsId
    private Person person;

    
    public Address(String street, String alley, String district, Person person) {
        this.street = street;
        this.alley = alley;
        this.district = district;
        this.person = person; 
    }
}

AddressService.java

@Service
@RequiredArgsConstructor
public class AddressServices {


    private final AddressRepository addressRepository;


    public Optional<Address> getAddress(Integer id){
        return addressRepository.findById(id);
    }
}

@Service
@RequiredArgsConstructor
public class PersonService {

    private final PersonRepository personRepository;


    public String createUser(){
        Person u1 = new Person();
        u1.setId(u1.getId());
        u1.setUserName("test");
        u1.setUserLastName("test");
        Address address = new Address();
        address.setAlley("test");
        address.setDistrict("test");
        address.setStreet("test");
        address.setPerson(u1);
        u1.setAddress(address);
        personRepository.save(u1);
        return "Save is successful";
    }

Data is successfully saved in the database. However, when I run "localhost:8080/gget-address?id=1" via postman, I get an error.

ERROR :

java.lang.StackOverflowError: null
    at java.base/java.util.concurrent.ConcurrentHashMap.putVal(ConcurrentHashMap.java:1012) ~[na:na]
    at java.base/java.util.concurrent.ConcurrentHashMap.putIfAbsent(ConcurrentHashMap.java:1541) ~[na:na]
    at java.base/java.lang.ClassLoader.getClassLoadingLock(ClassLoader.java:667) ~[na:na]
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:591) ~[na:na]
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:579) ~[na:na]
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178) ~[na:na]
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521) ~[na:na]
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:774) ~[jackson-databind-2.13.4.2.jar:2.13.4.2]
    at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178) ~[jackson-databind-2.13.4.2.jar:2.13.4.2]
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728) ~[jackson-databind-2.13.4.2.jar:2.13.4.2]
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:774) ~[jackson-databind-2.13.4.2.jar:2.13.4.2]
    at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178) ~[jackson-databind-2.13.4.2.jar:2.13.4.2]
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728) ~[jackson-databind-2.13.4.2.jar:2.13.4.2]
    at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:774) ~[jackson-databind-2.13.4.2.jar:2.13.4.2]

However, I still have the same problem! :) nothing has changed. the record to the database is successful, but it continues to give the same error when I want to retrieve the data

Before asking this question, I searched for similar questions but I could not find any answer to my problem.

I tried notations such as @JsonIgnore, @JsonBackReference, @JsonManagamedReference, but when I add them, this time the relationship is not established (lombok ignores) my goal is to establish the relationship mutually. I wonder if "Lombok" is making a mistake. I'm using Lombok. ?

What I want is that when I run "localhost:8080/gget-address?id=1", I can see all "person" with address 1.

There is a problem when using OneToOne and mappedBy. There is an infinite loop like Person-> Address --> Person --> Address.

I think it falls into serialization error for this reason. I need to solve this. However, I cannot realize what I want in the use of annotation such as @JsonIgnore, @JsonBackReference, @JsonManagamedReference. When I add them, the "field" is invisible and when I go to address 1, I cannot see the "people" who use address 1.

CodePudding user response:

You didn't include the Person class in your post. Assuming it is the same as the Personal class that you've included, it's likely a circular reference between Person and Address.

A Person has an Address which has a Person which has an Address, etc.

You can solve this by either using @JsonManagedReference and @JsonBackReference, or by adding @JsonIgnore.

In your Address class, adding @JsonIgnore to the person will likely fix this issue.

CodePudding user response:

It is very bad pratice to return the entity object directly in controller. You should map this to simple dto before. When you want get object from the controller object mapper is trying to serialize address it also serialize person (because it is class member) entity that has n adresses that would be also serialized and then serialize n persons that have n addresses etc. There is a recursion that overflow the stack.

It would looks something like:

address: {
properies....
person: {
   properties...
   address: [
   {
      properies....
      person {... address: ...}
   },
   {
      properies....
      person {... address: ...}
   },
   {
      properies....
      person {... address: ...}
   }
   ...
}]}}

Imo the best way and practive is creating simple dto like that:

public class AddressDto implements Serializable {

    private Integer id;
    private String street;
    private String alley;
    private String district;
    private PersonDto person;
}
  • Related