Create Neo4j relation in spring data with existing node - java

I am trying to create relationships between nodes in Neo4j. I am using the Neo4J(2.1.8 Community) & spring-data-neo4j(3.3.0.RELEASE).
I am trying to create the below relation.
Create a new Employee(node) which will be reporting(Empty Relation) to the Manager(node) which is there in DB (Searching by name). I have used the below query.
public interface EmployeeRepository extends GraphRepository<Employee> {
#Query("START employee=node:({0}), manager=node:Employee(name={1}) CREATE employee-[:REPORTS_TO]->manager")
void addNewEmployee(Employee employee, String managerName);}
I got the below error.
Caused by: org.springframework.dao.InvalidDataAccessResourceUsageException: Error executing statement START employee=node:({0}), manager=node:Employee(name={1}) CREATE employee-[:REPORTS_TO]->manager; nested exception is Invalid input '(': expected whitespace or an identifier (line 1, column 21)
"START employee=node:({0}), manager=node:Employee(name={1}) CREATE employee-[:REPORTS_TO]->manager"
^
Can anyone please tell me what is wrong with this query? Also if this is not the right way to create the relations using GraphRepository then what else I can use to accomplish the same.
Thanks in advance.
Note: I have used this to learn the queries in Spring Data for Neo4j. Where they have shown the basic queries.
Updated: Employee Class
#NodeEntity
public class Employee {
#GraphId
private Long id;
private String name;
private String department;
#RelatedTo(type = "REPORTS_TO")
private Employee reportsTo;
#RelatedTo(type = "REPORTS_TO", direction = Direction.INCOMING)
Set<Employee> directReport;
public Employee() {
}
public Employee(String name, String department) {
this.name = name;
this.department = department;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
public Employee getReportsTo() {
return reportsTo;
}
public void setReportsTo(Employee reportsTo) {
this.reportsTo = reportsTo;
}
public Set<Employee> getDirectReport() {
return directReport;
}
public void setDirectReport(Set<Employee> directReport) {
this.directReport = directReport;
}
#Override
public int hashCode() {
return super.hashCode();
}
#Override
public boolean equals(Object obj) {
return super.equals(obj);
}
#Override
public String toString() {
return "Employee{" + "id=" + id + ", name=" + name + ", department=" + department + '}';
}
}

Please show the definition of employee!
You can also use template.createRelationshipBetween(nodeOrEntity,nodeOrEntity2,type)
For your example this should work:
public interface EmployeeRepository extends GraphRepository {
#Query("MATCH (employee:Employee), (manager:Employee)
WHERE id(employee) = {0} AND manager.name = {1}
CREATE employee-[:REPORTS_TO]->manager")
void addNewEmployee(Employee employee, String managerName);
}
perhaps better use, employee as a data-container / map for employee or just pass in the name directly:
#Query("MATCH (employee:Employee), (manager:Employee)
WHERE employee.name = {0}.name AND manager.name = {1}
CREATE employee-[:REPORTS_TO]->manager")
void addNewEmployee(Employee employee, String managerName);
}

Related

Spring Boot Hibernate CRUD REST API 404 Error

I am simply trying to create a Spring boot Hibernate CRUD REST API through this code:
EmployeController.java
#RestController
#RequestMapping("/api")
public class EmployeController {
#Autowired
private EmployeService employeService;
#GetMapping("/employe")
public List<Employe> get(){
return employeService.get();
}
}
Employe.java
#Entity
#Table(name="employe")
public class Employe {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column
private int id;
#Column
private String name;
#Column
private String gender;
#Column
private String department;
#Column
private Date dob;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
public Date getDob() {
return dob;
}
public void setDob(Date dob) {
this.dob = dob;
}
#Override
public String toString() {
return "Employe [id=" + id + ", name=" + name + ", gender=" + gender + ", department=" + department + ", dob="
+ dob + "]";
}
}
EmployeService.java
public interface EmployeService {
List<Employe> get();
Employe get(int id);
void save(Employe employe);
void delete(int id);
}
EmployeServiceImplement.java
#Service
public class EmployeServiceImplement implements EmployeService {
#Autowired
private EmployeDAO employeDAO;
#Transactional
#Override
public List<Employe> get() {
return employeDAO.get();
}
}
EmployeDAO.java
public interface EmployeDAO {
List<Employe> get();
Employe get(int id);
void save(Employe employe);
void delete(int id);
}
EmployeDAOImplement.java
#Repository
public class EmployeDAOImplement implements EmployeDAO {
#Autowired
private EntityManager entityManager;
#Override
public List<Employe> get() {
Session currentSession = entityManager.unwrap(Session.class);
Query<Employe> query = currentSession.createQuery("from Employe", Employe.class);
List<Employe>list = query.getResultList();
return list;
}
}
I have write all the configuration related to MySQl database into the application.properties and when i run this project as Spring Boot App and go to the Postman and tried like this
and i a unable to understan why it always throws 404 error every time , can anyone tell me what i am missing in this code.
Try with this GET request, it may help you:
http://localhost:8080/api
I checked your code.
where is #RestController for your Controller file and where is #RequestMapping For your method in Controller class?
maybe you should write something like this according to your need.
tell me if you need more help.
#RestController
#RequestMapping("/api")
public class EmployeController {
#RequestMapping(value = "/employ")
public void employ() {
}
}
Instead of this -
#Override
public List get()
Use this -
#RequestMapping(value = "/Employe", method = RequestMethod.GET)
public List get()

