Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JPA: java.lang.StackOverflowError on adding toString method in entity classes

Everything worked fine until I added toSting() in my entity classes.

After which I start getting the following error in runtime:

Exception in thread "main" java.lang.StackOverflowError
    at java.lang.AbstractStringBuilder.append(Unknown Source)
    at java.lang.StringBuilder.append(Unknown Source)
    at java.lang.StringBuilder.<init>(Unknown Source)
    at entity.Guide.toString(Guide.java:51)
    at java.lang.String.valueOf(Unknown Source)
    at java.lang.StringBuilder.append(Unknown Source)
    at entity.Student.toString(Student.java:45)
        ...

@Entity
public class Teacher {

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

    private String name;

    @OneToMany(mappedBy="teacher", cascade={CascadeType.PERSIST})
    private Set<Student> students = new HashSet<Student>(); 

    public Teacher() {}
    public Teacher(String name) {
        this.name = name;
    }

    public Set<Student> getStudents() {
        return students;
    }       
    public void addStudent(Student student) {
        students.add(student);
        student.setTeacher(this);
    }
    @Override
    public String toString() {
        return "Teacher[id=" + id + ", name=" + name
                + ", students=" + students + "]";
    }

}

public class SnafuClient {
    public static void main(String[] args) {

        EntityManagerFactory emf = Persistence.createEntityManagerFactory("snafu");
        EntityManager em = emf.createEntityManager();
        EntityTransaction txn = em.getTransaction();

                try {
                    txn.begin();

                    Query query = em.createQuery("select teacher from Teacher teacher");
                    List<Teacher> teachers = query.getResultList();

                    for (Teacher teacher: teachers) {
                System.out.println(teacher);
            }


                    txn.commit();
                }   catch(Exception e) {
                    if(txn != null) { txn.rollback(); }
                    e.printStackTrace();
             }  finally {
                    if(em != null) { em.close(); }
                }

    }
}

EDIT: Code for Student entity added

@Entity
public class Student {

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

    private String name;

    @ManyToOne(cascade={CascadeType.PERSIST, CascadeType.REMOVE})
    @JoinColumn(name="teacher_id")
    private Teacher teacher;

    public Student() {}
    public Student(String name, Teacher teacher) {
        this.name = name;
        this.teacher = teacher;
    }

    public Teacher getTeacher() {
        return teacher;
    }
    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }
    @Override
    public String toString() {
        return "Student [id=" + id + 
                + ", name=" + name + ", teacher=" + teacher + "]";
    }   

}
like image 992
skip Avatar asked May 31 '14 17:05

skip


People also ask

How to avoid stackoverflowerror in Java?

The most common case that can possibly exhaust a Java application’s stack is recursion. In recursion, a method invokes itself during its execution. Recursion is considered as a powerful general-purpose programming technique, but must be used with caution, in order for the StackOverflowError to be avoided.

What is StackOverflow error in Java?

Here is an example of java.lang.StackOverflowError thrown due to unintended recursion: In this example, the recursive method print () calls itself over and over again until it reaches the maximum size of the Java thread stack since a terminating condition is not provided for the recursive calls.

What are the common causes of stackoverflowerror?

Some of the most common causes for a java.lang.StackOverflowError are: Deep or infinite recursion - If a method calls itself recursively without a terminating condition. Cyclic relationships between classes - If a class A instantiates an object of class B, which in turn instantiates an object of class A.


1 Answers

Updated based on the addition of the Student class

According to the stack trace, your problem is associated with the Student.toString(), so here's what is happening:

In Teacher.toString(), you are implicitly calling the Student.toString() by placing the students member within a String concatenation statement: + students +. Within Student.toString() the code does something similar, by including the teacher member within a String concatenation statement.

This means that calling either Teacher.toString() or Student.toString() will end up causing a never-ending loop where: Teacher.toString() implicitly calls Student.toString(), which in turn implicitly calls Teacher.toString(), which in turn calls Student.toString(), which in turn calls...

The 2 .toString() implementations keep calling back and forth, back and forth, back and forth, in a never-ending loop, which eventually overflows the stack and results in a java.lang.StackOverflowError.

To correct the problem, you should remove the implicit references to the .toString() methods of the entities. As a replacement, you could have Teacher.toString() simply output the length() of the students collection and maybe include a list of the Student name(s). And in the Student.toString(), simply include the Teacher.name member.

like image 103
Sean Mickey Avatar answered Oct 24 '22 01:10

Sean Mickey