0

I use Spring Data Neo4j 4 GraphRepository to save and retrieve data. Using GraphRepository save() and findAll() methods.

When I update an existing entity property to null, it seems that changes are not reflected in the returned data.

If I update the property to any other non-null value, the changes are reflected correctly.

I can see that the null property update is performed on the DB server. But the findAll() method doesn't reflect the change and keeps the old value.

Is this a known bug? Any workaround? Or is it some kind of caching problem?

UPDATE

After trying to understand what happens, I found that this problem will occur when you have two different Java objects for the same entity. The null property will never be updated (but other properties with non-null values will).

Example code:

@Autowired
MovieRepository repository;

public void test() {
    repository.deleteAll();

    Movie movie1 = new Movie();
    movie1.setName("Pulp Fiction");
    movie1.setDirector("Quentin Tarantino");
    movie1 = repository.save(movie1);

    System.out.println("Movie1: " + movie1);

    Movie movie2 = new Movie();
    movie2.setId(movie1.getId());
    movie2.setName(movie1.getName());
    movie2.setDirector(null); // implicit...
    movie2 = repository.save(movie2);

    System.out.println("Movie2: " + movie2);

    Movie movie3 = repository.findOne(movie1.getId());
    System.out.println("Movie3: " + movie3);
}

Real life case: when using SDN with a Spring MVC form, it looks like entities are created from Model attributes. When a value is set to null in a form, the update is performed correctly in Neo4j, but the values are not returned correctly when using any find...() methods. Therefore it leads to stale data.

Side note: this problem happens when the Neo4J session scope is per "session" and doesn't happen when the session scope is per "request".

@Bean
@Override
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public Session getSession() throws Exception {
    return super.getSession();
}
Martin L.
  • 135
  • 1
  • 7

2 Answers2

1

If you are using HttpSession-scoped persistence in SDN, you should ensure the the objects bound to your Controller via @ModelAttribute have the same scope as the persistence layer. Use the @SessionAttribute annotation on the Controller to achieve this.

If you use HttpRequest-scoped objects in your Controller and HttpSession-scoped persistence, you will get different objects representing the same graph entity at the web layer, and this will confuse the persistence mechanism.

Vince
  • 2,181
  • 13
  • 16
0

Should not be a problem at all. I just tried

@Test
 public void shouldPersistNulls() {
        TempMovie movie = new TempMovie( "Pulp Fiction" );

        tempMovieRepository.save( movie );

        assertSameGraph( getDatabase(), "CREATE (m:Movie {name:'Pulp Fiction'})");

        TempMovie loadedMovie = tempMovieRepository.findAll().iterator().next();
        loadedMovie.setName(null);
        tempMovieRepository.save(loadedMovie);

        assertSameGraph( getDatabase(), "CREATE (m:Movie)");

        TempMovie loadedAgainMovie = tempMovieRepository.findAll().iterator().next();
        assertNull(loadedAgainMovie.getName());
    }

and it passed.

Update based on edited question

The property representing the @GraphId must never be set manually i.e. via your code. You should load the entity by id when you require to update it. This ensures that the entity is known to the mapping context of the OGM and is managed correctly.

Luanne
  • 19,145
  • 1
  • 39
  • 51
  • I added a code example for you to see in which case the problem occurs. Let me know if you have any idea how to deal with this. Thanks. – Martin L. Dec 15 '15 at 22:18
  • Ok I see. So is there any "clean" way to make Spring MVC `@ModelAttribute` work with SDN in that case? New object instances are created and then populated when using the `@ModelAttribute` annotation, so it will always lead to this problem... – Martin L. Dec 16 '15 at 17:09