spring-projects / spring-data-neo4j

Provide support to increase developer productivity in Java when using Neo4j. Uses familiar Spring concepts such as a template classes for core API usage and lightweight repository style data access.
http://spring.io/projects/spring-data-neo4j
Apache License 2.0
825 stars 619 forks source link

Saving two entities with a relationship and UUID as id results in exception [DATAGRAPH-1156] #1719

Closed spring-projects-issues closed 4 years ago

spring-projects-issues commented 5 years ago

Leroy Truong opened DATAGRAPH-1156 and commented

Assume we have two simple entities

@NodeEntity
public class User {

    @Id
    @Property("id")
    @Convert(value = UUIDStringConverter.class)
    private UUID id = UUID.randomUUID();

    @Property("email")
    private String email;

    // constructor, getters and setters
}

@NodeEntity
public class Hobby {

    @Id
    @Property("id")
    @Convert(value = UUIDStringConverter.class)
    private UUID id = UUID.randomUUID();

    @Property("name")
    private String name;

    @Relationship(value = "HAS", direction = Relationship.INCOMING)
    private User hobbyist;

    // constructor, getters and setters
}

with corresponding repositories

public interface UserRepository extends Neo4jRepository<User, UUID> {
    Optional<User> findByEmail(String email);
}

public interface HobbyRepository extends Neo4jRepository<Hobby, UUID> {

}

Now we expose two REST endpoints

@PostMapping(path = "/users", consumes = "application/json")
public User create(@RequestBody User user) {
    return userRepository.save(user);
}

@PostMapping(path = "/users/{userId}/hobbies", consumes = "application/json")
public ResponseEntity<Hobby> create(@PathVariable UUID userId, @RequestBody Hobby hobby) {
    Optional<User> userOptional = userRepository.findById(userId);
    if (!userOptional.isPresent()) return ResponseEntity.notFound().build();
    User user = userOptional.get();

    hobby.setHobbyist(user);
    Hobby saved = hobbyRepository.save(hobby);
    return ResponseEntity.ok(saved);
}

The first endpoint will simply save a new user entity. That works fine.

As for the second endpoint, we retrieve an existing user by id and attach it to a new hobby. Upon saving, it throws the following exception:

[Request processing failed; nested exception is org.neo4j.ogm.exception.CypherException: Error executing Cypher; Code: Neo.ClientError.Statement.SemanticError; Description: Cannot merge node using null property value for id

I included the full stacktrace (reproducable from report project) in the attachment. I also included another one which originates from the original Kotlin/Spring Data Neo4j project I was working on.

The report project includes tests to verify the scenario. Surprisingly the repository integration test without the web layer succeeds whereas the web MVC test fails. In theory they should execute the same behavior


Affects: 5.0.12 (Kay SR12), 5.1.3 (Lovelace SR3)

Reference URL: https://github.com/FrozenGenis/neo4j-sdn-ogm-issue-report-template

Attachments:

Backported to: 5.2.10 (Moore SR10), 5.1.20 (Lovelace SR20)

spring-projects-issues commented 5 years ago

Leroy Truong commented

This issue seems to be fixed with the latest version of Spring Boot 2.1.1

spring-projects-issues commented 4 years ago

Michael Simons commented

Verified to work with Lovelace, Moore and Neumann