Skip to content

Relationship statemachine uses internal ids all the time, not being able to keep track of processed values. #2500

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
paolodedo opened this issue Mar 16, 2022 · 14 comments
Assignees
Labels
type: bug A general bug

Comments

@paolodedo
Copy link

paolodedo commented Mar 16, 2022

Regarding issue

#2282

that was about the need of adding relationship-objects to both the Sets of objects in order to save a relationship,

🔵with the dependencies:
spring-boot-starter-data-neo4j:2.6.4
spring-data-neo4j:6.2.2

🔵with the models:

public class Device {

    @Id
    private Long id;

    private String name;

    @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
    @JsonIdentityReference(alwaysAsId = true)
    @Relationship(type = "BELONGS_TO", direction = Relationship.Direction.OUTGOING)
    private Set<Group> groups = new LinkedHashSet<>();
}

public class Group {

    @Id
    @GeneratedValue(generatorClass = UUIDStringGenerator.class)
    private String id;

    private String name;

    @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
    @JsonIdentityReference(alwaysAsId = true)
    @Relationship(type = "BELONGS_TO", direction = Relationship.Direction.INCOMING)
    private Set<Device> devices = new LinkedHashSet<>();
}

🔵with the controller for Group (empty because rest services are auto-generated):

    @BasePathAwareController
    public class GroupController {}

the following POST request on the GroupController ReST endpoint returns a 200 OK (I expect the relationship with devices has been created):

{
    "name": "test",
    "description" : "",
    "devices": ["/1"]
}

but no relationship is created (not treated as a relationship for SDN as you'd have to tell your body that even the Device entity is linking to this very Group object); unfortunately, due to the double-side relationship saving mechanism, Spring Data Rest automatically generated ReST services (HATEOAS) have the relationship creation feature broken:

Do you have a solution for that?

Thanks!

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Mar 16, 2022
@michael-simons
Copy link
Collaborator

Sorry, but I can't investigate this issue with just a snippet of JSON code. The other issue had been solved.
So if you want us to work on it, please provide a full minimal reproducer.

@michael-simons michael-simons added blocked: awaiting feedback and removed status: waiting-for-triage An issue we've not yet triaged labels Mar 17, 2022
@paolodedo
Copy link
Author

Ok I edited the initial post.

@michael-simons
Copy link
Collaborator

Can you possibly try out the latest snapshot?

Actually, there has been work in this area and I'm pretty sure this will affect your usecase:

ff206fc

@paolodedo
Copy link
Author

paolodedo commented Mar 17, 2022

I've tried compiling main branch and referencing it from my project.

The error is 400 Bad request with response:

{
    "cause": {
        "cause": {
            "cause": null,
            "message": "class org.neo4j.driver.internal.InternalRecord cannot be cast to class org.neo4j.driver.types.MapAccessor (org.neo4j.driver.internal.InternalRecord and org.neo4j.driver.types.MapAccessor are in unnamed module of loader 'app')"
        },
        "message": "class org.neo4j.driver.internal.InternalRecord cannot be cast to class org.neo4j.driver.types.MapAccessor (org.neo4j.driver.internal.InternalRecord and org.neo4j.driver.types.MapAccessor are in unnamed module of loader 'app') (through reference chain: it.paolodedo.service.domain.Group[\"devices\"]->java.util.LinkedHashSet[0])"
    },
    "message": "JSON parse error: class org.neo4j.driver.internal.InternalRecord cannot be cast to class org.neo4j.driver.types.MapAccessor (org.neo4j.driver.internal.InternalRecord and org.neo4j.driver.types.MapAccessor are in unnamed module of loader 'app'); nested exception is com.fasterxml.jackson.databind.JsonMappingException: class org.neo4j.driver.internal.InternalRecord cannot be cast to class org.neo4j.driver.types.MapAccessor (org.neo4j.driver.internal.InternalRecord and org.neo4j.driver.types.MapAccessor are in unnamed module of loader 'app') (through reference chain: it.paolodedo.service.domain.Group[\"devices\"]->java.util.LinkedHashSet[0])"
}

and error log:

2022-03-17 12:05:16.570 WARN 24428 --- [nio-8082-exec-5] .n.c.Neo4jPersistenceExceptionTranslator : Don't know how to translate exception of type class java.lang.ClassCastException

if I try to add the relationship objects.

@michael-simons
Copy link
Collaborator

michael-simons commented Mar 17, 2022

Please create a reproducer and share it (Something that is executable as is).

@paolodedo
Copy link
Author

Here's ready to be attached to a neo4j instance and tried out, https://github.com/paolodedo/service

@michael-simons
Copy link
Collaborator

Hey. So the class cast issue is due to a dependency problem with Spring Boot. They didn't upgrade the Neo4j Java driver. Sorry for that issue.

If you set

<neo4j-java-driver.version>4.4.4</neo4j-java-driver.version>

with the snapshots, it works.

Your original problem still persist respectively I am investigating.

@michael-simons michael-simons self-assigned this Mar 18, 2022
@paolodedo
Copy link
Author

Ok, thank you very much.
I think this is fundamental using spring data rest, or we'll have to rewrite all the rest endpoints.

Thank and see you soon.

@michael-simons michael-simons added type: bug A general bug and removed type: enhancement A general enhancement labels Mar 18, 2022
@michael-simons michael-simons changed the title Spring Data Rest relationship creation feature (HATEOAS) broken Relationship statemachine uses internal ids all the time, not being able to keep track of processed values. Mar 18, 2022
michael-simons added a commit that referenced this issue Mar 18, 2022
…ionship statemachine.

When checking if a relationship has already been processed, the actual fromId is always the one returned by the persistent entity: This is always an internal generated id when no id generators are present, but when a user runs their own, it’s not. Therefor, when the internal id is always used in the statemachine, a processed relationship would have not been seen and known and deleted in a 2nd go.

This fixes #2500.
michael-simons added a commit that referenced this issue Mar 18, 2022
…ionship statemachine.

When checking if a relationship has already been processed, the actual fromId is always the one returned by the persistent entity: This is always an internal generated id when no id generators are present, but when a user runs their own, it’s not. Therefor, when the internal id is always used in the statemachine, a processed relationship would have not been seen and known and deleted in a 2nd go.

This fixes #2500.
@michael-simons
Copy link
Collaborator

Thanks, @paolodedo for the reproducer and the effort put into nudging us :)

