Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stackoverflow when querying document with cycle reference using @DBRef [DATAMONGO-488] #1368

Closed
spring-projects-issues opened this issue Jul 20, 2012 · 13 comments
Assignees
Labels
in: core Issues in core support status: declined A suggestion or change that we don't feel we should currently apply type: bug A general bug

Comments

@spring-projects-issues
Copy link

spring-projects-issues commented Jul 20, 2012

Ludovic Praud opened DATAMONGO-488 and commented

Given two documents which reference each other with a DBRef

@Document
class Site {
    @DBRef User admin;
}

@Document
class User {
    @DBRef Site site;
}

Site siteSaved = siteRepository.save(new Site());
User userSaved = userRepository.save(new User(siteSaved));
siteSaved.setAdmin(userSaved);
siteRepository.save(siteSaved);

siteRepository.findOne(siteSaved.id); // stackoverflow here
java.lang.StackOverflowError
	at org.bson.BasicBSONEncoder._put(BasicBSONEncoder.java:417)
	at org.bson.BasicBSONEncoder.putObjectId(BasicBSONEncoder.java:388)
	at org.bson.BasicBSONEncoder._putObjectField(BasicBSONEncoder.java:188)
	at org.bson.BasicBSONEncoder.putObject(BasicBSONEncoder.java:121)
	at org.bson.BasicBSONEncoder.putObject(BasicBSONEncoder.java:86)
	at com.mongodb.DefaultDBEncoder.writeObject(DefaultDBEncoder.java:27)
	at com.mongodb.OutMessage.putObject(OutMessage.java:142)
	at com.mongodb.OutMessage._appendQuery(OutMessage.java:85)
	at com.mongodb.OutMessage.query(OutMessage.java:44)
	at com.mongodb.OutMessage.query(OutMessage.java:38)
	at com.mongodb.DBApiLayer$MyCollection.__find(DBApiLayer.java:303)
	at com.mongodb.DBCollection.findOne(DBCollection.java:332)
	at com.mongodb.DBCollection.findOne(DBCollection.java:318)
	at com.mongodb.DBRefBase.fetch(DBRefBase.java:52)
	at org.springframework.data.mongodb.core.convert.MappingMongoConverter$DelegatingParameterValueProvider.getParameterValue(MappingMongoConverter.java:950)
	at org.springframework.data.mapping.model.BeanWrapper.<init>(BeanWrapper.java:115)
	at org.springframework.data.mapping.model.BeanWrapper.create(BeanWrapper.java:73)
	at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:217)
	at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:200)
	at org.springframework.data.mongodb.core.convert.MappingMongoConverter.getValueInternal(MappingMongoConverter.java:712)
	at org.springframework.data.mongodb.core.convert.MappingMongoConverter$2.doWithAssociation(MappingMongoConverter.java:239)
	at org.springframework.data.mapping.model.BasicPersistentEntity.doWithAssociations(BasicPersistentEntity.java:185)
	at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:236)
	at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:200)
	at org.springframework.data.mongodb.core.convert.MappingMongoConverter$DelegatingParameterValueProvider.getParameterValue(MappingMongoConverter.java:950)
	at org.springframework.data.mapping.model.BeanWrapper.<init>(BeanWrapper.java:115)
	at org.springframework.data.mapping.model.BeanWrapper.create(BeanWrapper.java:73)

As a workaround I can break this cycle and only reference Site from User and add an isAdmin property to User class but I think it is less direct and clear this way. It requires one more query to retrieve the admin of a Site.

I fell that it is because retrieving a document from DBRef is not lazy as asked in DATAMONGO-348, but I thought it was eager only for one level depth


Affects: 1.0.2

Issue Links:

Referenced from: pull request #12

8 votes, 9 watchers

@spring-projects-issues
Copy link
Author

spring-projects-issues commented Jul 23, 2012

Oliver Drotbohm commented

That seems to be a bug in the driver. Even if we retrieved the object lazily, that retrieving would then in turn trigger the stack overflow. Would you mind rasing a ticket in the MongoDB JIRA?

@spring-projects-issues
Copy link
Author

spring-projects-issues commented Jul 23, 2012

Ludovic Praud commented

