一个简单用例上的 Neo4j 节点持久性问题

Posted

技术标签:

【中文标题】一个简单用例上的 Neo4j 节点持久性问题【英文标题】:Neo4j node persistence issue on a simple use case 【发布时间】:2012-11-12 23:26:33 【问题描述】:

我目前正在实施一些测试示例来学习 neo4j 和 spring 数据,感谢 cineast 项目和其他示例,例如 spring-data hello-worlds...

不幸的是,我现在面临一个我不明白的问题,即使我已经在我的代码和其他示例代码上花费了一些时间在 eclipse 调试器上尝试编译并找到解决方案。这个问题对我来说尤其严重,因为我无法验证 Neo4j 的持久性,即使它真的应该是一个微不足道的用例......

这是我正在测试的课程:

@NodeEntity 
public class SimplePersonImpl 
@GraphId 
private Long nodeId;

@Indexed
private String    id        = null;

@Indexed(indexType=IndexType.FULLTEXT, indexName = "LastName")
private String    lastName  = null;
@Indexed(indexType=IndexType.FULLTEXT, indexName = "FirstName")
private String    firstName = null;

public SimplePersonImpl() 



public SimplePersonImpl(String firstName_, String lastName_) 
    this.firstName=firstName_;
    this.lastName=lastName_;
    this.id=this.firstName+"."+this.lastName;


public String getID() 
    return this.id;


public String getFirstName() 
    return this.firstName;


public String getLastName() 
    return this.lastName;


public void setID(String id_) 
    this.id=id_;


public void setFirstName(String firstName_) 
    this.firstName=firstName_;


public void setLastName(String lastName_) 
    this.lastName=lastName_;


@Override
public boolean equals(Object o) 
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    SimplePersonImpl person = (SimplePersonImpl) o;
    if (nodeId == null) return super.equals(o);
    return nodeId.equals(person.nodeId);



@Override
public int hashCode() 
    return nodeId != null ? nodeId.hashCode() : super.hashCode();


@Override
public String toString() 
    return String.format("Personfirst name='%s', last name='%s'", firstName, lastName);


我目前正在测试这个类,感谢以下 jUnit 测试用例:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/yes-test-context.xml")
@Transactional
public class SimplePersonImplTest 
@Autowired Neo4jTemplate template;

@Rollback(false)
@BeforeTransaction
public void cleanUpGraph() 
    Neo4jHelper.cleanDb(template);


@Test @Transactional
public void persistedPersonShouldBeRetrievableFromGraphDB() 
    SimplePersonImpl qqun          = template.save(new SimplePersonImpl());
    SimplePersonImpl retrievedQqun = template.findOne(qqun.getNodeId(), SimplePersonImpl.class);
    assertEquals("retrieved person matches persisted one", qqun, retrievedQqun);


@Test @Transactional
public void persistedPersonWithPropertiesShouldBeRetrievableFromGraphDB() 
    SimplePersonImpl qqun = template.save(new SimplePersonImpl("testFN","testLN"));

    SimplePersonImpl retrievedQqun = template.findOne(qqun.getNodeId(), SimplePersonImpl.class);
    assertEquals("check memory first name matches persisted one", qqun.getFirstName(), retrievedQqun.getFirstName());       
    assertEquals("check memory last name matches persisted one", qqun.getLastName(), retrievedQqun.getLastName());


第一个测试运行成功,但第二个测试失败。使用 eclipse 调试器检查时,我可以看到:

在保存方法后返回一个 SimplePersonImpl qqun,其中包含正确的 firtName、lastName 和 id 值。但是 nodeId 为空,我不明白为什么。但由于这种行为在 spring-data hello-worlds 示例中是相同的,我想我的问题不是来自那里。 在 findOne 方法中,即使我的 qqun 对象中的 nodeId 为空,qqun.getNodeId() 也会返回 1。我不明白这个值是从哪里来的,但让我们继续 findOne 方法返回一个检索到的Qqun。所有检索到的Qqun 属性均为空,这似乎是我在 firstName 上的第一个 assertEquals 失败的原因。

在这里我真的不明白我在哪里做错了(我想我做错了),但很明显 spring-data neo4j 持久性背后有很多我不明白的事情。我很乐意在这些方面得到一些答案(为什么我在保存调用后看到 nodeId=null ?为什么我检索一个所有属性都为 null 的非 null 对象?...)。

可能出现一些错误的最后一点是我的测试上下文配置,但我还是看不出问题出在哪里:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:neo4j="http://www.springframework.org/schema/data/neo4j"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
    http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/data/neo4j
    http://www.springframework.org/schema/data/neo4j/spring-neo4j.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd">

<context:annotation-config/>
<context:spring-configured/>
<context:component-scan base-package="yes.ds.domain.neo4j"/>

<neo4j:config graphDatabaseService="graphDatabaseService"/>
<bean id="graphDatabaseService" class="org.neo4j.test.ImpermanentGraphDatabase" destroy-method="shutdown"/>
<!-- <neo4j:config storeDirectory="target/neo4j-db"/> -->
<neo4j:repositories base-package="yes.ds.repository.neo4j"/>
<tx:annotation-driven mode="proxy"/></beans>

谢谢

【问题讨论】:

我认为您的困惑来自 aspectj 所做的拦截,它从您的类中拦截字段访问并查看这些值的节点属性,而不考虑类中的字段为了这。没有一个吸气剂返回值吗?作为一个实验:你可以尝试而不是直接在构造函数中设置字段,而是在外部或使用 setter 设置它们? 我已经在保存调用之前使用 setter 进行了测试:相同的行为... 我找到了我的问题。我不确定它是否与您的问题直接相关,但如果您创建自定义关系类(用于 [at]RelatedToVia 关系),并且使用该类创建的关系将自动具有 type 属性包的名称和类的类。如果您也有一个创建关系的 [at]Query,则该查询也不会创建 type 属性。这会在稍后加载关系时导致问题,从而导致缺少 type 属性问题。希望有帮助! 【参考方案1】:

我想我在创建的测试实体中遇到了类似的问题。 在那种特殊情况下,我忘记用 @Transactional 注释 getter 方法。

NodeEntities 的 getter 和 setter 都需要注解 @Transactional,否则它们的行为与您描述的一样。添加注释为我修复了它。

必须启用事务支持。

【讨论】:

以上是关于一个简单用例上的 Neo4j 节点持久性问题的主要内容,如果未能解决你的问题,请参考以下文章

你如何防止测试在第一个失败的测试用例上停止?

第二篇阅读笔记

我真的需要单例上的共享实例吗?

neo4j是啥?怎么配置?能单独使用吗?

Neo4j 超级节点问题 - 扇出模式

Neo4j学习--简单入门