user3076701
user3076701

Reputation: 21

Spring Data & mongodb Converter error: java.lang.StackOverflowError

I am using Spring data connect to mongodb. Please see my problems below:

Now, I have two data models (User and Draft):

@Document

public class User implements Serializable {
@Id
private String id;
private String showName;
private String password;
//..... (other attributes)

@DBRef
private List<Draft> createdDraft = new ArrayList<Draft>();

//constructors
public User() {
super();
}

public User(String id, String showName, String password, //....other attributes
List<Draft> createdDraft) {
super();
this.id = id;
this.showName = showName;
this.password = password;
//....
}

//getters and setters
}

and

@Document

public class Draft {
@Id
private String id;
private String title;
private Date createTime;
private Date lastEditTime;
@DBRef
private User lastEditor;
@DBRef
private User mainAuthor;
@DBRef
private List<User> coAuthors = new ArrayList<User>();
private String externalURL;

//constructors..
public Draft() {
super();
}

public Draft(String id, String title, Date createTime, Date lastEditTime,
User lastEditor, User mainAuthor, String externalURL) {
super();
this.id = id;
this.title = title;
this.createTime = createTime;
this.lastEditTime = lastEditTime;
this.lastEditor = lastEditor;
this.mainAuthor = mainAuthor;
this.externalURL = externalURL;
}

//getters and setters...

}

In my project, I have created the user successfully then, I would like to add a draft to the existing user.

public String CreateNewDraft(User mainAuthor)
{
Draft draft = new Draft();

draft.setMainAuthor(mainAuthor); 

Date now = new Date(System.currentTimeMillis());
 
draft.setCreateTime(now);

mainAuthor.getCreatedDraft().add(draft); 

//insert the draft --> Successful (1)

mongoOps.insert(draft);

//update the user --> Successful (2)

mongoOps.save(mainAuthor);

//find the last inserted draft. --> Errors.

Draft d = mongoOps.findOne(query(where("createTime").is(now) ), Draft.class);

return d.getId()
}

In (1), I have found a new "draft" document created in mongoDB, which has _id = it has 52a1591597d738f7b397be96.

In (2), I have found the existing user (mainAuhtor) document has one entry added in the createdDraft field like [ { "$ref" : "draft" , "$id" : { "$oid" : "52a1591597d738f7b397be96"}}]

Exception & log:

processing failed; nested exception is java.lang.StackOverflowError

org.springframework.web.servlet.DispatcherServlet.
doDispatch(DispatcherServlet.java:972)
org.springframework.web.servlet.DispatcherServlet.
doService(DispatcherServlet.java:852)
org.springframework.web.servlet.FrameworkServlet.p
rocessRequest(FrameworkServlet.java:882)
org.springframework.web.servlet.FrameworkServlet.d
oGet(FrameworkServlet.java:778)
javax.servlet.http.HttpServlet.service(HttpServlet .java:621)
javax.servlet.http.HttpServlet.service(HttpServlet .java:728)
org.apache.tomcat.websocket.server.WsFilter.doFilt
er(WsFilter.java:51)

root cause

java.lang.StackOverflowError 

