【问题标题】:Resolving entities with Spring Data Neo4j returns wrong entity types使用 Spring Data Neo4j 解析实体返回错误的实体类型
【发布时间】:2012-11-13 15:29:55
【问题描述】:

当我使用 Spring Data Neo4j (SDN) 查找节点实体时,我遇到了一些奇怪的行为。如果我使用 GraphRepository.findOne(long) 它将返回一个具有该标识符的实体,即使该实体不是同一类型。

这就是我(非常)简化的实体结构的样子:

@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;

}

对于每种实体类型,我都创建了这样的存储库:

@Repository
public interface PersonRepository extends GraphRepository<Person> {}

@Repository
public interface AuditRepository extends GraphRepository<Audit> {}

我的服务层类有一个抽象基类。它们大致是这样的:

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;
    }

}

当我执行以下代码时,结果并不如预期:

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);

您希望 AuditRepository 会返回 null,但事实并非如此。相反,它会返回一个带有标识符 1L 且所有属性都为 null 的 Audit。似乎只要有一个与给定标识符对应的节点,它就会被返回,不管它的类型是什么。如果 Person 和 Audit 有匹配的属性名称,它们也将包含它们的值......所有这些都是预期的行为,还是我遗漏了什么?

目前,我已经用下面的代码解决了这个问题,我自己进行类型检查。

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];
    }

}

如果您需要更多配置,请告诉我。

我的框架版本是:

<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>

【问题讨论】:

  • 谢谢@tstorms,这篇文章和你的解决方法在处理这个错误时对我很有帮助。
  • 一个简短的问题:PersonServiceImpl 的 '@Autowired` 注释应该在 PersonRepository 字段上而不是构造函数上?
  • 不,为什么?我选择了构造函数注入而不是字段注入。关于为什么场注入可能是“邪恶”的非常有趣的读物:olivergierke.de/2013/11/why-field-injection-is-evil

标签: spring neo4j nosql spring-data-neo4j


【解决方案1】:

在返回错误的实体类型之前,我们有这种行为,我们更改了这种行为,以便使用您提供的类型自动将节点投影到。

public <S extends PropertyContainer, T> T createEntityFromStoredType(S state, MappingPolicy mappingPolicy) {..}

模板。 createEntityFromStoredType(node, null) 将为您获取具有存储状态的对象。

public Class getStoredJavaType(Object entity) {} 

为您提供节点或关系(或实体)的存储类

我们讨论了改变行为和失败的问题。在存储库中。

问题是,接下来会发生什么?例外?空结果? ...

一般来说,如果您提供有效的原始节点 ID,返回错误或 Null 似乎也不是正确答案?

【讨论】:

  • 在我的解决方法中,当返回错误类型的实体时,我会抛出异常。对我来说,这确实感觉很自然。也许这只是品味问题?虽然改回行为对我的项目来说是理想的,但这不会破坏许多生产环境吗?
  • 我同意当找到具有 id 的节点时应该抛出异常,但它不是异常类型。为什么?我在预期类型的​​属性上使用注释。由于初始化对象时未找到预期的属性,因此引发异常。在这种情况下,恕我直言,存储库中的异常会更干净。
  • 为了满足大多数使用场景:也许期望的行为应该是可配置的。
  • 这确实是一个很棒的补充! @Michael,你怎么看?
  • 我想在这里权衡一下并说:在存储库级别,如果没有 id 为 123L 的人,我觉得 PersonRepository.find(123L) 返回任何东西都是不可接受的。我觉得唯一正确的行为是抛出 ObjectNotFoundException。我还觉得当前的行为非常隐含并且没有很好的文档记录,这反过来又导致我在代码中引入了一个错误。
猜你喜欢
  • 1970-01-01
  • 2012-12-18
  • 2020-03-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多