JPA Bidirectional relationship throws java.lang.StackOverflowError: null
Asked Answered
M

4

5

i'm trying to relate two entities with JPA, when i run the project don't throws me errors but when i request the method that get me the Voters of a Comunity Leader i get the next Exception:

java.lang.StackOverflowError: null
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:245) ~[gson-2.8.5.jar:na]
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:69) ~[gson-2.8.5.jar:na]
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:127) ~[gson-2.8.5.jar:na]
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:245) ~[gson-2.8.5.jar:na]
at com.google.gson.Gson$FutureTypeAdapter.write(Gson.java:1018) ~[gson-2.8.5.jar:na]
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:69) ~[gson-2.8.5.jar:na]
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:97) ~[gson-2.8.5.jar:na]
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:61) ~[gson-2.8.5.jar:na]
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:69) ~[gson-2.8.5.jar:na]
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:127) ~[gson-2.8.5.jar:na]
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:245) ~[gson-2.8.5.jar:na]
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:69) ~[gson-2.8.5.jar:na]
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:127) ~[gson-2.8.5.jar:na]
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:245) ~[gson-2.8.5.jar:na]
at com.google.gson.Gson$FutureTypeAdapter.write(Gson.java:1018) ~[gson-2.8.5.jar:na]
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:69) ~[gson-2.8.5.jar:na]
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:97) ~[gson-2.8.5.jar:na]
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:61) ~[gson-2.8.5.jar:na]
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:69) ~[gson-2.8.5.jar:na]
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:127) ~[gson-2.8.5.jar:na]
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:245) ~[gson-2.8.5.jar:na]
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:69) ~[gson-2.8.5.jar:na]
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:127) ~[gson-2.8.5.jar:na]
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:245) ~[gson-2.8.5.jar:na]
at com.google.gson.Gson$FutureTypeAdapter.write(Gson.java:1018) ~[gson-2.8.5.jar:na]

I'ts like a loop, i don't khow how to resolve this.

Here are my classes:

1)Leader Entity :

@Entity
@Table(name = "leaders")
public class Leader implements Serializable {

    private static final long serialVersionUID = 1L;

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

    @NotEmpty
    String phone;

    @NotEmpty
    String name;

    @OneToMany( mappedBy = "leader", fetch = FetchType.LAZY,cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Voter> voters;



    public Leader() {
        voters = new ArrayList<>();
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getCelular() {
        return phone;
    }

    public void setCelular(String celular) {
        this.phone = celular;
    }

    public String getNombre() {
        return name;
    }

    public void setNombre(String nombre) {
        this.name = nombre;
    }

    public List<Voter> getVotantes() {
        return voters;
    }

    public void setVotantes(List<Voter> votantes) {
        this.voters = votantes;
    }

    public void addVotante(Voter votante){
        voters.add(votante);
    }
}

2)Voter Entity :

@Entity
@Table(name = "voters")
public class Voter implements Serializable {

    @Id
    String id;

    String name;

    String phone;

    String email;

    @Column(name = "electoral_school")
    String electoralSchool;

    @Temporal(TemporalType.DATE)
    Date registrationDate;

    @ManyToOne(fetch = FetchType.LAZY)
    private Leader leader;

    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    Sector sector;

    @PrePersist
    public void prePersist() {
        registrationDate = new Date();
    }


    public String getCedula() {
        return id;
    }

    public void setCedula(String cedula) {
        this.id = cedula;
    }

    public String getNombre() {
        return name;
    }

    public void setNombre(String nombre) {
        this.name = nombre;
    }

    public String getTelefono() {
        return phone;
    }

    public void setTelefono(String telefono) {
        this.phone = telefono;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getColegioElectoral() {
        return electoralSchool;
    }

    public void setColegioElectoral(String colegioElectoral) {
        this.electoralSchool = colegioElectoral;
    }

    public Leader getDirigente() {
        return leader;
    }

    public void setDirigente(Leader dirigente) {
        this.leader = dirigente;
    }

    public Date getRegistro() {
        return registrationDate;
    }

    public void setRegistro(Timestamp registro) {
        this.registrationDate = registro;
    }
}

3)LeaderController :

@Controller
@RequestMapping("/leader")
public class LeaderController {

    @Autowired
    IDirigenteService leaderService;

    @RequestMapping(value="getVoters/{leader_id}")
    @ResponseBody
    public String getAll(@PathVariable(value = "leader_id") Long leader_id){
        Gson gson = new Gson();
        return gson.toJson(leaderService.findById(leader_id).getVotantes());

    }

}

I fill the tables with test data in import.sql.

Thanks in advance.

Maidamaidan answered 19/2, 2019 at 21:33 Comment(0)
A
7

Hibernate OneToMany or JPA Bidirectional relationship throws java.lang.StackOverflowError: null is a Circular dependency issue that can originate from Lombok's toString() autogenerated method if you use @Data complex annotation.

To exclude your circular dependency for a certain field or better still, you use @Getter and @Setter if you do not need the toString(). Well, when I had those issues above these solutions worked for me.

You may then use the @JsonIgnore on the particular attribute.

@Getter

@Setter

public class MyClass {

}
Aphonic answered 26/4, 2020 at 0:57 Comment(0)
K
3

Your Leader and Voter classes has references for each other, so serialization go into recursion.

Add @JsonIgnoreProperties to corresponding fields:

@OneToMany( mappedBy = "leader", fetch = FetchType.LAZY,cascade = CascadeType.ALL, orphanRemoval = true)
@JsonIgnoreProperties("leader")
private List<Voter> voters;

and

@ManyToOne(fetch = FetchType.LAZY)
@JsonIgnoreProperties("voters")
private Leader leader;

PS: Oops, it's for jackson, not for gson. But idea is the same - take a look here, how to exclude fields for serialization.

Khalilahkhalin answered 19/2, 2019 at 21:45 Comment(1)
I changed to jackson and wrote the code like you but it's the same java.lang.StackOverflowError: nullMaidamaidan
E
0

Correct, the serialization ends up in an infinite recursion trying to serialize the objects that reference each other.

When using Gson to serialize referenced types, try using an exclusion strategy.

In the following snippet I've informed Gson to exclude fields in the Leader class annotated with ManyToOne:

    @RequestMapping(value="getVoters/{leader_id}")
    @ResponseBody
    public String getAll(@PathVariable(value = "leader_id") Long leader_id){
        ExclusionStrategy strategy = new ExclusionStrategy() {
            @Override
            public boolean shouldSkipField(FieldAttributes field) {
                return field.getDeclaringClass() == Leader.class &&
                        field.getAnnotation(OneToMany.class) != null;
            }

            @Override
            public boolean shouldSkipClass(Class<?> clazz) {
                return false;
            }
        };

        Gson gson = new GsonBuilder().disableHtmlEscaping()
            .addSerializationExclusionStrategy(strategy)
            .create();
        return gson.toJson(leaderService.findById(leader_id).getVotantes());
    }
Extrusive answered 20/2, 2019 at 9:19 Comment(0)
P
0

@ToString(exclude = "leader") public class Voter{ ...... .....

}

Prevention answered 6/5, 2022 at 12:25 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Mosa

© 2022 - 2024 — McMap. All rights reserved.