0
votes

I've built a project that uses spring-data and succeeded to create entities, add relationships with properties. All seems to run well except for updating relationships properties values, after they were once persisted.

To try to explore it, I merged the simple "worlds" example from SpringData documentation and enhanced it a bit. In this example, there is a a World entity that is related to other worlds. I added a property called "yearsToReach" to this relationship.

In my tests I create two worlds: Mars and Earth and set the yearsToReach to 10. Then, I call the WorldRepositoryService.updateYearsToOtherWorld and set the yearsToReach to 20. This method ends with a 'save' of the first World, but when quering for the World again, it doesn't seems to actually change the value in the database.

Maybe it's really simple, but I've just spent hours trying to figure out what am I doing wrong...

Here is my code, I removed some of the non-relevant parts. I really appreciate any response / code example.

This is my 'World' entity (Note: 'BaseWorld' class is not marked as @NodeEntity and it conatins only 'name' property).


    @NodeEntity
    @Getter
    @Setter
    public class World extends BaseWorld {

        @GraphId
        private Long id;

        @RelatedToVia(type = OtherWorldRelationship.RELATIONSHIP_TYPE, direction = Direction.OUTGOING, elementClass = OtherWorldRelationship.class)
        private Set reachableByRocket = new HashSet();


        public void addRocketRouteTo(World otherWorld, int yearsToReach) {
            OtherWorldRelationship otherWorldRelationship = new OtherWorldRelationship();
            otherWorldRelationship.setWorld(this);
            otherWorldRelationship.setOtherWorld(otherWorld);
            otherWorldRelationship.setYearsToReach(yearsToReach);
            reachableByRocket.add(otherWorldRelationship);
        }

        public void updateYearsToOtherWorld(World otherWorld, int years) {
            Iterator otherWorlds = reachableByRocket.iterator();
            while (otherWorlds.hasNext() )
            {
                OtherWorldRelationship otherWorldRelationship = otherWorlds.next();
                if (otherWorld.getName().equals(otherWorldRelationship.getOtherWorld().getName())){
                    otherWorldRelationship.setYearsToReach(years);
                    break;
                }
            }
        }

This is the relationship class:


    @RelationshipEntity
    @Getter
    @Setter
    public class OtherWorldRelationship {
        public static final String RELATIONSHIP_TYPE = "OtherWorld";

        @GraphId
        Long id;

        @StartNode
        private World world;

        @EndNode
        private World otherWorld;

        private int yearsToReach;

    }

This is the WorldRepository interface - it only inherits from GraphRepository


    public interface WorldRepository extends GraphRepository, NamedIndexRepository {
    }

And this is the WorldRepositoryService:


    @Repository
    public class WorldRepositoryService implements IWorldRepositoryService {

        @Getter
        @Setter
        @Autowired
        private WorldRepository worldRepository;

        @Override
        @Transactional
        public Collection createSomeWorlds() {
            ArrayList newWorlds = new ArrayList();
            World earth = createWorld("Earth");
            newWorlds.add(earth);

            World mars = createWorld("Mars");
            mars.addRocketRouteTo(earth, 10);
            newWorlds.add(mars);

            return newWorlds;
        }

        @Override
        @Transactional
        public World createWorld(String name) {
            return worldRepository.save(new World(name));
        }

        @Override
        public World findWorldNamed(String name) {
            return worldRepository.findByPropertyValue("name", name);
        }


        @Override
        @Transactional
        public World updateYearsToOtherWorld(World mars, World earth, int years) {
            mars.updateYearsToOtherWorld(earth, years);
            return worldRepository.save(mars);
        }

    }

At last, these are lines from my test:


    @Test
        public void testNeo4JRelationshipUpdateData() {

            Iterable worlds = worldRepositoryService.createSomeWorlds();//Mars and Earth

            World earth = worldRepositoryService.findWorldNamed("Earth");
            World mars =  worldRepositoryService.findWorldNamed("Mars");

            Integer distanceInYears = mars.getYearsToOtherWorld(earth);
            System.out.println("Distance beteween Mars and " + distanceInYears);
            Assert.assertEquals(10, distanceInYears.intValue());

            mars = worldRepositoryService.updateYearsToOtherWorld(mars, earth, 20);

            System.out.println("Distance beteween Mars and Earth: " + distanceInYears);
            Assert.assertEquals(20, distanceInYears.intValue());

            mars = worldRepositoryService.findWorldNamed("Mars");
            distanceInYears = mars.getYearsToOtherWorld(earth);
            System.out.println("Distance beteween Mars and Earth after update: " + distanceInYears);

            // !!! This line fails - it gets 10 instead of 20 !!! //
            Assert.assertEquals(20, distanceInYears.intValue());

        }

3
A few thoughts: 1) print something when you're looping through the relationships set and find the one you're looking for to make sure the property gets set, 2) save the relationship rather than the 'mars' node, 3) try and see if you can edit your question and re-indent the code so the syntax highlighter works–it makes it easier to read :)jjaderberg
Thank you very much! I didn't know that it is possible to save the relationship independently (as I said - I'm new to Neo4J) - but once tried it - it works!!! Thanks again :)Carmel Baumel-Ezra

3 Answers

4
votes

Thanks to jjaderberg for his help - I'm pasting the solution for the sake of others:

The solution was to save the relation itself and not the entity that holds it. This is the fixed code:

I defined new repository interface for the relationship:


    public interface OtherWorldRelationshipRepository extends GraphRepository, NamedIndexRepository {

    }

In WorldRepositoryService.clas I saved the relationship instead of the entity:


    @Override
        @Transactional
        public OtherWorldRelationship updateYearsToOtherWorld(World mars, World earth, int years) {
            OtherWorldRelationship yearsToOtherWorldRelation = mars.updateYearsToOtherWorld(earth, years);
            return otherWorldRelationshipRepository.save(yearsToOtherWorldRelation);
        }

That's it!! Hope it helps someone as it helped myself.

Carmel

0
votes

Are you sure that it is the last assert that fails? It seems that the second should fail - you do not read the distanceInYears variable after update.

0
votes

You are totally right - I forgot to re-read the "distanceInYears". Unfortunately, this doesn't solves the problem... The value is still 10. Both assertion fails (if I comment the first one, the other one in which I'm trying to retrieve "Mars" again - also fails).

The correct test code looks like this:


    @Test
        public void testNeo4JRelationshipUpdateData() {

            worldRepositoryService.deleteAll();

            Iterable worlds = worldRepositoryService.createSomeWorlds();//Mars and Earth

            World earth = worldRepositoryService.findWorldNamed("Earth");
            World mars = worldRepositoryService.findWorldNamed("Mars");

            Integer distanceInYears = mars.getYearsToOtherWorld(earth);
            System.out.println("Distance beteween Mars and " + distanceInYears);
            Assert.assertEquals(10, distanceInYears.intValue());

            mars = worldRepositoryService.updateYearsToOtherWorld(mars, earth, 20);
            distanceInYears = mars.getYearsToOtherWorld(earth);

            /* This is the assertion that fails: */
            System.out.println("Distance beteween Mars and Earth: " + distanceInYears);
            Assert.assertEquals(20, distanceInYears.intValue());

            /* This one also fails if we comment the two previous lines: */
            mars = worldRepositoryService.findWorldNamed("Mars");
            distanceInYears = mars.getYearsToOtherWorld(earth);
            System.out.println("Distance beteween Mars and Earth after update: " + distanceInYears);
            Assert.assertEquals(20, distanceInYears.intValue());

        }