Reputation: 7212
EDIT: Sample project available on github.
I'm using Neo4J (Rest graph database, hosted in grapheneDb) and Spring Data in our backend project.
<bean id="graphDatabaseService" class="org.springframework.data.neo4j.rest.SpringCypherRestGraphDatabase">
I have a simple one-to-many relationship between two entities: User
and Stay
.
EDIT: I thought this wasn't relevant for the issue, but after seeing a similar problem in SDN4, I think I need to update the question (there is a basic @NodeEntity
class, and both entities are extending this base class).
@NodeEntity
public abstract class BasicNodeEntity implements Serializable {
@GraphId
private Long nodeId;
}
public class User extends BasicNodeEntity {
@RelatedTo(type = "HAS_STAY", direction = Direction.OUTGOING)
Set<Stay> stays;
public void addStay(Stay stay) {
stays.add(stay);
}
}
public class Stay extends BasicNodeEntity {
@RelatedTo(type = "HAS_STAY", direction = Direction.INCOMING)
User user;
}
I'm unable to persist more than one stay. The first stay I add to the user is persisted correctly, but just the first one. The next stays added never persists, and I always retrieve the first one.
The method I use to create a new stay is:
@Autowired
Neo4jOperations template;
@Transactional
private void createStay(Stay stay, User user) throws Exception {
stay = template.save(stay);
user.addStay(stay);
template.save(user);
// If i evaluate user at this point, it contains both stays
// But if I retrieve the user from the repository, it just contains
// the first stay, the second one has not persisted.
}
EDIT: User modified is retrieved correctly through UserRepository
.
public interface UserRepositoryCustom {}
public interface UserRepository extends GraphRepository<User>, UserRepositoryCustom {
User findById(String id);
}
User user = userRepository.findById(userId);
NOTE: I also tried to save through the repository interface instead of the Neo4jTemplate one, but I have the same problem.
Both entities are correctly saved in the neo4j database, it's just a persistence issue.
I think this should be quite easy, so I'm probably missing something..
Any help would be greatly appreciated.
Relevant versions:
<spring.version>4.0.5.RELEASE</spring.version>
<spring-data-neo4j.version>3.3.2.RELEASE</spring-data-neo4j.version>
There is another SO question with a very similar problem, but without response so far.
Upvotes: 4
Views: 1625
Reputation: 41676
It is a tricky thing.
Your custom equals method causes two entities which have their node-id set but not yet their uuid-id set, to be equal so that when loading them into a set the set will only contain one.
protected Set<Object> createEntitySetFromRelationshipEndNodes(Object entity, final MappingPolicy mappingPolicy, final Class<?> relatedType) {
final Iterable<Node> nodes = getStatesFromEntity(entity);
final Set<Object> result = new HashSet<Object>();
for (final Node otherNode : nodes) {
Object target = template.createEntityFromState(otherNode, relatedType, mappingPolicy);
result.add(target);
}
return result;
}
If you change your code to have an equals/hashcode in your BasicNode entity:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof BasicNodeEntity)) return false;
BasicNodeEntity that = (BasicNodeEntity) o;
if (nodeId != null) {
if (!nodeId.equals(that.nodeId)) return false;
} else {
if (that.nodeId != null) return false;
}
return true;
}
@Override
public int hashCode() {
return nodeId != null ? nodeId.hashCode() : 0;
}
so that entities that have only a nodeId set are comparable
and adapt the subclass methods
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof IdentifiableEntity)) return false;
IdentifiableEntity entity = (IdentifiableEntity) o;
//change
if (!super.equals(o)) return false;
if (id != null) {
if (!id.equals(entity.id)) return false;
} else {
if (entity.id != null) return false;
}
return true;
}
@Override
public int hashCode() {
//change
if (super.hashCode() != 0) return super.hashCode();
return id != null ? id.hashCode() : 0;
}
Then it works.
Going forward if you are working with Neo4j Server I recommend to you to check out SDN 4 RC2 instead which was released on Friday.
Upvotes: 2