java.net.SocketInputStream.socketRead0(Native Method)
java.net.SocketInputStream.read(Unknown Source)
java.net.SocketInputStream.read(Unknown Source)
java.io.BufferedInputStream.fill(Unknown Source)
java.io.BufferedInputStream.read1(Unknown Source)
java.io.BufferedInputStream.read(Unknown Source)
org.bson.io.Bits.readFully(Bits.java:46)
org.bson.io.Bits.readFully(Bits.java:33)
org.bson.io.Bits.readFully(Bits.java:28)
com.mongodb.Response.<init>(Response.java:40)
com.mongodb.DBPort.go(DBPort.java:124)
com.mongodb.DBPort.call(DBPort.java:74)
com.mongodb.DBTCPConnector.innerCall(DBTCPConnecto r.java:286)
com.mongodb.DBTCPConnector.call(DBTCPConnector.jav a:257)
com.mongodb.DBApiLayer$MyCollection.__find(DBApiLa yer.java:310)
com.mongodb.DBApiLayer$MyCollection.__find(DBApiLa yer.java:295)
com.mongodb.DBCollection.findOne(DBCollection.java :346)
com.mongodb.DBCollection.findOne(DBCollection.java :331)
com.mongodb.DBRefBase.fetch(DBRefBase.java:53)
org.springframework.data.mongodb.core.convert.Mapp
ingMongoConverter.readValue(MappingMongoConverter. java:1046)
org.springframework.data.mongodb.core.convert.Mapp
ingMongoConverter.access$100(MappingMongoConverter .java:77)
org.springframework.data.mongodb.core.convert.Mapp
ingMongoConverter$MongoDbPropertyValueProvider.get
PropertyValue(MappingMongoConverter.java:999)
org.springframework.data.mongodb.core.convert.Mapp
ingMongoConverter.getValueInternal(MappingMongoCon verter.java:755)
org.springframework.data.mongodb.core.convert.Mapp
ingMongoConverter$2.doWithAssociation(MappingMongo Converter.java:265)
org.springframework.data.mapping.model.BasicPersis
tentEntity.doWithAssociations(BasicPersistentEntit y.java:269)
org.springframework.data.mongodb.core.convert.Mapp
ingMongoConverter.read(MappingMongoConverter.java: 262)
org.springframework.data.mongodb.core.convert.Mapp
ingMongoConverter.read(MappingMongoConverter.java: 223)
org.springframework.data.mongodb.core.convert.Mapp
ingMongoConverter.readCollectionOrArray(MappingMon
goConverter.java:788)
org.springframework.data.mongodb.core.convert.Mapp
ingMongoConverter.readValue(MappingMongoConverter. java:1048)
org.springframework.data.mongodb.core.convert.Mapp
ingMongoConverter.access$100(MappingMongoConverter .java:77)
org.springframework.data.mongodb.core.convert.Mapp
ingMongoConverter$MongoDbPropertyValueProvider.get
PropertyValue(MappingMon```

Can someone help me to take a look? Thanks so much!

Upvotes: 2

Views: 5386

Answers (3)

spring-developer
spring-developer

Reputation: 11

I was also facing this issue while querying data from mongo. In my case, it was a dataType mismatch, I was using org.joda.DateTime to query on a document field of type java.time.LocalDateTime.

For anyone facing such issue, please just have a look in this direction as well

Upvotes: 0

besanur
besanur

Reputation: 31

you should map your received entity (document) from the mongo database..

use the Springs Converter interface

for example:

public class ProfileReadConverter implements Converter<DBObject, Profile> {
@Override
public Profile convert(DBObject source) {
    @SuppressWarnings("unchecked")
    Profile p = new Profile((ObjectId) source.get("_id"), (boolean) source.get("active"), (String) source.get("name"),
            (String) source.get("shortName"), (List<Person>) source.get("person"));
    return p;
    }
}

Profile.java

@Document(collection = "profile")
public class Profile {

@Id
private ObjectId id;
private boolean active;
@Indexed(unique = true)
@Field("ProfileName")
private String name;
private String shortName;
@DBRef
private List<Person> person = new ArrayList<Person>();

public Profile() {

}

@PersistenceConstructor
public Profile(ObjectId id, boolean active, String name, String shortName, List<Person> person,) {
    this.id = id;
    this.active = active;
    this.name = name;
    this.shortName = shortName;
    this.person = person;
}
//getter and setter

Person.java

@Document(collection = "person")
public class Person extends Ressource {

@Indexed
private String firstname;
private String lastname;
@Field("email")
@Indexed(unique = true)
private String eMailAddress;
private String login;
@DBRef
private List<Profile> profiles = new ArrayList<Profile>();

public Person(ObjectId id, String firstname, String lastname, String eMailAddress, String login) {
    this.setId(id);
    this.firstname = firstname;
    this.lastname = lastname;
    this.eMailAddress = eMailAddress;
    this.login = login;
}

@PersistenceConstructor
public Person(ObjectId id, String firstname, String lastname, String eMailAddress, String login,
        List<Profile> profiles) {
    this.firstname = firstname;
    this.lastname = lastname;
    this.eMailAddress = eMailAddress;
    this.login = login;
    this.profiles = profiles;
}

main or test class

    ...
    Profile profileFind = mongoOps.findOne(new Query(where("shortName").is("SE")), Profile.class, "profile");

Upvotes: 0

František Hartman
František Hartman

Reputation: 15076

This is a bug (or expected behaviour?) of spring data mongodb (I get this in 1.3.x version, haven't tried 1.4.x).

The problem is that User has reference to Draft and Draft to the same user instance so the converter gets into infinite loop.

@Document
public class User implements Serializable {
...

@DBRef
private List<Draft> createdDraft = new ArrayList<Draft>();

and

@Document
public class Draft {
...

@DBRef
private User lastEditor;
@DBRef
private User mainAuthor;
@DBRef
private List<User> coAuthors = new ArrayList<User>();

You should probably use simple id references, not DBRef (it is even suggested here http://docs.mongodb.org/manual/reference/database-references/ as suitable for most use cases)

If you find yourself using DBRef a lot you should consider using different kind of database, e.g. a graph database.

Upvotes: 3

Related Questions