9

I have entities User and GrantedRole that have a bidirectional one-to-many relation.

When I try to add a GrantedRole to the Set in User, there is no exception thrown, but when I debug the variables for the User and GrantedRole object have a description that reads

com.sun.jdi.InvocationException occurred invoking method.

The different fields for the variables can be read while debugging, but when I select the roles field in User or the user field in GrantedRole I get the same description as above. When I go into the Set of GrantedRole in user, I eventually find the following description:

Detail formatter error: An exception occurred: java.lang.StackOverflowError

My code:

public class User {
    private Set<GrantedRole> roles = new HashSet<GrantedRole>();

    public User() {
        super();
    }
    public User(String name, String password) {
        this.name = name;
        this.password = password;
    }

    @OneToMany(fetch = FetchType.EAGER, mappedBy = "user")
    public Set<GrantedRole> getRoles() {
        return roles;
    }

    public void setRoles(Set<GrantedRole> roles) {
        this.roles = roles;
    }

    // equals and hashCode are based on username
    // toString is based on all fields
}

public class GrantedRole {
    private user user;

    public GrantedRole() {
        super();
    }
    public GrantedRole(User user, Role role, Organization organization) {
        this.user = user;
        this.role = role;
        this.organization = organization;
    }

    @ManyToOne
    @NotNull
    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    // equals and hashCode are based on all fields
    // toString was based on all fields, I've changed it to not include User.
}

public class Test {
    public void testStuff() {
        User user = new User("name");
        GrantedRole role = new GrantedRole(user, "role"); //this sets the user field
        user.getRoles().add(role); //doesn't throw Exception, but debugging shows InvocationTargetException and StackOverflowException
        System.out.println(user.getRoles()); //throws StackOverflowException, as I would expect
    }
}

It was my understanding that I should be able to set up a bidirectional relation in this way. I read multiple tutorials, like this one, and I don´t see what I am doing differently to cause the .add(role) to go wrong.

I left out some trivial code, if it is needed I will gladly provide it. I haven't made code to ensure that a GrantedRole with a reference to a User is referenced by that user in return, but I think that is not relevant for the problem I'm having.

Johanneke
  • 5,443
  • 4
  • 20
  • 33
  • 1. Are the getters/setters really trivial? 2. The full stack of the `StackOverflowError` would help a lot to understand what is happening. – Grzegorz Oledzki Jun 19 '13 at 15:59
  • And you can add the code of both constructors, `User(String)` and `GrantedRole(User, String)`, so that we can understand what you're doing exactly. – eternay Jun 19 '13 at 16:04
  • 2
    It might the `toString()` method recursive call. You should make sure that you don't call the methods like `equals`, `hashCode`, ... from child in parent, only child should be interested in calling from parent. – Bhesh Gurung Jun 19 '13 at 16:05
  • 1
    I just realized that the reason the description when debugging shows the exceptions, is because it is trying to print the variables, which causes the exception. – Johanneke Jun 20 '13 at 08:16
  • I've updated my question to include the information requested, apart from the Exception. I was just being stupid and have solved the problem. I will post it as an answer. – Johanneke Jun 20 '13 at 08:27

1 Answers1

19

So, I was just being stupid and had a recursive call in the toString() methods. User.toString() tried to print the roles, and GrantedRole.toString() tried to print the user.

I fixed it by altering the GrantedRole.toString() to print user.getUsername(), thus breaking the cycle.

Johanneke
  • 5,443
  • 4
  • 20
  • 33
  • 2
    the same can happen with your .hashCode() method :) – Bogdan Emil Mariesan Aug 27 '14 at 10:41
  • 1
    Thanks, stuck with this stackoverflow issue for more than a day for one to one bidirectional.Resolved the issue by modifing the tostring() – Bhanu prathap Nov 03 '16 at 06:44
  • I can confirm this. We also had a Stackoverflow and thought we need to specify all ManyToOne combinations with @JsonManagedReference/@JsonBackReference. But in the end I just commented all toString() methods of the entities out and then one by one back in and realized two entities created an infinite loop in their toString Methods. – Invest Aug 27 '21 at 12:33