ClassCastException using Multi level Projection

@Node
public class User {
    @Id
    private String id;
    private String username;
    @Relationship("ACTIVE_BUSINESS_UNIT")
    private BusinessUnit activeBusinessUnit;
}

@Node
public class BusinessUnit {

    @Id
    private String id;
    private String name;
    @Relationship(type = "BUSINESS_UNIT_IN", direction = OUTGOING)
    private List<User> users = new ArrayList<>();
}

@Data
public class UserView {
    private String id;
    private String username;
    private BusinessUnitView activeBusinessUnit;

    @data
    public static class BusinessUnitView {
        private String id;
        private String name;
    }
}

UserView user = userRepository.findByUsername(myusername);

java.lang.ClassCastException: Cannot cast au.com.objects.neo4j.entity.BusinessUnit to au.com.objects.neo4j.entity.UserView$BusinessUnitView

Trying to use multilevel projections to control how much data gets loaded and am getting ClassCastException when I load from repository.

Any ideas what I'm doing wrong?

Appears multi level projections don't work for class based projections

"These DTO types can be used in exactly the same way projection interfaces are used, except that no proxying happens and no nested projections can be applied."

https://docs.spring.io/spring-data/neo4j/docs/current/reference/html/#projections.dtos

As only class based projections support doing updates, what is the preferred way to load a partial graph for update?

For example if I wanted to load a User and update it's activeBusinessUnit

UserView user = userRepository.findByUsername(myusername);
BusinessUnitView bu = businessUnitRepository.findById(myid);
user.setACtiveBusinessUnit(bu);
neo4jTemplate.save(User.class).one(user);
  • Interface based projections don't support updating
  • Class based projection don't allow multilevel projections
  • Don't want to load all the users attached to a BusinessUnit whenever I load a User (with its activeBusinessUnit)

I am so sorry that I missed this question. This limitation is unfortunate but technical needed.
Basically what I would do is to use the interface projection to create an instance of the entity object from.
e.g something like:

class User {
  User fromUserView(UserView userView) {
    return new User(userView.getUsername(),...);
  }
}

The version annotation is needed regardless if you are using projection or not.

Otherwise, SDN does not know if the entity is new or already exists. It does not have any control over the identifier in your case and needs another information (the version) to determine if this was already processed or not.

I see a missing documentation piece around the identifiers and will take care of this.

@gerrit_meier thanks for the reply, thats a great help.

So that seems to work, just needed to add @Version, is that expected?

This is what I ended up with. Now to try and apply similar to the rest of the migration too SDN6

IUserView userView = userRepository.findByUsername(username);
IBusinessUnitView bu = businessUnitRepository.findByName(name);
User user = new User(userView.getId(), userView.getVersion(), 
   userView.getUsername(),
   new BusinessUnit(businessUnitView.getId(), businessUnitView.getVersion(), 
      businessUnitView.getName(), 
      null)); // users

neo4jTemplate.saveAs(user, IUserView.class);