I'm experiencing some strange behavior when I'm looking up node entities with Spring Data Neo4j (SDN). If I use GraphRepository.findOne(long) it will return an entity with that identifier even though the entity is not of the same type.
This is what my (very) simplified entity structure looks like:
@NodeEntity
protected abstract class BaseEntity {
@GraphId
private Long id;
@JsonIgnore
@RelatedTo(type = RelationType.ENTITY_AUDIT)
private Audit audit;
}
@NodeEntity
public final class Person extends BaseEntity {
@Indexed(indexType = IndexType.FULLTEXT)
private String firstName;
@Indexed(indexType = IndexType.FULLTEXT)
private String lastName;
}
@NodeEntity
public class Audit extends BaseEntity {
@RelatedTo(type = RelationType.ENTITY_AUDIT, direction = Direction.INCOMING)
private BaseEntity parent;
private Long date;
private String user;
}
For every entity type, I've created repositories like this:
@Repository
public interface PersonRepository extends GraphRepository<Person> {}
@Repository
public interface AuditRepository extends GraphRepository<Audit> {}
I've got an abstract base class for my service layer classes. That is what they roughly look like:
public abstract class MyServiceImpl<T extends BaseEntity> implements MyService<T> {
private GraphRepository<T> repository;
public MyServiceImpl(final GraphRepository<T> repository) {
this.repository = repository;
}
@Override
public T read(final Long identifier) throws EntityNotFoundException {
return repository.findOne(identifier);
}
@Override
public T create(final T entity) {
return repository.save(entity);
}
}
@Service
public class PersonServiceImpl extends MyServiceImpl<Person> implements PersonService {
private PersonRepository personRepository;
@Autowired
public PersonServiceImpl(final PersonRepository personRepository) {
super(personRepository);
this.personRepository = personRepository;
}
}
When I execute the following code, the result is not as expected:
Person person = new Person();
person.setFirstName("Test");
person.setLastName("Person");
personService.create(person);
// suppose the person identifier is 1L
final Audit audit = auditRepository.findOne(1L);
You'd expect that the AuditRepository would return null, but this in not the case. Instead, it returns an Audit with identifier 1L and null in all of its properties. It seems that as long as there's a node that corresponds to a given identifier, it will be returned, no mather what its type is. If Person and Audit would have had matching property names, they would contain their values too... Is all this expected behavior, or am I missing something?
For now, I've solved this problem with the code below, where I do the type check myself.
public abstract class MyServiceImpl<T extends BaseEntity> implements MyService<T> {
private GraphRepository<T> repository;
public MyServiceImpl(final GraphRepository<T> repository) {
this.repository = repository;
}
@Override
public T read(final Long identifier) throws EntityNotFoundException {
return get(identifier);
}
protected T get(final Long identifier) throws EntityNotFoundException {
final T entity = repository.findOne(identifier);
final Class<T> type = getServiceType();
if (entity == null || !(type.equals(repository.getStoredJavaType(entity)))) {
throw new EntityNotFoundException(type, identifier);
}
return entity;
}
@SuppressWarnings("unchecked")
private Class<T> getServiceType() {
return (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
}
}
If you need more configuration, please let me know.
My framework versions are:
<spring.version>3.2.0.RC1</spring.version>
<neo4j.version>1.8</neo4j.version>
<spring.data.neo4j.version>2.1.0.RELEASE</spring.data.neo4j.version>
we had that behavior before that it failed on the wrong entity type being returned, we changed that behavior so that the type you provide is used to automatically project the node to.
public <S extends PropertyContainer, T> T createEntityFromStoredType(S state, MappingPolicy mappingPolicy) {..}
template. createEntityFromStoredType(node, null) will get you the object with the stored state.
public Class getStoredJavaType(Object entity) {}
gives you the stored class for a node or relationship (or entity)
We had a discussion of changing the behavior back and failing esp. in Repositories.
The question is, what should happen then? An Exception? A Null result? ...
In general if you provide a raw node-id that is valid, returning an error or Null doesn't seem to be like a correct answer either?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With