We are using spring-data-neo4j with repository methods and annotated queries. Formerly, we used SDN with a rest connection to a standalone server. Performance was very poor, though, so we decided to use SDN with an embedded neo4j instance. But that does not work as expected.
Here are some classes
The node entity
import java.util.Set;
import org.codehaus.jackson.annotate.JsonIgnore;
import org.neo4j.graphdb.Direction;
import org.springframework.data.neo4j.annotation.Fetch;
import org.springframework.data.neo4j.annotation.GraphId;
import org.springframework.data.neo4j.annotation.Indexed;
import org.springframework.data.neo4j.annotation.NodeEntity;
import org.springframework.data.neo4j.annotation.RelatedToVia;
import com.xxx.xyz.relationships.UserToAnnotation;
import com.xxx.xyz.relationships.UserToBookmark;
import com.xxx.xyz.relationships.UserToGroup;
import com.xxx.xyz.relationships.UserToLastReadMedia;
import com.xxx.xyz.relationships.UserToLicenseOwner;
import com.xxx.xyz.relationships.UserToLicenseReader;
import com.xxx.xyz.relationships.UserToUser;
/** Simple user class. */
@NodeEntity
public class Neo4jUser {
@GraphId
Long id;
private String type;
@Indexed(unique=true)
private String emailAddress;
@Indexed
private String nickname;
private String passwordHash;
private String givenName;
private String familyName;
private Long birthdate;
@JsonIgnore
@RelatedToVia(type = "isIn", direction = Direction.OUTGOING)
private Set<UserToGroup> memberships;
public Neo4jUser() {
}
//constructor
//getter / setter
public void isIn(final Neo4jGroup group, final String role) {
memberships.add(new UserToGroup(this, group, role));
}
}
One relationship entity
package com.xxx.xyz.relationships;
import org.springframework.data.neo4j.annotation.EndNode;
import org.springframework.data.neo4j.annotation.Fetch;
import org.springframework.data.neo4j.annotation.GraphId;
import org.springframework.data.neo4j.annotation.RelationshipEntity;
import org.springframework.data.neo4j.annotation.StartNode;
import com.xxx.xyz.model.db.Neo4jGroup;
import com.xxx.xyz.model.db.Neo4jUser;
@RelationshipEntity(type="isIn")
public class UserToGroup {
@GraphId
Long id;
@Fetch @StartNode
private Neo4jUser user;
@Fetch @EndNode
private Neo4jGroup group;
private String role;
public UserToGroup(){
}
public UserToGroup(final Neo4jUser user, final Neo4jGroup group, final String role)
{
this.user = user;
this.group = group;
this.role = role;
}
}
The user repository
@Transactional
public interface UserRepository extends GraphRepository<Neo4jUser> {
//annotated cypher queries
}
spring applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/data/neo4j
http://www.springframework.org/schema/data/neo4j/spring-neo4j-2.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<context:annotation-config />
<context:spring-configured />
<context:component-scan base-package="com.xxx.xyz.service" />
<neo4j:config storeDirectory="data/graph.db" />
<neo4j:repositories base-package="com.xxx.xyz.repository" />
</beans>
maven dependency
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
When trying to modify the set memberships
by using the method Neo4JUser.isIn()
the following exception is thrown
org.neo4j.graphdb.NotInTransactionException
at org.neo4j.kernel.impl.persistence.PersistenceManager.getResource(PersistenceManager.java:252)
at org.neo4j.kernel.impl.persistence.PersistenceManager.relationshipCreate(PersistenceManager.java:161)
at org.neo4j.kernel.impl.core.NodeManager.createRelationship(NodeManager.java:252)
at org.neo4j.kernel.impl.core.NodeImpl.createRelationshipTo(NodeImpl.java:578)
at org.neo4j.kernel.impl.core.NodeProxy.createRelationshipTo(NodeProxy.java:207)
at org.springframework.data.neo4j.support.DelegatingGraphDatabase.createRelationship(DelegatingGraphDatabase.java:125)
at org.springframework.data.neo4j.support.mapping.EntityStateHandler.getOrCreateRelationship(EntityStateHandler.java:168)
at org.springframework.data.neo4j.support.mapping.EntityStateHandler.useOrCreateState(EntityStateHandler.java:139)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityConverterImpl.write(Neo4jEntityConverterImpl.java:146)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister$CachedConverter.write(Neo4jEntityPersister.java:179)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister.persist(Neo4jEntityPersister.java:249)
at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister.persist(Neo4jEntityPersister.java:231)
at org.springframework.data.neo4j.support.Neo4jTemplate.save(Neo4jTemplate.java:293)
at org.springframework.data.neo4j.fieldaccess.RelatedToViaCollectionFieldAccessorFactory$RelatedToViaCollectionFieldAccessor.persistEntities(RelatedToViaCollectionFieldAccessorFactory.java:99)
at org.springframework.data.neo4j.fieldaccess.RelatedToViaCollectionFieldAccessorFactory$RelatedToViaCollectionFieldAccessor.setValue(RelatedToViaCollectionFieldAccessorFactory.java:93)
at org.springframework.data.neo4j.fieldaccess.ManagedFieldAccessorSet.updateValue(ManagedFieldAccessorSet.java:94)
at org.springframework.data.neo4j.fieldaccess.ManagedFieldAccessorSet.update(ManagedFieldAccessorSet.java:82)
at org.springframework.data.neo4j.fieldaccess.ManagedFieldAccessorSet.add(ManagedFieldAccessorSet.java:108)
at com.xxx.xyz.model.db.Neo4jUser.isIn(Neo4jUser.java:187)
As I found out doing some research, the rest connection of SDN does not use transactions, the embedded version does, so NotInTransactionException
are possible.
Moreover, we didn't expect save()
to be triggered, when manipulating a set with relationships. We always called save() manually after. Why is that?
But what is done wrong here?
Any help is appreciated!
Thanks!