Using embedded value objects in spring data jpa projections

I'm working with Spring Boot 2.0RC2 and in the documentation I read you can return a projection of an entity instead of the entity as a whole when calling the Repository. This is working fine in case I use a String in my Entity but not when I use an embedded value objects.
Let's say I have the Product entity:
#Entity
#Table(name = "t_product")
public class Product extends BaseEntity {
#Column(nullable = false)
private String name;
private Product() {}
private Product(final String name) {
this.name = name;
}
public static Result<Product> create(#NonNull final String name) {
return Result.ok(new Product(name));
}
public String getName() {
return name;
}
public void setName(#NonNull final String name) {
this.name = name;
}
}
The BaseEntity simply holds the id, created and updated attributes.
I have my projection interface called ProductSummary:
interface ProductSummary {
String getName();
Long getNameLength();
}
And in my ProductRepository I have the following method that returns the ProductSummary:
public interface ProductRepository extends JpaRepository<Product, Long> {
#Query(value = "SELECT p.name as name, LENGTH(p.name) as nameLength FROM Product p WHERE p.id = :id")
ProductSummary findSummaryById(#Param("id") Long id);
}
This works perfectly fine. Now let's say I am doing DDD and instead of using a String to represent the name attribute in the Product entity, I want to use a value object called Name:
#Embeddable
public class Name implements Serializable {
public static final int MAX_NAME_LENGTH = 100;
#Column(nullable = false, length = Name.MAX_NAME_LENGTH)
private String value;
private Name() {}
private Name(final String value) {
this.value = value;
}
public static Result<Name> create(#NonNull final String name) {
if (name.isEmpty()) {
return Result.fail("Name cannot be empty");
}
if (name.length() > MAX_NAME_LENGTH) {
return Result.fail("Name cannot be longer than " + MAX_NAME_LENGTH + " characters");
}
return Result.ok(new Name(name));
}
public String getValue() {
return value;
}
}
I change my Product entity to:
#Entity
#Table(name = "t_product")
public class Product extends BaseEntity {
#Embedded
private Name name;
private Product() {}
private Product(final Name name) {
this.name = name;
}
public static Result<Product> create(#NonNull final Name name) {
return Result.ok(new Product(name));
}
public Name getName() {
return name;
}
public void setName(final Name name) {
this.name = name;
}
}
And in the ProductSummary I change the return type from String to Name.
When I run that I always get the exception:
Caused by: java.lang.IllegalAccessError: tried to access class com.acme.core.product.ProductSummary from class com.sun.proxy.$Proxy112
Can I make this work or am I missing some restriction which doesn't allow this?
If you wish to get the complete Name field(not a particular field in Name class), then you need to create another interface like ProductSummary.
interface ProductSummary {
NameSummary getName();
interface NameSummary {
String getValue();
}
}
No need to change anything in your repository.
It is quite clearly documented here
And make sure your interfaces and the methods are public.

Spring Data JPA ManyToOne Bidirectional

Problem: it works till i try to add Student obejcts to Database, but the tables are being created correctly .
I can't simplify the post any further. But it's mainly code that doesn't require a lot of reading, it's a simple spring data repository service model. I posted it all due to the fact idk what am i doing wrong. Problem is in the JPA mapping.
I got the example from over here http://www.java2s.com/Tutorial/Java/0355__JPA/OneToManyBidirectional.htm
MDOELS
#Entity
public class Department {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private String name;
#OneToMany(mappedBy="department")
private Collection<Student> students;
public Department() {
}
public Department(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String deptName) {
this.name = deptName;
}
public Collection<Student> getStudents() {
return students;
}
public void setStudent(Collection<Student> students) {
this.students = students;
}
public String toString() {
return "Department id: " + getId() +
", name: " + getName();
}
}
#Entity
public class Student {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private String name;
#ManyToOne (cascade=CascadeType.ALL)
private Department department;
public Student() {
}
public Student(String name, Department department) {
this.name = name;
this.department = department;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
public String toString() {
return "\n\nID:" + id + "\nName:" + name + "\n\n" + department;
}
}
REPOSITORIES
#Repository
public interface DepartmentRepository extends JpaRepository<Department, Integer> {
Department findByName(String name);
}
#Repository
public interface StudentRepository extends JpaRepository<Student, Integer> {
Student findByName(String name);
}
SERVICES
#Service
public class StudentService {
private final StudentRepository studentRepository;
#Autowired
public StudentService(StudentRepository studentRepository) {
this.studentRepository = studentRepository;
}
public void addToDatabase(Student student) {
this.studentRepository.saveAndFlush(student);
}
public Student getStudentByName(String name) {
return studentRepository.findByName(name);
}
}
#Service
public class DepartmentService {
private final DepartmentRepository departmentRepository;
#Autowired
public DepartmentService(DepartmentRepository departmentRepository) {
this.departmentRepository = departmentRepository;
}
public void addToDataBase(List<Department> department) {
this.departmentRepository.save(department);
department.forEach(this.departmentRepository::saveAndFlush);
}
public Department getDepartmentByName(String name){
return this.departmentRepository.findByName(name);
}
}
My main method
#Component
public class Terminal implements CommandLineRunner {
private final StudentService studentService;
private final DepartmentService departmentService;
#Autowired
public Terminal(StudentService studentService, DepartmentService departmentService) {
this.studentService = studentService;
this.departmentService = departmentService;
}
#Override
public void run(String... strings) throws Exception {
Department department = new Department("dep1");
Department department1 = new Department("dep2");
Department department2 = new Department("dep3");
Department department3 = new Department("dep4");
List<Department> departments = new ArrayList<>(Arrays.asList(department, department1, department2, department3));
this.departmentService.addToDataBase(departments);
//
Student student = new Student("pesho", department);
Student student11 = new Student("gosho", department1);
this.studentService.addToDatabase(student11);
this.studentService.addToDatabase(student);
student = new Student("sasho", department2);
this.studentService.addToDatabase(student);
// System.out.println(this.studentService.getStudentByName("gosho").getDepartment1());
// System.out.println("CHECKING ONE TO ONE BIDIRECTIONAL: " + this.departmentService.getDepartmentByName("dep1").getStudent());
}
}
So here when i try to add students in the students table it gives an error
The error is the fallowing
Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: app.models.Department
you added cascade= CascadeType.ALL for Department in Student class and save departments separete. this.departmentService.addToDataBase(departments);
fix : dont call
departmentService.addToDataBase(departments);
or remove CascadeType.ALL from Student
Well I can't understand you problem completely but here's what I would like to add. Cascading for add operation is not implemented or it's incomplete. Hope it helps.

How to model a one-to-one relationship in JPA when the "parent" table has a composite PK?

While there is plenty of information around on how to model, in JPA (2), a one-to-one relationship OR an entity having a natural key, I haven't been able to find a clear / simple answer to how to model the situation where we have both, i.e. a one-to-one relationship where the parent table has a natural key. It could obviously be that I might have missed such a tutorial; if so, pointing me to one could also be the answer.
And, as many times with JPA and noobs such as I, the moment one needs a bit more than the most basic model, one can quickly hit the wall.
Hence, considering the following DB model:
What would be the corresponding JPA-annotated object model? (I'm sparing you guys of the things I've tried since I don't want to influence the answer...)
Performance recommendations are also welcome (e.g. "a one-to-many could perform faster", etc.)!
Thanks,
The composite identifier is built out of two numerical columns so the mapping looks like this:
#Embeddable
public class EmployeeId implements Serializable {
private Long companyId;
private Long employeeId;
public EmployeeId() {
}
public EmployeeId(Long companyId, Long employeeId) {
this.companyId = companyId;
this.employeeId = employeeId;
}
public Long getCompanyId() {
return companyId;
}
public Long getEmployeeId() {
return employeeId;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof EmployeeId)) return false;
EmployeeId that = (EmployeeId) o;
return Objects.equals(getCompanyId(), that.getCompanyId()) &&
Objects.equals(getEmployeeId(), that.getEmployeeId());
}
#Override
public int hashCode() {
return Objects.hash(getCompanyId(), getEmployeeId());
}
}
The parent class, looks as follows:
#Entity(name = "Employee")
public static class Employee {
#EmbeddedId
private EmployeeId id;
private String name;
#OneToOne(mappedBy = "employee")
private EmployeeDetails details;
public EmployeeId getId() {
return id;
}
public void setId(EmployeeId id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public EmployeeDetails getDetails() {
return details;
}
public void setDetails(EmployeeDetails details) {
this.details = details;
}
}
And the child like this:
#Entity(name = "EmployeeDetails")
public static class EmployeeDetails {
#EmbeddedId
private EmployeeId id;
#MapsId
#OneToOne
private Employee employee;
private String details;
public EmployeeId getId() {
return id;
}
public void setId(EmployeeId id) {
this.id = id;
}
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
this.id = employee.getId();
}
public String getDetails() {
return details;
}
public void setDetails(String details) {
this.details = details;
}
}
And everything works just fine:
doInJPA(entityManager -> {
Employee employee = new Employee();
employee.setId(new EmployeeId(1L, 100L));
employee.setName("Vlad Mihalcea");
entityManager.persist(employee);
});
doInJPA(entityManager -> {
Employee employee = entityManager.find(Employee.class, new EmployeeId(1L, 100L));
EmployeeDetails employeeDetails = new EmployeeDetails();
employeeDetails.setEmployee(employee);
employeeDetails.setDetails("High-Performance Java Persistence");
entityManager.persist(employeeDetails);
});
doInJPA(entityManager -> {
EmployeeDetails employeeDetails = entityManager.find(EmployeeDetails.class, new EmployeeId(1L, 100L));
assertNotNull(employeeDetails);
});
doInJPA(entityManager -> {
Phone phone = entityManager.find(Phone.class, "012-345-6789");
assertNotNull(phone);
assertEquals(new EmployeeId(1L, 100L), phone.getEmployee().getId());
});
Code available on GitHub.

Strange behaviour of MongoDB when querying by foreign key

I am writing some test code to learn spring-data with MongoDB. I can successfully create two Documents: Person and ADocument, where ADocument contains a reference to Person.
#Document
public class Person {
#Id
private ObjectId id;
#Indexed
private String name;
public ObjectId getId() {
return id;
}
public void setId(ObjectId id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
...
#Document
public class ADocument {
#Id
private ObjectId id;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
private String title;
private String text;
#DBRef
private Person docperson;
public Person getDocperson() {
return docperson;
}
public void setDocperson(Person docperson) {
this.docperson = docperson;
}
public ObjectId getId() {
return id;
}
public void setId(ObjectId id) {
this.id = id;
}
}
The problem arises when I try to get all the 'adocuments' related to a person by using the person's ID (once the person's name is provided):
public List<ADocument> loadDocumentsByPersonName(String pname) {
Query qPerson = new Query().addCriteria(Criteria.where("name").is(pname));
qPerson.fields().include("_id");
Person pers = mongoTemplate.findOne(qPerson, Person.class);
ObjectId persId = pers.getId();
Query qDoc = new Query().addCriteria(Criteria.where("person.$id").is(persId));
System.out.println(qDoc.toString());
List<ADocument> list2 = mongoTemplate.find(qDoc, ADocument.class);
return list2;
}
Everyting works fine except that list2 is always empty (while it shouldn't).
System.out.println(qDoc.toString()) gives something like:
Query: { "person.$id" : { "$oid" : "536a0d50e4b0d0c10297f2ab"}}, Fields: null, Sort: null
If I try to issue the query above on the Mongo shell I get the following:
db.adocument.find({ "person.$id" : { "$oid" : "536a0805e4b0af174d0b5871"}})
error: {
"$err" : "Can't canonicalize query: BadValue unknown operator: $oid",
"code" : 17287
}
While if I type
db.adocument.find({ "person.$id" : ObjectId("536a0805e4b0af174d0b5871")})
I actually get a result!
I am using MongoDB 2.6.0 and Spring Data 1.4.2.
I really can't figure out what's going on... Any help is extremely appreciated!
I got it!
For some reason, I had to explicit the collection name in the Query:
List list2 = mongoTemplate.find(qDoc, ADocument.class, COLLECTION_NAME);
where COLLECTION_NAME="adocument".
As for the shell behaviour, it seems that Query.toString() does never return a correct syntax to be cut and paste for shell execution.

Categories

Resources