JPA: java.lang.StackOverflowError при добавлении метода toString в классы сущностей

Все работало нормально, пока я не добавил toSting() в свои классы сущностей.

После чего я начинаю получать следующую ошибку во время выполнения:

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: добавлен код для объекта Student

@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 + "]";
    }   

}

person skip    schedule 31.05.2014    source источник


Ответы (3)


Обновлено в связи с добавлением класса Student.

Судя по трассировке стека, ваша проблема связана с Student.toString(), так вот что происходит:

В Teacher.toString() вы неявно вызываете Student.toString(), помещая элемент students в оператор конкатенации String: + students +. В Student.toString() код делает нечто подобное, включая элемент teacher в оператор конкатенации String.

Это означает, что вызов Teacher.toString() или Student.toString() приведет к бесконечному циклу, в котором: Teacher.toString() неявно вызывает Student.toString(), который, в свою очередь, неявно вызывает Teacher.toString(), который, в свою очередь, вызывает Student.toString(), который, в свою очередь, вызывает...

Реализации 2 .toString() продолжают вызывать туда-сюда, туда-сюда, туда-сюда, в бесконечном цикле, который в конечном итоге переполняет стек и приводит к java.lang.StackOverflowError.

Чтобы решить эту проблему, вы должны удалить неявные ссылки на .toString() методы сущностей. В качестве замены вы могли бы Teacher.toString() просто вывести length() коллекции students и, возможно, включить список имен Student. А в Student.toString() просто включите члена Teacher.name.

person Sean Mickey    schedule 31.05.2014
comment
Отредактировал сообщение, добавив код для объекта Student внизу. - person skip; 31.05.2014
comment
Только что обновил метод toString в объекте Guide до этого public String toString() { return "Guide [id=" + id + ", staffId=" + staffId + ", name=" + name + ", salary=" + salary + ", students.size()=" + students.size() + "]"; }. Это решило проблему. Спасибо. - person skip; 31.05.2014
comment
@skip - я обновил свой ответ на основе вашего добавления класса Student. Моя первоначальная догадка была правильной, и мой ответ должен помочь вам решить проблему. - person Sean Mickey; 31.05.2014

Вы должны исключить лишний параметр из метода toString().

GL

person Braian Coronel    schedule 25.07.2020

У учителя Entity Class есть toString() проблема:

@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
            + "]";
}

}
person user8300258    schedule 13.07.2017