Spring Data Neo4j and InvalidDataAccessApiUsageException for #Index property - neo4j

I have added uuid property to my SDN 4 Base entity so now it looks like:
#NodeEntity
public abstract class BaseEntity {
#GraphId
private Long id;
#Index(unique = true, primary = true)
private String uuid;
...
}
Right now some of my tests stopped working.
I have a Commentable abstract class:
#NodeEntity
public abstract class Commentable extends BaseEntity {
private final static String COMMENTED_ON = "COMMENTED_ON";
#Relationship(type = COMMENTED_ON, direction = Relationship.INCOMING)
private Set<Comment> comments = new HashSet<>();
public Set<Comment> getComments() {
return comments;
}
public void addComment(Comment comment) {
if (comment != null) {
comments.add(comment);
}
}
public void removeComment(Comment comment) {
comments.remove(comment);
}
}
Different NodeEntity from my domain model extend this class.. like
public class Decision extends Commentable
public class Product extends Commentable
etc
This my SDN 4 repository
#Repository
public interface CommentableRepository extends GraphRepository<Commentable> {
}
Right now when I try to access
commentableRepository.findOne(commentableId);
where commentableId is Decision id or Product id it is fails with a following exception:
org.springframework.dao.InvalidDataAccessApiUsageException: Supplied id does not match primary index type on supplied class.; nested exception is org.neo4j.ogm.session.Neo4jException: Supplied id does not match primary index type on supplied class.
at org.springframework.data.neo4j.transaction.SessionFactoryUtils.convertOgmAccessException(SessionFactoryUtils.java:154)
at org.springframework.data.neo4j.repository.support.SessionBeanDefinitionRegistrarPostProcessor.translateExceptionIfPossible(SessionBeanDefinitionRegistrarPostProcessor.java:71)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:147)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:57)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
at com.sun.proxy.$Proxy175.findOne(Unknown Source)
But if I remove #Index(unique = true, primary = true) from my BaseEntity.uuid field everything starts working fine.
How to solve this issue ?

You are using the wrong Repository. GraphRepository is a legacy implementation. The new implementation is called Neo4jRepository.
Try:
public interface CommentableRepository extends Neo4jRepository<Commentable, String> {
...
}
The key difference is the second parameterized type which is the type of the ID used for the domain class; in this case String uuid.

Related

Neo4j Spring Data NodeEntity use String as #id

