Skip to content

Cannot resolve targetEntity in EntityFromDtoInstantiatingConverter. #2505

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

Closed
meistermeier opened this issue Mar 20, 2022 · 11 comments
Closed
Assignees
Labels
status: waiting-for-feedback We need additional information before we can continue type: bug A general bug

Comments

@meistermeier
Copy link
Collaborator

This issue still occurs in 6.2.2. The NPE is now on line 77:

PreferredConstructor> constructor = targetEntity
.getPersistenceConstructor();

Originally posted by @seabamirum in #2395 (comment)

@meistermeier
Copy link
Collaborator Author

This might be something different. But I wonder how the targetEntity should become null if you are requesting a known type. Can you provide a reproducer?

@meistermeier meistermeier self-assigned this Mar 20, 2022
@meistermeier meistermeier added the status: waiting-for-feedback We need additional information before we can continue label Mar 20, 2022
@seabamirum
Copy link

Hello! My class extends org.springframework.security.core.userdetails.User, but the error occurs even with this simple User class below, with a single Boolean (error occurs with either primitive or wrapper type) constructor argument. I stepped it pretty deep into the debugger and found that AbstractMappingContext getPersistentEntity(TypeInformation<?> type) was returning NULL for boolean types.

User u = new User(true);
reactiveNeo4jTemplate.save(User.class).one(u);

encounters the following NPE:

java.lang.NullPointerException: null
	at org.springframework.data.neo4j.core.mapping.EntityFromDtoInstantiatingConverter.convert(EntityFromDtoInstantiatingConverter.java:78) ~[spring-data-neo4j-6.2.2.jar:6.2.2]
	Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
...
Original Stack Trace:
		at org.springframework.data.neo4j.core.mapping.EntityFromDtoInstantiatingConverter.convert(EntityFromDtoInstantiatingConverter.java:78) ~[spring-data-neo4j-6.2.2.jar:6.2.2]
		at org.springframework.data.neo4j.core.mapping.EntityFromDtoInstantiatingConverter.getPropertyValueFor(EntityFromDtoInstantiatingConverter.java:136) ~[spring-data-neo4j-6.2.2.jar:6.2.2]
		at org.springframework.data.neo4j.core.mapping.EntityFromDtoInstantiatingConverter$1.getParameterValue(EntityFromDtoInstantiatingConverter.java:90) ~[spring-data-neo4j-6.2.2.jar:6.2.2]
		at org.springframework.data.mapping.model.ClassGeneratingEntityInstantiator.extractInvocationArguments(ClassGeneratingEntityInstantiator.java:276) ~[spring-data-commons-2.6.2.jar:2.6.2]
		at org.springframework.data.mapping.model.ClassGeneratingEntityInstantiator$EntityInstantiatorAdapter.createInstance(ClassGeneratingEntityInstantiator.java:248) ~[spring-data-commons-2.6.2.jar:2.6.2]
		at org.springframework.data.mapping.model.ClassGeneratingEntityInstantiator.createInstance(ClassGeneratingEntityInstantiator.java:89) ~[spring-data-commons-2.6.2.jar:2.6.2]
		at org.springframework.data.neo4j.core.mapping.EntityFromDtoInstantiatingConverter.convert(EntityFromDtoInstantiatingConverter.java:82) ~[spring-data-neo4j-6.2.2.jar:6.2.2]
		at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195) ~[na:na]
		at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1655) ~[na:na]
		at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484) ~[na:na]
		at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474) ~[na:na]
		at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150) ~[na:na]
		at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173) ~[na:na]
		at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:na]
		at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:497) ~[na:na]
		at org.springframework.data.neo4j.core.mapping.EntityFromDtoInstantiatingConverter.getPropertyValueFor(EntityFromDtoInstantiatingConverter.java:129) ~[spring-data-neo4j-6.2.2.jar:6.2.2]
		at org.springframework.data.neo4j.core.mapping.EntityFromDtoInstantiatingConverter.lambda$convert$0(EntityFromDtoInstantiatingConverter.java:100) ~[spring-data-neo4j-6.2.2.jar:6.2.2]
		at org.springframework.data.mapping.PersistentEntity.lambda$doWithAll$0(PersistentEntity.java:257) ~[spring-data-commons-2.6.2.jar:2.6.2]
		at org.springframework.data.mapping.model.BasicPersistentEntity.doWithAssociations(BasicPersistentEntity.java:387) ~[spring-data-commons-2.6.2.jar:2.6.2]
		at org.springframework.data.mapping.PersistentEntity.doWithAll(PersistentEntity.java:256) ~[spring-data-commons-2.6.2.jar:2.6.2]
		at org.springframework.data.neo4j.core.mapping.EntityFromDtoInstantiatingConverter.convert(EntityFromDtoInstantiatingConverter.java:95) ~[spring-data-neo4j-6.2.2.jar:6.2.2]
		at org.springframework.data.neo4j.core.ReactiveNeo4jTemplate.lambda$doSave$6(ReactiveNeo4jTemplate.java:377) ~[spring-data-neo4j-6.2.2.jar:6.2.2]