Mongodb Java Driver Jira issue created https://jira.mongodb.org/browse/JAVA-607

@spring-projects-issues
Copy link
Author

spring-projects-issues commented Jul 23, 2012

Scott Hernandez commented

Couldn't reproduce it in the driver with the supplied sample; can you please provide more of the stack trace showing the stack. I can't see how this could be a problem in the driver as dbrefs are never resolved automatically; it is just data. There seems to be a problem resolving the references and the circular references so this would seem to be in spring, since the driver just deals with dumb data

@spring-projects-issues
Copy link
Author

spring-projects-issues commented Aug 15, 2012

Matthew T. Adams commented

I'm having this problem, too, and it's not a blocker for me.

I have a reproducible test case available at https://github.com/matthewadams/spring-data-document-examples/tree/master/mongodb-hello. My changes to that project were:

  • updated pom.xml to use latest SD Mongo 1.1.0.BUILD-SNAPSHOT & Spring 3.1.2.RELEASE (also fails with SD Mongo 1.0.3.RELEASE, 1.1.0.M1, & 1.1.0.M2)
  • added field "@DBRef Account primary" to class Person & supporting methods
  • added field "@DBRef Person holder" to class Account & supporting methods
  • added "@Document" to class Account
  • added "@PersistenceConstructor" to privatized no-arg constructors on Person & Account
  • added test saveAndRetrievePrimaryAccount to SimpleMongoTest

Test fails with StackOverflowError without @DBRef annotations. With @DBRef annotations, test fails on Mac OS X 10.7.4 with Java 1.6.0_33 & Maven 3.0.3 with an abrupt process termination (googling & my own debugging indicates termination is due to infinite recursion):