I'm trying to use a java.lang.String as the #Id of a NodeEntity.
#NodeEntity(label = "MachineType")
public class MachineType {
#Id private String id;
....
It should be possible acording to the spring data neo4j docu:
While an id is still required on all entities, the behavior has been
simplified by introducing the new #Id annotation. It replaces both
#GraphId and the primary attribute and can be placed on any attribute
with a simple type.
When I'm trying to insert I get a:
{
"cause": null,
"message": "Id must be assignable to Serializable!: null"
}
Which is strange, because String implements the Serializable.
Anyone has an idea where to search next?
I think that you cannot use anything else as an ID. Keep in mind that this Long number will be reused if you delete the node.
I use UUID plugin to generate true unique keys and when I use spring-data-rest I use BackendIdConverter to change the id to the uuid for the resources that I expose.
Example:
Model:
#NodeEntity
#Data
public class Target {
#Id #GeneratedValue Long id; // <----Neo4j id
private String uuid; // <----My Key
#Version Long version;
private List<String> labels = new ArrayList<>();
#Relationship(type = "HAS_MEDIA", direction=Relationship.OUTGOING)
private List<Gallery> media = new ArrayList<>();
}
Convert resource id to my key:
#Component
public class MovieIdConverter implements BackendIdConverter {
#Autowired MovieRepo movieRepository;
#Override
public Serializable fromRequestId(String id, Class<?> entityType) {
Movie movie = movieRepository.findByUuid(id);
return (Serializable) movie.getId();
}
#Override
public String toRequestId(Serializable serializable, Class<?> aClass) {
Long id = (Long) serializable;
Optional<Movie> movie = movieRepository.findById(id);
if (movie.isPresent()) return movie.get().getUuid();
return null;
}
#Override
public boolean supports(Class<?> aClass) {
return Movie.class.equals(aClass);
}
}

GraphRepository find method on 2nd level relationship

I am using spring-data-neo4j-4.2.0.RC1 and neo4j-ogm-core-2.1.0.jarHave the following domain objectsUser -> Role -> Priviledge
public class User extends BaseEntity{
private String firstName;
private String lastName;
#Relationship(type="ROLE")
private Role role;
}
#NodeEntity
public class Role extends BaseEntity{
private String name;
#Relationship(type="PRIVILEDGE")
private Priviledge priviledge;
}
#NodeEntity
public class Priviledge extends BaseEntity {
private String name;
}
public interface UserRepository extends GraphRepository<User> {
User findByRolePriviledgeName(String name);
}
I want to find all users that have a specific priviledge name. The above query worked with JPA and spring repository, but does not return expected result with GraphRepository. Not sure if this is a bug/not supported or I am doing something wrong
This is not supported in SDN- derived queries support only one level of nesting but yours uses two (Role, then Privilege).
You can write a custom #Query for this.

spring data neo4j #Query by relationship type

I need to query neo4j by relationship type
this is My Entity class
#NodeEntity
#Getter
#Setter
#NoArgsConstructor
public class ProductRecommendation {
#GraphId
private Long id;
String product;
#Relationship(type = "RECOMMENDS", direction = Relationship.OUTGOING)
Set<ProductRecommendation> linkedProducts = new HashSet<>();
}
I need to find all the nodes with relationship type as "RECOMMENDS".
Is there a default findBy method?
I tried with this and it works
public interface ProductRecommendationRepository extends GraphRepository<ProductRecommendation> {
#Query("MATCH p=()-[r:RECOMMENDS]->() RETURN p")
List<ProductRecommendation> findByRelationShipType();
}
however if I pass relationship type as variable, it doesn't work
public interface ProductRecommendationRepository extends GraphRepository<ProductRecommendation> {
#Query("MATCH p=()-[r:{0}]->() RETURN p")
List<ProductRecommendation> findByRelationShipType(String type);
}
Can someone please explain.
Thanks
The relationship type cannot be parameterised (see http://neo4j.com/docs/developer-manual/current/cypher/#cypher-parameters).
so you will have to go with
public interface ProductRecommendationRepository extends GraphRepository<ProductRecommendation> {
#Query("MATCH p=()-[r:RECOMMENDS]->() RETURN p")
List<ProductRecommendation> findByRelationShipType();
}

Use #NodeEntity on interface/abstract class

Is it possible to add #NodeEntity (or even #RelationshipEntity) annotation from SpringData Neo4j on an interface or abstact class or their fields? If not, how do you manage these situations?
Definitely you can do that on Abstract classes, and I think it's a good practice in some common cases. Let me give you an example that I'm using in my graph model:
#NodeEntity
public abstract class BasicNodeEntity implements Serializable {
#GraphId
private Long nodeId;
public Long getNodeId() {
return nodeId;
}
#Override
public abstract boolean equals(Object o);
#Override
public abstract int hashCode();
}
public abstract class IdentifiableEntity extends BasicNodeEntity {
#Indexed(unique = true)
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof IdentifiableEntity)) return false;
IdentifiableEntity entity = (IdentifiableEntity) o;
if (id != null ? !id.equals(entity.id) : entity.id != null) return false;
return true;
}
#Override
public int hashCode() {
return id != null ? id.hashCode() : 0;
}
}
Example of entity idenifiable.
public class User extends IdentifiableEntity {
private String firstName;
// ...
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}
OTOH, as far as I know, if you annotate an interface with #NodeEntity, those classes who implement the interface DON'T inherite the annotation. To be sure I've made a test to check it and definately spring-data-neo4j throws an Exception because don't recognize the inherited class neither an NodeEntity nor a RelationshipEntity.
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'neo4jMappingContext' defined in class org.springframework.data.neo4j.config.Neo4jConfiguration: Invocation of init method failed; nested exception is org.springframework.data.neo4j.mapping.InvalidEntityTypeException: Type class com.xxx.yyy.rest.user.domain.User is neither a #NodeEntity nor a #RelationshipEntity
Hope it helps
#NodeEntity or #RelationshipEntity needs to be defined on POJO or concrete classes. Think it same as #Entity in Hibernate.
But do you see any valid use case for annotating Interfaces or Abstract Classes?

Target state is not the requested interface org.neo4j.graphdb.Node but null

I encountered some difficulties during playing with neo4j. Firstly, when I try to delete defined #EntityModel, I get an exception (Please, forgive me the quality of pics, the exception messages are also in question title):
My Controller (this is just for testing purpouse):
#Controller
public class HomeController {
#Autowired
PersonRepository personRepository;
#RequestMapping(value="/", method = RequestMethod.GET)
public String loadPage(final Model model, final HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
Person person = new Person("My person");
personRepository.save(person);
personRepository.findOne(person.getId());
return "home";
}
}
And model:
#NodeEntity
public class Person {
#GraphId
private Long id;
private String name;
public Person() {}
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public Long getId() {
return id;
}
}
Configuration file:
#Configuration
#EnableTransactionManagement
#EnableNeo4jRepositories(basePackages = "com.springapp.mvc.repository")
#ComponentScan({"com.springapp.mvc"})
public class PersistenceConfig extends Neo4jConfiguration {
#Bean
public GraphDatabaseService graphDatabaseService() {
return new SpringRestGraphDatabase("http://localhost:7474/db/data");
}
}
My Repository:
public interface PersonRepository extends GraphRepository<Person> {
#Query("MATCH (n:Person{name: \"{namveValue}\"}) RETURN n;")
Person findByName(#Param("nameValue") final String name);
}
What am I doing wrong? I figured out that perhaps Person should implement org.neo4j.graphdb.Node and this is the source of these exceptions. However, having searched github repos I see that people do not implement this interface in their models. I have not found solution on stackoverflow so far.
Node exists in database but I cannot either delete it or save it. Please, help.
You are trying to see if a node with ID '0' exists as a person. Since the root node hasn't got a '__type__' property, the call will fail. SDN uses this property to determine the entity type of a node.
That being said, the exception seems to be caused by the following line:
if(! personRepository.exists(0L)) {

Resources