Skip to content

Cyclic relationship not reconstituted from DB #2622

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
languitar opened this issue Oct 31, 2022 · 4 comments
Closed

Cyclic relationship not reconstituted from DB #2622

languitar opened this issue Oct 31, 2022 · 4 comments
Assignees
Labels
status: feedback-provided Feedback has been provided

Comments

@languitar
Copy link

languitar commented Oct 31, 2022

I have a model where cycles in the nodes are possible. However, I could not find any way to reconstitute these cycle from the database. Persisting them works as expected.

Here is an example project that demonstrates the problem: https://github.com/languitar/sdn-cycles-repro. A startup event listener executes the reproduction code. This code creates a single node with a relationship pointing to itself. On loading, this cycle is filled with a null value, as can be seen in the output of the reproduction code:

Optional[TestNode{uuid=41ff1326-857c-4d5e-8c05-51626074d14f, cycles=[null]}]

Is there any way to reconstitute such a situation from the database? The documentation is a bit scarce on this topic.

For the reference, this is the node class used:

@Node("TestNode")
public class TestNode {

    @Id
    @GeneratedValue
    private UUID uuid;

    @Relationship("CYCLES_TO")
    private List<TestNode> cycles;

    public TestNode(final UUID uuid, final List<TestNode> cycles) {
        this.uuid = uuid;
        this.cycles = cycles;
    }

    public UUID getUuid() {
        return uuid;
    }

    public void setUuid(UUID uuid) {
        this.uuid = uuid;
    }

    public List<TestNode> getCycles() {
        return cycles;
    }

    public void setCycles(List<TestNode> cycles) {
        this.cycles = cycles;
    }

    @Override
    public String toString() {
        return "TestNode{" +
                "uuid=" + uuid +
                ", cycles=" + cycles +
                '}';
    }
}
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Oct 31, 2022
@meistermeier meistermeier self-assigned this Nov 1, 2022
@meistermeier
Copy link
Collaborator

Sorry for the delayed answer on this. Somehow it slipped through my list.
In this case, you cannot use an all-args constructor (or at least the relationship definition as a parameter). It will end up in a egg-chicken situation, where it wants to construct the object itself but then defers to the creation of the (very same) object first, which defers to the creation... and so on.
Same is true if you have not the same object but something like A(b) and B(a) defined in the constructors. It is not solvable for SDN. You have to manually break the cycle by removing it from the constructor and either providing a wither method (https://docs.spring.io/spring-data/neo4j/docs/current/reference/html/#mapping.fundamentals.property-population) and make it non-final (in your example this is already done).

@meistermeier meistermeier added status: waiting-for-feedback We need additional information before we can continue and removed status: waiting-for-triage An issue we've not yet triaged labels Jan 20, 2023
@languitar
Copy link
Author

Thanks for the explanations. Any chance to improve the documentation for this situation? I had experimented with wither methods but didn't think about removing the constructor argument.

@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 Jan 21, 2023
@meistermeier
Copy link
Collaborator

Will keep this issue open for the documentation.
Currently investigating why a certain exception on load that would tell you about the cyclic dependency, does not get thrown. We introduced the exception here because we don't want people to have an incomplete object tree in the first place when we know that there would be more.

@meistermeier
Copy link
Collaborator

meistermeier added a commit that referenced this issue Jan 23, 2023
This slipped through the check before:
A has dependency on A defined within the constructor.
Because of a "same" entity check, we skipped the `inCreation` check
and did not throw the MappingException.

Closes #2622
meistermeier added a commit that referenced this issue Jan 23, 2023
This slipped through the check before:
A has dependency on A defined within the constructor.
Because of a "same" entity check, we skipped the `inCreation` check
and did not throw the MappingException.

Closes #2622
meistermeier added a commit that referenced this issue Jan 23, 2023
This slipped through the check before:
A has dependency on A defined within the constructor.
Because of a "same" entity check, we skipped the `inCreation` check
and did not throw the MappingException.

Closes #2622
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: feedback-provided Feedback has been provided
Projects
None yet
Development

No branches or pull requests

3 participants