My simple User class is below

import org.springframework.data.neo4j.core.schema.GeneratedValue;
import org.springframework.data.neo4j.core.schema.Id;
import org.springframework.data.neo4j.core.schema.Node;

@Node
public class User 
{
	@Id @GeneratedValue private Long userId;
	
	private final Boolean enabled;

	public User(Boolean enabled) 
	{
		super();
		this.enabled = enabled;
	}

	public Boolean getEnabled() {
		return enabled;
	}

	public Long getUserId() {
		return userId;
	}
}```

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Mar 20, 2022
@meistermeier
Copy link
Collaborator Author

I created an example based on your given explanation about the problem (https://github.com/meistermeier/neo4j-issues-examples/tree/master/gh-2505).
This works out of the box with SDN 6.2.2. I even tried various mutations but could not get the exception.
Also I think that requesting getPersistenceEntity(Boolean.class) would already be the problem because this should only happen for real entities not primitives or their box types.
Could you compare where the differences are?

@meistermeier meistermeier added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels Mar 21, 2022
@seabamirum
Copy link

seabamirum commented Mar 21, 2022

I think you're right that the problem is the call itself. My property value is a wrapped Boolean (false) but the target property type is a primitive Class (boolean), so it is entering this code block on line 133 when it shouldn't be.

if (propertyValue != null && !targetPropertyType.isInstance(propertyValue)) {

isInstance() always returns false for primitive type Class objects. Unfortunately I can't reproduce this anymore with my simple User example :( will keep trying.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Mar 21, 2022
@meistermeier meistermeier added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels Mar 21, 2022
@seabamirum
Copy link

seabamirum commented Mar 21, 2022

Ah, I don't encounter the error unless I actually return the Mono in the repo method

@Override
	public Mono<User> testUserSave() 
	{
		User u = new User(true);
		return reactiveNeo4jTemplate.save(User.class).one(u);
	}

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Mar 21, 2022
@seabamirum
Copy link

I also see these warnings in the logs

WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.springframework.util.ReflectionUtils (file:/home/seab/.m2/repository/org/springframework/spring-core/5.3.16/spring-core-5.3.16.jar) to field java.lang.Boolean.value
WARNING: Please consider reporting this to the maintainers of org.springframework.util.ReflectionUtils
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

@seabamirum
Copy link

Okay, I'm running the same test now that you created in https://github.com/meistermeier/neo4j-issues-examples and it fails :(

@seabamirum
Copy link

if I change the enabled field and constructor argument from boolean to Boolean then the test passes.

@seabamirum
Copy link

In your User class (and also the original one I provided, oops) the type is Boolean, so that is why the test passed in that case. I think this should be enough now to track down the issue.

https://github.com/meistermeier/neo4j-issues-examples/blob/88629c9660009073a2d937d162a5639533d10307/gh-2505/src/main/java/com/example/gh2505/User.java#L8

@meistermeier
Copy link
Collaborator Author

Thanks for the investigation. And you are 100% right. The EntityFromDtoInstantiatingConverter assumes every property type to be an entity. Which does not make any real problems if a matching constructor exists. When it come to primitive types this gets problematic ;)

@meistermeier meistermeier added the type: bug A general bug label Mar 21, 2022
@meistermeier meistermeier added this to the 6.2.4 (2021.1.4) milestone Mar 21, 2022
meistermeier added a commit that referenced this issue Mar 23, 2022
@meistermeier
Copy link
Collaborator Author

You could check out the 6.2.4-GH-2505-SNAPSHOT (available in ~30 minutes)
Side note: Using the fluent API is meant for projections, we do not mention this explicitly enough in the API and I will add this as part of this bug fix. If you just want to store a domain object, you should use template.save(object) and not template.save(Object's class).one(object).

@meistermeier meistermeier added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels Mar 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: waiting-for-feedback We need additional information before we can continue type: bug A general bug
Projects
None yet
Development

No branches or pull requests

3 participants