This is fixed and will be out on monday. In the meantime, I have sent you a PR.

michael-simons added a commit that referenced this issue Mar 18, 2022
…ionship statemachine.

When checking if a relationship has already been processed, the actual fromId is always the one returned by the persistent entity: This is always an internal generated id when no id generators are present, but when a user runs their own, it’s not. Therefor, when the internal id is always used in the statemachine, a processed relationship would have not been seen and known and deleted in a 2nd go.

This fixes #2500.
@paolodedo
Copy link
Author

Thank you very much for your time and effort :)

Your PR is fixing POST and PATCH scenarios: relationship are created without the need of setting them bidirectionally.
For PATCH requests, as expected, if I do not pass my relationship IDs array then the relationships are left as they were; if I pass my array they are overridden.

But, for PUT requests scenario, relationships are not changed.
Every relationship IDs array is ignored, I don't know why.
Do you?

@michael-simons
Copy link
Collaborator

No, I don't know…
the PUT in Spring Data Rest ignores the associations. Why it does it that why is behind my knowledge. Our persister only sees the old object without data, so it deletes the existing ones. Feel free to raise it as an issue with Spring Data Rest.

For reference, here the PATCH

image

compare it with the PUT

image

same data sent

## Request Duplicate (2)
curl -X "PUT" "http://localhost:8082/service/groups/80ff67e2-63d6-492b-a3a4-49546507d6bf" \
     -H 'Content-Type: application/json; charset=utf-8' \
     -d $'{
  "name": "from put2",
  "devices": [
    "/1"
  ]
}'

@michael-simons
Copy link
Collaborator

A put on the sub resource does work as expected

## Request Duplicate (2)
curl -X "PUT" "http://localhost:8082/service/groups/80ff67e2-63d6-492b-a3a4-49546507d6bf/devices" \
     -H 'Content-Type: text/uri-list' \
     -d "/1"

don't know much more about Spring Data Rest… Maybe @odrotbohm knows more what's going on.

@paolodedo
Copy link
Author

Thanks for being so accurate. For the moment, I'll use PATCH, and try to indagate the Spring Data Rest PUT issue.

@michael-simons
Copy link
Collaborator

You're welcome, @paolodedo

I read through the linked issue in Spring Data Rest: I think that Spring Data Rest should load the entity in question first and than update the fields through the PUT request, so that the associations are populated.
If they are empty, Spring Data Neo4j will always wipe the existing ones.

That would be in line with what Oliver writes here spring-projects/spring-data-rest#1364 (comment)

@michael-simons michael-simons mentioned this issue Jan 4, 2023
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug A general bug
Projects
None yet
Development

No branches or pull requests

3 participants