Cyclic recursion call in neo4j - spring

I am trying to input few graph nodes in neo4j db using spring-data-neo4j.
Nodes are having the following relationship.
Project -> Cluster -> Entity -> Methods and Entity node has relationship with its ownn, making it bidirectinoal relationship.
Entity class is defined as follows.
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
#NodeEntity
public class Entity {
public int id;
public String type;
public String name;
public String entityId;
Public String projectId;
#Relationship(type = "CONNECTS_TO", direction = Relationship.INCOMING)
private Set<Entity> entityIdr;
}
Below error is thrown while trying to insert Cluster and Entity nodes, what could be possible resolution to avoid following ?
com.fasterxml.jackson.databind.JsonMappingException: Infinite
recursion (StackOverflowError) (through reference chain:

I assume that you try to serialize the data and expose it on the web layer or similar, right?
The jackson serialization needs to get more informations on how to break the cycles the Java data model describes.
So either you ignore a property with #JsonIgnore like
#JsonIgnore
#Relationship(type = "CONNECTS_TO", direction = Relationship.INCOMING)
private Set<Entity> entityIdr;
, but that seems at least for the first level like a loss of information,
or
#JsonIgnoreProperties("entityIdr")
#Relationship(type = "CONNECTS_TO", direction = Relationship.INCOMING)
private Set<Entity> entityIdr;
We wrote this up in the documentation for Neo4j-OGM: https://neo4j.com/docs/ogm-manual/current/reference/#_a_note_on_json_serialization

Related

spring boot resource as links

I am new to spring boot, I have used it to implement a rest API. I have a self referencing table where each item has a parent -tree structure- . I have used ManyToOne to implement this and i get a json object which holds the parent. If i get all the items the speed is very slow since there are a huge network latency and processing because my tree can hold up to 10000 item.
How can I represent this using links, i.e the the json object contains a link to parent and array of links to children. I have read that DTO can be used to implement this but I did not find a full details.
part of my code
#Entity
#Table(name = "Item", schema = "dbo")
#Getter
#Setter
public class Item {
#Id
#GeneratedValue
#Column()
private Integer ItemID;
#Column()
private String Project;
#Column()
private String Name;
#Column()
private Integer Version;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ParentItemID", insertable = false, updatable = false)
private Item parentItem;
My controller is strait forward
#Autowired
ItemService ItemService;
#RequestMapping ("/items")
public Iterable<Item> items(#RequestParam(value = "page", defaultValue = "0") Integer page,
#RequestParam(value = "size", defaultValue = "20") Integer size, Authentication auth) {
return ItemService.findPaginated(page, size);
}
I would be glad if i get more explanation on how to use DTOs , or another design to get links instead of full objects.
Easiest thing you can do is, return parent and children ids instead of Item objects.
#Entity
#Table(name = "Item", schema = "dbo")
public class Item {
.
.
private Integer ItemID;
private Integer parentItem;
.
.
Have another endpoint in your controller like /items/{itemId}
#GetMapping("/items/{itemId}")
public Item item(#PathVariable itemId, Authentication auth) {
return ItemService.findById(itemId);
Let your consumer grab the parent/children Ids and make separate REST calls
Alternatively, you can use Spring-HATEOAS
The Spring HATEOAS project is a library of APIs that we can use to
easily create REST representations that follow the principle of
HATEOAS (Hypertext as the Engine of Application State).
Generally speaking, the principle implies that the API should guide
the client through the through the application by returning relevant
information about the next potential steps, along with each response.
More Information Here

Spring entity dynamically calculated field with parameter from native query

I have a somewhat complex entity like following (Notice the super-class with many more fields):
public class Question extends Entry {
#OneToMany(orphanRemoval = true, mappedBy = "question")
#JsonManagedReference
private List<Answer> answers = new ArrayList<>();
private Long viewCount = 0L;
private Category category;
#OneToMany(mappedBy = "question", fetch = FetchType.LAZY,
cascade = CascadeType.ALL, orphanRemoval = true)
private List<QuestionTranslation> translations = new ArrayList<>();
#Transient
private double distance;
}
distance should be calculated from the DB when retrieving the result set from a native query.
E.g.
SELECT q.*, ST_Distance_Sphere(cast(q.location as geometry), ST_MakePoint(cast(?1 as double precision), cast(?2 as double precision))) as distance from question q
I cannot use #Formula to annotate my field distance since the query has to take parameters.
How can I map the field distance from the SQL query result to my entity field distance while leaving all the other mappings to be done by Hibernate?
Edit
Based on #gmotux suggestion I created a wrapper entity.
#Entity
#SqlResultSetMapping(
name="MappingQ",
entities={
#EntityResult(
entityClass = QuestionWithDistance.class,
fields={
#FieldResult(name="distance",column="distance"),
#FieldResult(name="question",column="question")})})
public class QuestionWithDistance{
#Id
#GeneratedValue
private String id;
#OneToOne
private Question question;
private double distance;
}
Query
Query query = entityManager.createNativeQuery("SELECT q.*, 222.22 as distance from question q", "MappingQ");
But it always fails with
org.postgresql.util.PSQLException: The column name id1_15_0_ was not found in this ResultSet.
Since you need extra parameters to calculate your field, you indeed cannot use #Formula, or even a getter to calculate the field.
Unfortunately for your case the only thing that comes to mind, assuming you are using an EntityManager based configuration for Hibernate, is leveraging its #PostLoad event listener, which you can use for calculating field values upon entity loading, like :
public class Question extends Entry {
#PostLoad
private void postLoad() {
this.distance = DistanceCalculator.calculateDistance(Double param1,Double param2);
//other calculations
}
}
That of-course is only a workaround and it means that you must have a static method somewhere execute native queries.
I would suggest detaching the "distance" notion from your Question entity, if possible in your requirements and calculate it, when required, with either a native SQL function call or a service method.

Cannot Query Neo4j Repositories

Hey everyone I am fairly new to Neo4j and am having an issue querying my repositories.
Repository is the follow:
public interface NodeOneRepository extends GraphRepository<NodeOne> {
List<NodeOne> findByNodeTwoNodeThreeAndActiveTrue(NodeThree nodeThree);
}
My entities are the following:
#NodeEntity(label = "NodeOne")
public class NodeOne {
#GraphId
private Long id;
private Boolean active = TRUE;
#Relationship(type = "IS_ON")
private NodeTwo nodeTwo;
}
#NodeEntity(label = "NodeTwo")
public class NodeTwo {
#GraphId
private Long id;
#Relationship(type = "CONTAINS", direction = "INCOMING")
private NodeThree nodeThree;
#Relationship(type = "IS_ON", direction = "INCOMING")
private List<NodeOne> nodeOnes = new ArrayList<>();
}
#NodeEntity(label = "NodeThree")
public class NodeThree {
#GraphId
private Long id;
#Relationship(type = "CONTAINS")
private List<NodeTwo> nodeTwos = new ArrayList<>();
}
Getters & Setters omitted. When I call the method I get an empty list. Is there something I am doing incorrectly?
You didn't describe exactly what you wanted to achieve, but I can see two problems:
Problem 1:
The current version of Spring Data Neo4j and OGM only allow nested finders, that is, finders that specify a relationship property, to one depth.
Supported
findByNodeTwoSomePropertyAndActiveTrue(String relatedNodePropertyValue)
Not Supported
findByNodeTwoNodeThree //Nesting relationships in finders is not supported
Problem 2:
Derived Finders Allow Matching Properties and Nested Properties. Not a whole instance of that class.
You can probably achieve what you would like using a custom query.
#Query("custom query here")
List<NodeOne> findByNodeTwoNodeThreeAndActiveTrue(NodeThree nodeThree);
If you need help to write a custom query, you can post another question or join the neo4j-users public slack channel.

Spring Data Rest - sort by nested property

I have a database service using Spring Boot 1.5.1 and Spring Data Rest. I am storing my entities in a MySQL database, and accessing them over REST using Spring's PagingAndSortingRepository. I found this which states that sorting by nested parameters is supported, but I cannot find a way to sort by nested fields.
I have these classes:
#Entity(name = "Person")
#Table(name = "PERSON")
public class Person {
#ManyToOne
protected Address address;
#ManyToOne(targetEntity = Name.class, cascade = {
CascadeType.ALL
})
#JoinColumn(name = "NAME_PERSON_ID")
protected Name name;
#Id
protected Long id;
// Setter, getters, etc.
}
#Entity(name = "Name")
#Table(name = "NAME")
public class Name{
protected String firstName;
protected String lastName;
#Id
protected Long id;
// Setter, getters, etc.
}
For example, when using the method:
Page<Person> findByAddress_Id(#Param("id") String id, Pageable pageable);
And calling the URI http://localhost:8080/people/search/findByAddress_Id?id=1&sort=name_lastName,desc, the sort parameter is completely ignored by Spring.
The parameters sort=name.lastName and sort=nameLastName did not work either.
Am I forming the Rest request wrong, or missing some configuration?
Thank you!
The workaround I found is to create an extra read-only property for sorting purposes only. Building on the example above:
#Entity(name = "Person")
#Table(name = "PERSON")
public class Person {
// read only, for sorting purposes only
// #JsonIgnore // we can hide it from the clients, if needed
#RestResource(exported=false) // read only so we can map 2 fields to the same database column
#ManyToOne
#JoinColumn(name = "address_id", insertable = false, updatable = false)
private Address address;
// We still want the linkable association created to work as before so we manually override the relation and path
#RestResource(exported=true, rel="address", path="address")
#ManyToOne
private Address addressLink;
...
}
The drawback for the proposed workaround is that we now have to explicitly duplicate all the properties for which we want to support nested sorting.
LATER EDIT: another drawback is that we cannot hide the embedded property from the clients. In my original answer, I was suggesting we can add #JsonIgnore, but apparently that breaks the sort.
I debugged through that and it looks like the issue that Alan mentioned.
I found workaround that could help:
Create own controller, inject your repo and optionally projection factory (if you need projections). Implement get method to delegate call to your repository
#RestController
#RequestMapping("/people")
public class PeopleController {
#Autowired
PersonRepository repository;
//#Autowired
//PagedResourcesAssembler<MyDTO> resourceAssembler;
#GetMapping("/by-address/{addressId}")
public Page<Person> getByAddress(#PathVariable("addressId") Long addressId, Pageable page) {
// spring doesn't spoil your sort here ...
Page<Person> page = repository.findByAddress_Id(addressId, page)
// optionally, apply projection
// to return DTO/specifically loaded Entity objects ...
// return type would be then PagedResources<Resource<MyDTO>>
// return resourceAssembler.toResource(page.map(...))
return page;
}
}
This works for me with 2.6.8.RELEASE; the issue seems to be in all versions.
From Spring Data REST documentation:
Sorting by linkable associations (that is, links to top-level resources) is not supported.
https://docs.spring.io/spring-data/rest/docs/current/reference/html/#paging-and-sorting.sorting
An alternative that I found was use #ResResource(exported=false).
This is not valid (expecially for legacy Spring Data REST projects) because avoid that the resource/entity will be loaded HTTP links:
JacksonBinder
BeanDeserializerBuilder updateBuilder throws
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of ' com...' no String-argument constructor/factory method to deserialize from String value
I tried activate sort by linkable associations with help of annotations but without success because we need always need override the mappPropertyPath method of JacksonMappingAwareSortTranslator.SortTranslator detect the annotation:
if (associations.isLinkableAssociation(persistentProperty)) {
if(!persistentProperty.isAnnotationPresent(SortByLinkableAssociation.class)) {
return Collections.emptyList();
}
}
Annotation
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface SortByLinkableAssociation {
}
At project mark association as #SortByLinkableAssociation:
#ManyToOne
#SortByLinkableAssociation
private Name name;
Really I didn't find a clear and success solution to this issue but decide to expose it to let think about it or even Spring team take in consideration to include at nexts releases.
Please see https://stackoverflow.com/a/66135148/6673169 for possible workaround/hack, when we wanted sorting by linked entity.

spring jpa projection nested bean

is it possible to have a projection with nested collection with Spring JPA?
I have the following 2 simple entity (to explain the problem)
#Entity
#Table(name = "person")
public class Person implements Serializable {
private Integer id;
private String name;
#OneToMany
private List<Address> addressList = new ArrayList<>();
}
#Entity
#Table(name = "address")
public class Address implements Serializable {
private Integer id;
private String city;
private String street;
}
Is it possible to have a projection of Person with following attributes filled in ? {person.name, address.city}
I might be wrong in semantics of word Projection. but the problem is what i need to achieve. Maybe it is not possible with Projection, but is there another way to achieve the end goal? Named Entity graph perhaps ?
P.S. please suggest a solution for Spring JPA not Spring Jpa REST
thanks in advance
You're right, Entity Graphs serve this exact purpose - control field loading.
Create entity graphs dynamically from the code or annotate target entities with Named Entity Graphs and then just use their name.
Here is how to modify your Person class to use Named Entity Graphs:
#Entity
#Table(name = "person")
#NamedEntityGraph(name = "persion.name.with.city",
attributeNodes = #NamedAttributeNode(value = "addressList", subgraph = "addresses.city"),
subgraphs = #NamedSubgraph(name = "addresses.city", attributeNodes = #NamedAttributeNode("city")))
public class Person implements Serializable {
private Integer id;
private String name;
#OneToMany
private List<Address> addressList;
}
And then when loading your person:
EntityGraph graph = em.getEntityGraph("person.name.with.city");
Map hints = new HashMap();
hints.put("javax.persistence.fetchgraph", graph);
return em.find(Person.class, personId, hints);
The same applies for queries, not only em.find method.
Look this tutorial for more details.
I think that that's not usual scenario of Data JPA usage. But you can achieve your goal with pure JPQL:
SELECT a.street, a.person.name FROM Address a WHERE …
This solution has 2 drawbacks:
It forces you to have bidirectional relationship Address ←→ Person
It returns List
Another solution (and that's preferred JPA way) is to create DTO like this:
class MyPersonDTO {
private String personName;
private List<String> cities;
public MyPersonDTO(String personName, List<Address> adresses) {
this.personName = personName;
cities = adresses
.stream()
.map(Address::getCity)
.collect(Collectors.toList());
}
}
And the execute JPQL query like this:
SELECT NEW package.MyPersonDTO(p.name, p.addressList) FROM Person p WHERE …
Return type will be List<MyPersonDTO> in that case.
Of course you can use any of this solutions inside #Query annotation and it should work.

Resources