TransactionEventHandler在Node.setProperty()上给出javax.transaction.SystemException

我正在尝试实现一个类似于neo4j-versioning中使用的TransactionEventHandler ,以便创建一个时间机器样式,版本化的Neo4j数据库,现在使用Neo4j 2.x. 它失败并带有以下无限堆栈跟踪:

 javax.transaction.SystemException: TM has encountered some problem, please perform necessary action (tx recovery/restart) at org.neo4j.kernel.impl.transaction.TxManager.assertTmOk(TxManager.java:349) at org.neo4j.kernel.impl.transaction.TxManager.setRollbackOnly(TxManager.java:758) at org.neo4j.kernel.TransactionEventHandlers.beforeCompletion(TransactionEventHandlers.java:120) at org.neo4j.kernel.impl.core.TransactionEventsSyncHook.beforeCompletion(TransactionEventsSyncHook.java:68) at org.neo4j.kernel.impl.transaction.TransactionImpl.doBeforeCompletion(TransactionImpl.java:368) at org.neo4j.kernel.impl.transaction.TxManager.commit(TxManager.java:398) at org.neo4j.kernel.impl.core.IsolatedTransactionTokenCreator.getOrCreate(IsolatedTransactionTokenCreator.java:61) at org.neo4j.kernel.impl.core.TokenHolder.createToken(TokenHolder.java:114) at org.neo4j.kernel.impl.core.TokenHolder.getOrCreateId(TokenHolder.java:102) at org.neo4j.kernel.impl.api.store.DiskLayer.propertyKeyGetOrCreateForName(DiskLayer.java:367) at org.neo4j.kernel.impl.api.store.CacheLayer.propertyKeyGetOrCreateForName(CacheLayer.java:370) at org.neo4j.kernel.impl.api.StateHandlingStatementOperations.propertyKeyGetOrCreateForName(StateHandlingStatementOperations.java:939) at org.neo4j.kernel.impl.api.DataIntegrityValidatingStatementOperations.propertyKeyGetOrCreateForName(DataIntegrityValidatingStatementOperations.java:67) at org.neo4j.kernel.impl.api.OperationsFacade.propertyKeyGetOrCreateForName(OperationsFacade.java:397) at org.neo4j.kernel.impl.core.NodeProxy.setProperty(NodeProxy.java:205) ... 

这是我的测试:

 @Test public void test() { GraphDatabaseService graphDb = new TestGraphDatabaseFactory().newImpermanentDatabase(); Node referenceNode = null; try (Transaction transaction = graphDb.beginTx()) { referenceNode = graphDb.createNode(); transaction.success(); } VersioningTransactionEventHandler versioningTransactionEventHandler = new VersioningTransactionEventHandler(referenceNode); graphDb.registerTransactionEventHandler(versioningTransactionEventHandler); try (Transaction tx = graphDb.beginTx()) { Node node = graphDb.createNode(); tx.success(); } graphDb.shutdown(); } 

这是VersioningTransactionEventHandler

 public class VersioningTransactionEventHandler implements TransactionEventHandler { private final Node versionDataNode; public VersioningTransactionEventHandler(Node versionDataNode) { this.versionDataNode = versionDataNode; } @Override public Object beforeCommit(TransactionData data) throws Exception { versionDataNode.setProperty("foo", "bar"); // <- this causes the error return null; } @Override public void afterCommit(TransactionData data, Object state) { } @Override public void afterRollback(TransactionData data, Object state) { } } 

我在我的应用程序中使用org.neo4j.neo4j-2.0.1org.neo4j.neo4j-kernel-2.0.1

为什么setProperty()导致此错误? 我该如何解决? 任何可能出错的线索都非常感谢。


更新

正如Michael Hunger建议我在传递节点之前做了一个setProperty() ,但是现在测试默默地挂起无限并且没有任何反应。 在节点上设置什么属性键值对无关紧要:

 ... referenceNode = graphDb.createNode(); referenceNode.setProperty("foo", "bar"); // <- results in hang referenceNode.setProperty("herp", "derp"); // <- results in hang also ... 

还有什么线索吗? 我只想在事务事件处理程序内操作一个节点,就像它在1.9版本中完成一样,但是Neo4j 2.x没有GraphDatabaseService#getReferenceNode()方法,该方法在那里的构造函数中传递。

首先,您似乎想要在TransactionEventHandler的afterCommit()方法中调用incrementTransactionVersion()。 这样,您就知道在增加版本之前事务已成功提交。 我假设循环性是由于增加版本Node的version属性导致另一个事务的事实。 要解决此问题,您可以检查TransactionData以查看当前事务是否是由版本节点的更改引起的。 这并不像它可能那么简单,但是这里有一些伪Java(我甚至没有测试它编译),它说明了如何做到这一点:

 Iterable> ii = txData.assignedNodeProperties(); boolean doIncrementVersion = false; for (PropertyEntry pEntry : txData.assignedNodeProperties()) { if (pEntry.key().equals("transactionVersion") { doIncrementVersion = true; break; } } if (doIncrementVersion) { incrementTransactionVersion(); } 

希望这可以帮助!

我设法通过在测试中创建版本节点的初始化代码中添加一行来摆脱循环错误混乱:

 @BeforeClass public static void setup() { db = new TestGraphDatabaseFactory().newImpermanentDatabase(); Node versionNode = null; try (Transaction tx = db.beginTx()) { versionNode = db.createNode(DynamicLabel.label("__TransactionVersion__")); versionNode.setProperty("transactionVersion", 0L); // <--- tx.success(); } transactionEventHandler = new MyTransactionEventHandler(versionNode); db.registerTransactionEventHandler(transactionEventHandler); } 

在创建节点后设置属性似乎可以修复它。 因此,错误似乎是由于在incrementVersionNumber()方法中引用事务事件处理程序中的未知“token” transactionVersion而导致错误。

显然,这是Neo4j中的一个错误。

这似乎是一个错误,是由为该属性的“名称”即时创建内部令牌引起的。

您是否可以尝试使用该属性名称,以便之前创建令牌?

如果你这样做的话,试试吧

 @Test public void test() { GraphDatabaseService graphDb = new TestGraphDatabaseFactory().newImpermanentDatabase(); Node referenceNode = null; try (Transaction transaction = graphDb.beginTx()) { referenceNode = graphDb.createNode(); // use the "foo" property-name once before referenceNode.setProperty("foo", "bar"); transaction.success(); } VersioningTransactionEventHandler versioningTransactionEventHandler = new VersioningTransactionEventHandler(referenceNode); graphDb.registerTransactionEventHandler(versioningTransactionEventHandler); try (Transaction tx = graphDb.beginTx()) { Node node = graphDb.createNode(); tx.success(); } graphDb.shutdown(); } 

当我在beforeCommit()调用setProperty()时,我发现了最后一次挂起的变通方法。 它似乎只发生在非写事务上(我以为beforeCommit()只在写操作上调用)?

那么,如果我在进行属性设置之前检查,挂起消失:

 ... @Override public Object beforeCommit(TransactionData data) throws Exception { if (containsWriteChanges(data)) { versionDataNode.setProperty("foo", "bar"); } return null; } private boolean containsWriteChanges(TransactionData data) { return data.assignedNodeProperties().iterator().hasNext() || data.assignedRelationshipProperties().iterator().hasNext() || data.createdNodes().iterator().hasNext() || data.createdRelationships().iterator().hasNext() || data.deletedNodes().iterator().hasNext() || data.deletedRelationships().iterator().hasNext() || data.removedNodeProperties().iterator().hasNext() || data.removedRelationshipProperties().iterator().hasNext(); } ...