matthew@newt:~/Documents/github/matthewadams/spring-data-document-examples/mongodb-hello
$ java -version
java version "1.6.0_33"
Java(TM) SE Runtime Environment (build 1.6.0_33-b03-424-11M3720)
Java HotSpot(TM) 64-Bit Server VM (build 20.8-b03-424, mixed mode)
matthew@newt:~/Documents/github/matthewadams/spring-data-document-examples/mongodb-hello
$ javac -version
javac 1.6.0_33
matthew@newt:~/Documents/github/matthewadams/spring-data-document-examples/mongodb-hello
$ mvn -version
Apache Maven 3.0.3 (r1075438; 2011-02-28 11:31:09-0600)
Maven home: /usr/share/maven
Java version: 1.6.0_33, vendor: Apple Inc.
Java home: /Library/Java/JavaVirtualMachines/1.6.0_33-b03-424.jdk/Contents/Home
Default locale: en_US, platform encoding: MacRoman
OS name: "mac os x", version: "10.7.4", arch: "x86_64", family: "mac"
matthew@newt:~/Documents/github/matthewadams/spring-data-document-examples/mongodb-hello
$ mvn clean test
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building spring-data-mongodb-examples-hello 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-clean-plugin:2.4.1:clean (default-clean) @ spring-data-mongodb-examples-hello ---
[INFO] Deleting /Users/matthew/Documents/github/matthewadams/spring-data-document-examples/mongodb-hello/target
[INFO] 
[INFO] --- maven-resources-plugin:2.4.2:resources (default-resources) @ spring-data-mongodb-examples-hello ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 3 resources
[INFO] 
[INFO] --- maven-compiler-plugin:2.1:compile (default-compile) @ spring-data-mongodb-examples-hello ---
[INFO] Compiling 5 source files to /Users/matthew/Documents/github/matthewadams/spring-data-document-examples/mongodb-hello/target/classes
[INFO] 
[INFO] --- maven-resources-plugin:2.4.2:testResources (default-testResources) @ spring-data-mongodb-examples-hello ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO] 
[INFO] --- maven-compiler-plugin:2.1:testCompile (default-testCompile) @ spring-data-mongodb-examples-hello ---
[INFO] Compiling 1 source file to /Users/matthew/Documents/github/matthewadams/spring-data-document-examples/mongodb-hello/target/test-classes
[INFO] 
[INFO] --- maven-surefire-plugin:2.5:test (default-test) @ spring-data-mongodb-examples-hello ---
[INFO] Surefire report directory: /Users/matthew/Documents/github/matthewadams/spring-data-document-examples/mongodb-hello/target/surefire-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running org.springframework.data.mongodb.examples.hello.SimpleMongoTest
2012-08-15 08:20:37,399 INFO [org.springframework.test.context.support.AbstractContextLoader] - <Detected default resource location "classpath:/org/springframework/data/mongodb/examples/hello/SimpleMongoTest-context.xml" for test class [org.springframework.data.mongodb.examples.hello.SimpleMongoTest].>
2012-08-15 08:20:37,400 INFO [org.springframework.test.context.support.DelegatingSmartContextLoader] - <GenericXmlContextLoader detected default locations for context configuration [ContextConfigurationAttributes@b6e39f declaringClass = 'org.springframework.data.mongodb.examples.hello.SimpleMongoTest', locations = '{classpath:/org/springframework/data/mongodb/examples/hello/SimpleMongoTest-context.xml}', classes = '{}', inheritLocations = true, contextLoaderClass = 'org.springframework.test.context.ContextLoader'].>
2012-08-15 08:20:37,400 INFO [org.springframework.test.context.support.AnnotationConfigContextLoader] - <Could not detect default configuration classes for test class [org.springframework.data.mongodb.examples.hello.SimpleMongoTest]: SimpleMongoTest does not declare any static, non-private, non-final, inner classes annotated with @Configuration.>
2012-08-15 08:20:37,401 INFO [org.springframework.test.context.TestContextManager] - <@TestExecutionListeners is not present for class [class org.springframework.data.mongodb.examples.hello.SimpleMongoTest]: using defaults.>
2012-08-15 08:20:37,491 INFO [org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - <Loading XML bean definitions from class path resource [org/springframework/data/mongodb/examples/hello/SimpleMongoTest-context.xml]>
2012-08-15 08:20:37,631 INFO [org.springframework.context.support.GenericApplicationContext] - <Refreshing org.springframework.context.support.GenericApplicationContext@37eb2c1b: startup date [Wed Aug 15 08:20:37 CDT 2012]; root of context hierarchy>
2012-08-15 08:20:37,705 INFO [org.springframework.beans.factory.support.DefaultListableBeanFactory] - <Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@55dec1dd: defining beans [org.springframework.beans.factory.config.CustomEditorConfigurer#0,mongoDbFactory,mongoTemplate,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor#0]; root of factory hierarchy>

Invalid access of stack red zone 0x10712fff8 rip=0x106aacf53
/bin/sh: line 1: 18132 Bus error: 10           /Library/Java/JavaVirtualMachines/1.6.0_33-b03-424.jdk/Contents/Home/bin/java -jar /var/folders/8z/yrvl9xzn3jl3czsk22sr_kf80000gn/T/surefirebooter3325364587542109980.jar /var/folders/8z/yrvl9xzn3jl3czsk22sr_kf80000gn/T/surefire8538766697642807545tmp /var/folders/8z/yrvl9xzn3jl3czsk22sr_kf80000gn/T/surefire7574065392062521213tmp

[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.277s
[INFO] Finished at: Wed Aug 15 08:20:38 CDT 2012
[INFO] Final Memory: 11M/81M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.5:test (default-test) on project spring-data-mongodb-examples-hello: There are test failures.
[ERROR] 
[ERROR] Please refer to /Users/matthew/Documents/github/matthewadams/spring-data-document-examples/mongodb-hello/target/surefire-reports for the individual test results.
[ERROR] -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException
matthew@newt:~/Documents/github/matthewadams/spring-data-document-examples/mongodb-hello
$ 

@spring-projects-issues
Copy link
Author

spring-projects-issues commented Aug 15, 2012

Matthew T. Adams commented

I meant to say that it is a blocker for me... (couldn't edit above post)

@spring-projects-issues
Copy link
Author

spring-projects-issues commented Aug 15, 2012

Oliver Drotbohm commented

Looking at both of the samples given the reason for the exception is quite obvious. You have a bi-directional relationship implemented with DBRef. As we're eagerly resolving the DBRef instances we get into a ping pong situation here. While we of course wil generally think about a solution to this, I think it's advisable to avoid circular dependencies between domain objects and essentially break them up by replacing one relationship with a repository (i.e. query) call. So for the Account / Person scenario I think a reasonable workaround is removing the Account property from the Person class and rather introduce a List<Account> findByPerson(Person person); on an AccountRepository to break the cyclic dependency

@spring-projects-issues
Copy link
Author

spring-projects-issues commented Aug 15, 2012

Matthew T. Adams commented

IMHO, bidi relationships in object models are common, especially for people coming from the JPA & JDO side of the fence.

Your suggestion of pushing logic up into the repository layer isn't ideal for me. If DATAJPA-237 is fixed, I'm on my way again using SD JPA + DataNucleus + MongoDB. Is that a possibility so that I don't have to wait for the general solution that you mentioned?

@spring-projects-issues
Copy link
Author

spring-projects-issues commented Aug 15, 2012

Ludovic Praud commented

Cyclic dependency is allowed in java objects graph in memory, is handle in java serialization, and some others persistence technology. It is handle as well in spring IoC bean injection. Of course mongodb does not provide direct cyclic dependency when querying but it does not forbid the cyclic dependency using dbref. May be it is a difficult case to handle when resolving query with spring data but it would be a great feature to use with lazy fetching

@spring-projects-issues
Copy link
Author

spring-projects-issues commented Aug 15, 2012

Oliver Drotbohm commented

Just because something is common it doesn't mean it's a good idea :). I doubt you get SD JPA running on top of DataNucleus' MongoDB implementation. As with all libraries claiming they provide a JPA API for a NoSQL store, they essentially don't. They usually provide a profile of the JPA, as some features of it essentially can't work with MongoDB (try calling EntityManager.getTransaction() or even better EntityTransaction.rollBack()), a huge part of the mapping annotations simply doesn't make sense. To round this off you're not able to use store specific functionality through JPA neither (upserts, geo-spatial stuff, indexing). To get SD JPA running you at least need the meta-model API as well as a complete Criteria API in place. None of the self-entitled JPA-for-NoSQL approaches I've seen so far provided this.

This issue will eventually resolve when we fix DATAMONGO-348. Still it's very unlikely that this ticket (as well as DATAJPA-237) will make it into the upcoming GA releases

@spring-projects-issues
Copy link
Author

spring-projects-issues commented Aug 15, 2012

Matthew T. Adams commented

@Oliver, I generally agree with your point about JPA. I think DataNucleus's JPA metamodel layer is logically above the database layer, so I still expect it to work. The reason for DATAJPA-237 is primarily due to the fact that the JPA TCK is not openly available, which is another discussion.

As for transaction management, begin & commit seem like no-ops to me, and JPA basically punts on rollback anyway, so maybe it's ok.

JDO support (DATACMNS-96) would be a great solution to this problem. I wish I had the time to contribute, but I don't (yet)

@spring-projects-issues
Copy link
Author

spring-projects-issues commented Sep 16, 2012

Hasnain Javed commented

We also had the same issue and our workaround was the same (to break the cyclic relationship in the domain model) as Ludovic Praud

@spring-projects-issues
Copy link
Author

spring-projects-issues commented Mar 25, 2016

Christoph Strobl commented

Closed due to inactivity. Please feel free to reopen the issue and provide additional information

@spring-projects-issues spring-projects-issues added type: bug A general bug status: declined A suggestion or change that we don't feel we should currently apply in: core Issues in core support labels Dec 30, 2020
@MLyousfi
Copy link

MLyousfi commented Apr 9, 2022

Oliver Drotbohm commented

Looking at both of the samples given the reason for the exception is quite obvious. You have a bi-directional relationship implemented with DBRef. As we're eagerly resolving the DBRef instances we get into a ping pong situation here. While we of course wil generally think about a solution to this, I think it's advisable to avoid circular dependencies between domain objects and essentially break them up by replacing one relationship with a repository (i.e. query) call. So for the Account / Person scenario I think a reasonable workaround is removing the Account property from the Person class and rather introduce a List<Account> findByPerson(Person person); on an AccountRepository to break the cyclic dependency

so how can we implement a many to many relationship ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core support status: declined A suggestion or change that we don't feel we should currently apply type: bug A general bug
Projects
None yet
Development

No branches or pull requests

3 participants