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

BeanCreationException thrown creating 'neo4jMappingContext' with Spring Boot 2.4.2 when combined with MongoDB #25069

Closed
star4j opened this issue Feb 1, 2021 · 11 comments
Assignees
Milestone

Comments

@star4j
Copy link

@star4j star4j commented Feb 1, 2021

  1. add dependency
   <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-mongodb</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-neo4j</artifactId>
    </dependency>
  1. add Friend class
import lombok.Data;
import lombok.experimental.Accessors;
import org.bson.types.ObjectId;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.MongoId;

@Document(collection = "friend")
@Data
@Accessors(chain = true)
public class Friend {

    @MongoId
    private ObjectId id;
}
  1. startup exception

springboot 2.4.1 has no problem starting
If the type of the id field of the Friend class is changed to String, the startup is OK
**Test project address is https://github.com/star4j/test.git

@wilkinsona
Copy link
Member

@wilkinsona wilkinsona commented Feb 1, 2021

This is the failure:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'neo4jMappingContext' defined in class path resource [org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfiguration.class]: Invocation of init method failed; nested exception is org.springframework.data.mapping.MappingException: The property 'null' is not mapped to a Graph property!
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1788) ~[spring-beans-5.3.3.jar:5.3.3]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:609) ~[spring-beans-5.3.3.jar:5.3.3]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:531) ~[spring-beans-5.3.3.jar:5.3.3]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.3.jar:5.3.3]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.3.jar:5.3.3]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.3.jar:5.3.3]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.3.jar:5.3.3]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944) ~[spring-beans-5.3.3.jar:5.3.3]
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:923) ~[spring-context-5.3.3.jar:5.3.3]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:588) ~[spring-context-5.3.3.jar:5.3.3]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:144) ~[spring-boot-2.4.2.jar:2.4.2]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:767) [spring-boot-2.4.2.jar:2.4.2]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:759) [spring-boot-2.4.2.jar:2.4.2]
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:426) [spring-boot-2.4.2.jar:2.4.2]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:326) [spring-boot-2.4.2.jar:2.4.2]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1311) [spring-boot-2.4.2.jar:2.4.2]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1300) [spring-boot-2.4.2.jar:2.4.2]
	at com.example.test.TestApplication.main(TestApplication.java:10) [classes/:na]
Caused by: org.springframework.data.mapping.MappingException: The property 'null' is not mapped to a Graph property!
	at org.springframework.data.neo4j.core.mapping.DefaultNeo4jPersistentProperty.getPropertyName(DefaultNeo4jPersistentProperty.java:247) ~[spring-data-neo4j-6.0.3.jar:6.0.3]
	at org.springframework.data.neo4j.core.mapping.DefaultNeo4jPersistentEntity.computeIdDescription(DefaultNeo4jPersistentEntity.java:350) ~[spring-data-neo4j-6.0.3.jar:6.0.3]
	at org.springframework.data.neo4j.core.mapping.DefaultNeo4jPersistentEntity.verify(DefaultNeo4jPersistentEntity.java:203) ~[spring-data-neo4j-6.0.3.jar:6.0.3]
	at org.springframework.data.mapping.context.AbstractMappingContext.addPersistentEntity(AbstractMappingContext.java:388) ~[spring-data-commons-2.4.3.jar:2.4.3]
	at org.springframework.data.neo4j.core.mapping.Neo4jMappingContext.addPersistentEntity(Neo4jMappingContext.java:258) ~[spring-data-neo4j-6.0.3.jar:6.0.3]
	at org.springframework.data.mapping.context.AbstractMappingContext.addPersistentEntity(AbstractMappingContext.java:334) ~[spring-data-commons-2.4.3.jar:2.4.3]
	at java.lang.Iterable.forEach(Iterable.java:75) ~[na:1.8.0_252]
	at org.springframework.data.mapping.context.AbstractMappingContext.initialize(AbstractMappingContext.java:463) ~[spring-data-commons-2.4.3.jar:2.4.3]
	at org.springframework.data.mapping.context.AbstractMappingContext.afterPropertiesSet(AbstractMappingContext.java:455) ~[spring-data-commons-2.4.3.jar:2.4.3]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1847) ~[spring-beans-5.3.3.jar:5.3.3]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1784) ~[spring-beans-5.3.3.jar:5.3.3]
	... 17 common frames omitted

For future reference, @star4j, please include this sort information when opening an issue as it helps us to process things more efficiently.

@wilkinsona
Copy link
Member

@wilkinsona wilkinsona commented Feb 1, 2021

The change in behaviour is due to #24239.

@meistermeier Friend is now included in the initial entity set that's passed into Spring Data Neo4j's Neo4jMappingContext. It's then failing when processing the entity. Can you please take a look?

@meistermeier
Copy link
Contributor

@meistermeier meistermeier commented Feb 1, 2021

Will do. 👍

@meistermeier
Copy link
Contributor

@meistermeier meistermeier commented Feb 1, 2021

