I have an issue with a manytomany bi-directional JPA mapping that is causing me a stack overflow error While I have a total of 5 entities in my application I believe the issue is only related to 2 of them which are described below for simplicity.
Entities:
Application
User
An Application can have many developers
A User can develop many applications
Here is how they are mapped:
//Application Entity:
@ManyToMany
private List<Users> users
//User Entity
@ManyToMany(mappedBy = "users", fetch = FetchType.LAZY)
private List <Applications> applications;
This results in 3 tables being created in the database:
APPLICATIONS < ID APPNAME
APPLICATION_USERS < Join Table contains USERS_ID APPLICATIONS_ID
USERS < ID USER NAME
It is a circular reference that keeps running until there is a stack overflow.
The application works fine when it is first deployed with empty tables. A user registers for an application and this creates a row in the APPLICATIONS table (if the Application does not exist) A row is also created in the USERS table (if the user does not exist) and the join table APPLICATION_USERS is populated with the ID from the APPLICATION Table called APPLICATIONS_ID and the ID from the USERS table called USERS_ID.
You can add as many applications or users as you wish and the application works perfectly. I have verified that data is being loaded and persisted into the 3 tables exactly as expected Here is an example of the data in the tables after a user registers an Application:
APPLICATIONS
ID 51
APPLICATION_USERS
USERS_ID APPLICATIONS_ID
1 51
USERS
ID
1
Now when the server is stopped and restarted or when the application is re-deployed using create-tables (vs drop-and-create-tables) (and data is present in the tables) then I get a stack overflow at each entities toString() function. I have run this in debug with breakpoints on the Applications toString() function and on the Users toString() function and I can click resume and watch each toString() function get called over and over until the stack overflow results.
Here is the console log:
(Entity query being executed)
[EL Fine]: 2014-01-21 14:48:44.383--ServerSession(1615948530)--Connection(49767657)--Thread(Thread[http-bio-8080-exec-9,5,main])--SELECT t1.ID, t1.APPIDENTIFIER, t1.DATECREATED, t1.DATEMODIFIED, t1.DEVICETYPE FROM APPLICATIONS_Users t0, APPLICATIONS t1 WHERE ((t0.users_ID = ?) AND (t1.ID = t0.applications_ID))
(second entity query is invoked)
[EL Fine]: 2014-01-21 14:50:02.444--ServerSession(1615948530)--Connection(1871047709)--Thread(Thread[http-bio-8080-exec-9,5,main])--SELECT t1.ID, t1.DATECREATED, t1.DATEMODIFIED, t1.EMAIL, t1.FIRSTNAME, t1.FULLNAME, t1.LASTLOGINDATE, t1.LASTNAME, t1.USERNAME FROM APPLICATIONS_Users t0, Users t1 WHERE ((t0.applications_ID = ?) AND (t1.ID = t0.users_ID))
[EL Finest]: 2014-01-21 14:50:02.471--ServerSession(1615948530)--Connection(1601422824)--Thread(Thread[http-bio-8080-exec-9,5,main])--Connection released to connection pool [read].
java.lang.StackOverflowError
at java.util.Vector.get(Vector.java:693) at java.util.AbstractList$Itr.next(AbstractList.java:345) at java.util.AbstractCollection.toString(AbstractCollection.java:421) at java.util.Vector.toString(Vector.java:940) at org.eclipse.persistence.indirection.IndirectList.toString(IndirectList.java:797) at java.lang.String.valueOf(String.java:2826) at java.lang.StringBuilder.append(StringBuilder.java:115) at com.sap.crashlogserver.dao.entities.Applications.toString(Applications.java:150) at java.lang.String.valueOf(String.java:2826) at java.lang.StringBuilder.append(StringBuilder.java:115) at java.util.AbstractCollection.toString(AbstractCollection.java:422) at java.util.Vector.toString(Vector.java:940) at org.eclipse.persistence.indirection.IndirectList.toString(IndirectList.java:797) at java.lang.String.valueOf(String.java:2826) at java.lang.StringBuilder.append(StringBuilder.java:115) at com.sap.crashlogserver.dao.entities.Users.toString(Users.java:168) at java.lang.String.valueOf(String.java:2826) at java.lang.StringBuilder.append(StringBuilder.java:115) at java.util.AbstractCollection.toString(AbstractCollection.java:422) at java.util.Vector.toString(Vector.java:940) at org.eclipse.persistence.indirection.IndirectList.toString(IndirectList.java:797) at java.lang.String.valueOf(String.java:2826) at java.lang.StringBuilder.append(StringBuilder.java:115) at com.sap.crashlogserver.dao.entities.Applications.toString(Applications.java:150) at java.lang.String.valueOf(String.java:2826) at java.lang.StringBuilder.append(StringBuilder.java:115) at java.util.AbstractCollection.toString(AbstractCollection.java:422) at java.util.Vector.toString(Vector.java:940)
Based on a number of threads I have read I tried: 1. Reversing the mappings, 2. Adding @JsonIgnore to some of the entity fields 3. Using fetch = FetchType.LAZY
and many other config tweaks but none of them resolved this issue. Some of the suggestions like using transient fields. I am not sure this is supported in my JPA implementation of eclipselink.
I also read a thread suggestion me to implement gson.ExclusionStrategy. Have not tried this yet.
So that's the story. I am a newbie at Java and JPA. This is a very difficult issue for me to figure out. Any suggestions you may have to help me to resolve it would be greatly appreciated.