【问题标题】:Spring Data & mongodb Converter error: java.lang.StackOverflowErrorSpring Data & mongodb 转换器错误:java.lang.StackOverflowError
【发布时间】:2013-12-07 03:58:42
【问题描述】:

我正在使用 Spring 数据连接到 mongodb。请在下面查看我的问题:

现在,我有两个数据模型(用户和草稿):

@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
}

@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...

}

在我的项目中,我已经成功创建了用户 然后,我想为现有用户添加草稿。

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()
}

在(1)中,我发现在 mongoDB 中创建了一个新的“草稿”文档,它的 _id = 它有 52a1591597d738f7b397be96。

在 (2) 中,我发现现有用户 (mainAuhtor) 文档在 createdDraft 字段中添加了一个条目,例如 [ { "$ref" : "draft" , "$id" : { "$oid" : "52a1591597d738f7b397be96"}}]

异常和日志:

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)

根本原因

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```

谁能帮我看看?非常感谢!

【问题讨论】:

    标签: java spring mongodb spring-data spring-data-mongodb


    【解决方案1】:

    这是 spring data mongodb 的一个错误(或预期的行为?)(我在 1.3.x 版本中得到这个,没有尝试过 1.4.x)。

    问题是 User 引用 Draft 和 Draft 指向同一个用户实例,因此转换器进入无限循环。

    @Document
    public class User implements Serializable {
    ...
    
    @DBRef
    private List<Draft> createdDraft = new ArrayList<Draft>();
    

    @Document
    public class Draft {
    ...
    
    @DBRef
    private User lastEditor;
    @DBRef
    private User mainAuthor;
    @DBRef
    private List<User> coAuthors = new ArrayList<User>();
    

    您可能应该使用简单的 id 引用,而不是 DBRef(这里甚至建议 http://docs.mongodb.org/manual/reference/database-references/ 适合大多数用例)

    如果您发现自己经常使用 DBRef,您应该考虑使用不同类型的数据库,例如图形数据库。

    【讨论】:

      【解决方案2】:

      您应该从 mongo 数据库映射您收到的实体(文档)..

      使用弹簧Converter interface

      例如:

      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;
      }
      

      主类或测试类

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

      【讨论】:

      • 我发现这种情况不正确,所以我像这样延迟加载引用 -> @DBRef(lazy=true).. 这样你就不会陷入无限循环,所以你也可以省略转换器
      猜你喜欢
      • 1970-01-01
      • 2016-12-29
      • 1970-01-01
      • 1970-01-01
      • 2015-12-17
      • 1970-01-01
      • 2015-10-09
      • 1970-01-01
      • 2020-10-14
      相关资源
      最近更新 更多