First of you are correct in tracking this down to the recent change we introduced in Spring Boot.
On the other side this was more or less just an alignment to the other stores and their auto configuration behaviour esp. to keep up with the scenarios if the mapping contexts are getting set explicitly into the strict mode. In those cases a registration of additional entities cannot be done later at runtime.
Having said this, digging a little bit deeper into e.g. SD Mongo's definition of the Document annotation, I can see that the generic persistent type (annotation) gets inherited / declared. The change we introduced will now discover also Document annotated entities because they are also Persistent annotated.
Discovering Persistent annotated entities is something that "every" (TBH I haven't checked all but for example

context.setInitialEntitySet(new EntityScanner(applicationContext).scan(Document.class, Persistent.class));
) other store also does. This means that a SD Mongo setup will also fail if we would inherit / decorate our very own annotations with Persistent.
(This now gets longer than I have expected...but glad you are still here)
My suggestion is that if an entity should not get discovered is either creating a Neo4jMappingContext or MongoMappingContext bean manually and providing the initial entity set (setInitialEntitySet) there.
I would far less be in favour to go the defensive way and revert the changes because the stores are now aligned.
What I would suggest, and can discuss with the Spring Data team, is that we talk about if and how to give users a more configuration-based way to separate the domain classes.

@wilkinsona
Copy link
Member

@wilkinsona wilkinsona commented Feb 1, 2021

Thanks, @meistermeier.

While things are now aligned in terms of the annotations used when scanning, the behaviour when processing the results is not. For example, if I modify the supplied sample and replace Neo4j with Cassandra, the verification of the Friend entity performed in BasicCassandraPersistentEntity succeeds. In other words, Neo4j appears to be more restrictive in terms of the entities that will pass verification so the widening of the entities that are found (#24239) is now causing startup failures.

I think we either need to revert #24239 or Neo4j's verification needs to align with the other stores.

@philwebb philwebb changed the title springboot2.4.2 use spring data mongodb and spring data neo4j startup exception BeanCreationException thrown creating 'neo4jMappingContext' with Spring Boot 2.4.2 when combined with mongodb Feb 1, 2021
@philwebb philwebb added this to the 2.4.x milestone Feb 1, 2021
@meistermeier
Copy link
Contributor

@meistermeier meistermeier commented Feb 2, 2021

Tried it with SD Cassandra and with the mongo/neo4j example provided ^^: Either replacing Neo4j with Cassandra or turning the example the other way around and making friend a Spring Data Neo4j Node (and removing the Document) will not result in an exception at startup but will register Friend also as an eligible entity for SD Cassandra/Mongo. Of course accessing a Cassandra table entity with the Mongo operations will fail during runtime with arbitrary mapping errors.
I would suggest to "fix" the problem that we (you?) remove the Persistent annotation from the initialEntityClasses (we need to stick with the Node and the newly introduced RelationshipProperties). Additionally I had a chat with Mark about the overall usage of Persistent in the other modules and we will discuss a long-term solution that probably have an impact on all store modules configuration.

@wilkinsona
Copy link
Member

@wilkinsona wilkinsona commented Feb 2, 2021

I would suggest to "fix" the problem that we (you?) remove the Persistent annotation from the initialEntityClasses

That makes sense to me. We can take care of that.

Additionally I had a chat with Mark about the overall usage of Persistent in the other modules and we will discuss a long-term solution that probably have an impact on all store modules configuration.

That sounds good too. Boot includes @Persistent when looking for Couchbase and Mongo entities. It's also included when looking for Cassandra entities but via code in CassandraEntityClassScanner which is part of Spring Data.

@mp911de Please let us know how we can track the long-term solution so that we can keep Boot's code in sync.

@wilkinsona wilkinsona changed the title BeanCreationException thrown creating 'neo4jMappingContext' with Spring Boot 2.4.2 when combined with mongodb BeanCreationException thrown creating 'neo4jMappingContext' with Spring Boot 2.4.2 when combined with MongoDB Feb 2, 2021
@mp911de
Copy link
Member

@mp911de mp911de commented Feb 2, 2021

We'll discuss that topic within our team. I'm not sure why @Persistent-annotated entities were picked up in the first place. Once we've clarified that, we can proceed. In any case, scanning for @Persistent entities is rather an obstacle in multi-store arrangements.

@mp911de
Copy link
Member

@mp911de mp911de commented Feb 17, 2021

We discussed that topic with the Spring Data team and concluded that we should remove @Persistent from the annotations that qualify for picking up a class as entity. Instead, we should use store-specific annotations. @Persistent is an SPI to be used by Spring Data store modules. We will update the affected Spring Data modules.

@wilkinsona wilkinsona modified the milestones: 2.4.x, 2.4.3 Feb 17, 2021
@wilkinsona
Copy link
Member

@wilkinsona wilkinsona commented Feb 17, 2021

Thanks, Mark. Is there an issue that we can subscribe to so that we can align Boot with Data when the time comes?

Until that time, we'll use this issue to remove @Persistent from the Neo4j setup, i.e. we're going to partially revert #24239, keeping support for RelationshipProperties.

@mp911de
Copy link
Member

@mp911de mp911de commented Feb 17, 2021

We need to check which modules are affected and file tickets where the scanning needs to be fixed. I'll leave references here once we have created tickets.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
6 participants