I have an issue with a manytomany bi-directional JPA relationship that is causing a stack overflow error
when the server is restarted (if there is data in the 3 tables that comprise the manytomany relationship.)
While I have a total of 5 entities in my application, I believe the issue is only related to
the Users and Application entities which use use a bi-directional manytomany relationship.
I have included the JPA diagram containing all entities for completeness
Below are the 2 entities that I believe are causing my circular reference stack overflow:
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 as shown below:
APPLICATIONS
APPLICATION_USERS < Join table created by JPA when using a ManyToMany relationship.
USERS
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
an ID from the USERS table called USERS_ID as shown above.
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:
Here is the problem:
Whenever 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). Application gets a stack overflow at each entities toString() function.
I have run this in debug with a 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:
- reversing the mappings,
- Adding @JsonIgnoreputting to some of the entity fields
- 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 are supported in the JPA implemenation of eclipselink.
I also read a thread said that I should implement gson.ExclusionStrategy but I have not tried this yet.
I am new to Java and JPA and greatly appreciate any suggestions you may have to help me resolve this issue.