Parent - Child relationship - self join mapping - java

I'm trying to build an application which will receive the XML file with list of Employees and store the parent-child/employee-manager relation in the single database table.
My XML file looks like this one:
<Employees>
<Employee manager="Patrick">Martin</Employee>
<Employee manager="Patrick">Kent</Employee>
<Employee manager="Martin">Mark</Employee>
<Employee>Hugo</Employee> <!-- root element of the employee-manager tree -->
<Employee manager="Hugo">Osa</Employee>
<Employee manager="Osa">Patrick</Employee>
</Employee>
One employee can have only one manager, but one manager can have multiple subordinates/employees.
I have no troubles when unmarshalling the received XML file but now I'm trying to create the appropriate model which will allow me to store the unmarshalled values in the database. Data should be stored in the table named "Employee" and should contain following data:
------------------------------
| id | Integer |
------------------------------
| employee_name | String |
------------------------------
| parent_id | Integer | -- reference to the manager
------------------------------
I created a new class named Employee but I'm not sure how to define appropriate ManyToOne/OneToMany annotations.
Since I'm fairly new to this, I've Googled couple of examples and tutorials (as well as the answers on the similar questions here on Stack Overflow), but I guess I'm making some big mistake in this implementation when defining this model. My latest try looks like this:
public class Employee {
#Id
#GeneratedValue
private int id;
#Column(name = "parent_id")
#Transient
#ManyToOne(cascade={CascadeType.ALL})
private String managerName;
#Column(name = "employee_name")
#JoinColumn(name="parent_id")
private String employeeName;
// getters and setters
If anyone could point me in the the direction of defining appropriate model, it would be much, much appreciated!

In Hibernate when you want to map a ManyToOne relationship you map it between entities and not just properties, so you need to reference an object of type Employee and not only a String or an id.
Problems:
So your mapping is incorrect and will throw many mapping errors,
instead of writing:
#Column(name = "parent_id")
#Transient
#ManyToOne(cascade={CascadeType.ALL})
private String managerName;
You need to map the ManyToOne realtionship like this:
#ManyToOne(cascade={CascadeType.ALL})
#JoinColumn(name="manager_id")
private Employee manager;
And make sure you map the other side of the relationship like this:
#OneToMany(mappedBy="manager")
private Set<Employee> subordinates = new HashSet<Employee>();
Also your mapping for the column employee_name is incorrect, the
#JoinColumn is only used for relationships and can't be used with a
simple column, you need to write it like this:
#Column(name = "employee_name")
private String employeeName;
The #Transient is useless in your mapping, we only use it if we
want to use an attribute that won't be persisted in the database.
And most important make sure you map your class with #Entity, so it can be
persisted in the database.
Example:
You can check Hibernate Self Join Annotations One To Many mapping example it uses the same model you want to implement.

You should simply have a ManyToOne relation to your Employee table, that is several employees can have the same manager (who is also an employee) and for the manager this field will remain empty, like this:
#Entity
#Table(name = "EMPLOYEE")
public class Employee {
#Id
#GeneratedValue
private int id;
#ManyToOne
private Employee manager;
#Column(name = "employee_name")
private String employeeName;

Related

How to separate data into two tables with Spring Boot?

Now I have two tables, the first table called StudentBase and has three columns: id, firstname and lastname. The second table called ResearchAssistant and has two columns: id and course. I designed the tables like this because there are different kinds of students and research assistant is one of them. The two table could be joint together with the primary key id.
I'm writing an endpoint /researchAssistant and take following content as request body of POST method.
{
"firstname":"Jack",
"lastname":"Peter",
"course":"MATH"
}
What I want is that saving firstname and lastname into StudentBase table and save course into ResearchAssistant table. And generate a same id for both.
The first idea comes to my mind is building 3 model classes: StudentBase(id, firstname, lastname), ResearchAssistant(id, course) and ResearchAssistantMixed(firstname, lastname, course). I use ResearchAssistantMixed class as the request body class. After getting the data I will seperate it into a new StudentBase object and a ResearchAssistant object, then I store them seperately.
This process seems really stupid and the performance should be quite low. Do you have some better ideas? How does Spring Boot deal with such cases? Thank you!
This is a database problem and not a spring-boot problem. This is how I would approach (I'm assuming you're using some relational DB like MySql and hibernate for ORM):
Database Tables:
student_base
- id (primary key)
- first_name
_ last_name
research_assistant
- id (primary key)
- student_base_id (foreign key referencing id of student_base)
- course
You can now have equivalent entity classes in Java (for hibernate):
#Entity
#Table(name = "student_base")
public class StudentBase {
#Id
#Column(name = "id")
private Integer id;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
// getters and setters
}
#Entity
#Table(name = "research_assistant")
public class ResearchAssistant {
#Id
#Column(name = "id")
private Integer id;
#ManyToOne(optional = false)
#JoinColumn(name = "student_base_id")
private StudentBase studentBase;
#Column(name = "course")
private String course;
// getters and setters
}
Now in your DAOs, you don't need to do much, just persist a student_base record and use the returned object to persist a research_assistant record. For example:
StudentBase studentBase = persist(new StudentBase(1, "abc", "xyz");
persist(new ResearchAssistant(1, studentBase, "pqr");
You can (and should) have two separate classes to accept the request object of the post API (don't use entity classes to accept request data).

Neo4j - Unable to create Relationship entities

I am trying to insert the relationships between two nodes in Neo4j. I am using the Neo4J(2.1.8 Community) & spring-data-neo4j(3.3.0.RELEASE).
I am using trying to create the Employee-Manager relationship. This relationship entity is Report. (Both class are given below)
But when I am trying to save the relation ship
Employee manager = new Employee("Dev_manager", "Management");
Employee developer = new Employee("Developer", "Development");
developer.setReportsTo(manager);
developer.relatedTo(manager, "REPORTS_TO")
employeeRepository.save(developer);
I am getting exception as
Exception in thread "main" org.springframework.dao.DataRetrievalFailureException: RELATIONSHIP[0] has no property with propertyKey="type".; nested exception is org.neo4j.graphdb.NotFoundException: RELATIONSHIP[0] has no property with propertyKey="type".
Can any one please help me that what is exactly wrong in this code.
The same code works after I change the type of relations in Employee as
#RelatedToVia(type = "REPORT_TO", elementClass = Report.class, direction = Direction.INCOMING)
Note: I am using this reference for this tutorial.
Employee.java class
#NodeEntity
public class Employee {
#GraphId
private Long id;
private String name;
private String department;
#Fetch
#RelatedTo(type = "REPORTS_TO")
private Employee reportsTo; //Employee reports to some employee (i.e. Manager).
#Fetch
#RelatedTo(type = "REPORTS_TO", direction = Direction.INCOMING)
Set<Employee> directReport; //All the employees who reports to this particular this employee.
#Fetch
#RelatedToVia(type = "REPORTS_TO", elementClass = Report.class, direction = Direction.INCOMING)
Set<Report> relations = new HashSet<Report>(); // All the incoming relationship entities.
//*** Constructor, Getter-setters and other methods...
}
Report.java class
#RelationshipEntity(type = "REPORTS_TO")
public class Report {
#GraphId
private Long id;
private String title;
#Fetch
#StartNode
private Employee child;
#Fetch
#EndNode
private Employee parent;
//*** Constructor, Getter-setters and other methods...
}
**Update: **
I have created 2 relations using this class structure. And I got the below result.
It looks like it creates 2 relations between the node. 1 is empty relation using reportsTo(i.e. REPORTS_TO) and another relation using the relations(i.e. REPORT_TO). Can anyone please update why this is happening?
What's the different between: relations and directReport?
I think SDN is just confused with all the duplicate listing of relationships?
Esp. if they are once declared as light relationships without type and once as relationship-entities.
I think for this case it is much clearer and easier to use
template.createRelationshipBetween(employee, manager, "REPORTS_TO");
Or just create, populate and save the relationship-entity Report.
Otherwise you have to make sure that all collections on all sides are consistent with each other.

How to define unidirectional OneToMany relationship in JPA

I have a following problem with entity mapping in JPA. I have two entities, first one is Lookup and the second is Text which represents translations for entities. Now I need to bound Lookup to the Text but I don't want Text to have reference to Lookup. To make this more complicated, Text does not use its primary key in this relationship but a metacode defined in a TXTHEAD_CODE column.
Lookup.java
#Entity
#Table(name = "DATREG")
public class Lookup implements PersistableEntity {
#Id
#Column(name = "DATREG_META_CODE")
private String metaCode;
#OneToMany
#JoinTable(name="TXT",
joinColumns=#JoinColumn(name="DATREG_META_CODE", referencedColumnName="TXTHEAD_CODE"),
inverseJoinColumns=#JoinColumn(name="DATREG_META_CODE"))
private List<Text> text;
Text.java
#Entity
#Table(name = "TXT")
public class Text {
#Id
#Column(name = "TXT_ID")
private Long id;
#Column(name = "TXTHEAD_CODE")
private String code;
So I have tried this (and few other variations) but with no result. I also can't create join table in the DB and I don't want bound Lookup to my Text class. So can anyone please tell me if there is some other way?
My bible for JPA work is the Java Persistence wikibook. It has a section on unidirectional OneToMany which explains how to do this with a #JoinColumn annotation. In your case, i think you would want:
#OneToMany
#JoinColumn(name="TXTHEAD_CODE")
private Set<Text> text;
I've used a Set rather than a List, because the data itself is not ordered.
The above is using a defaulted referencedColumnName, unlike the example in the wikibook. If that doesn't work, try an explicit one:
#OneToMany
#JoinColumn(name="TXTHEAD_CODE", referencedColumnName="DATREG_META_CODE")
private Set<Text> text;

Hibernate and JPA: how to make a foreign key constraint on a String

I am using Hibernate and JPA. If I have two simple entities:
#Entity
#Table(name = "container")
public class Container {
#Id
#Column(name="guid")
private String guid;
}
#Entity
#Table(name="item")
public class Item {
#Id
#Column(name="guid")
private String guid;
#Column(name="container_guid")
private String containerGuid;
}
and I want to insure that inserting an Item fails if the referenced Container does not exist. I would prefer not to have a Container object populated inside the item object (ManyToOne), how would I do this if it is possible to do?
You can declare arbitrary constraint using columnDefinition attribute:
#Column(name="container_guid",
columnDefinition = "VARCHAR(255) REFERENCES container(guid)")
private String containerGuid;
Note, however, that Hibernate doesn't know anything about this constraint, so that, for example, it may not perform inserts in proper order with respect of it and so on.
Therefore it would be better to create a #ManyToOne relationship. If you are afraid of extra SQL query for Container needed to set this property, you can use Session.load()/EntityManager.getReference() to get a proxy without issuing actulal query.
Try using below relationship mapping
RelationShip Mapping
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#ManyToOne()
#ManyToMany()
<>
#JoinColumn(name="<>")

Joining tables with composite keys in a legacy system in hibernate

I'm currently trying to create a pair of Hibernate annotated classes to load (read only) from a pair of tables in a legacy system. The legacy system uses a consistent (if somewhat dated) approach to keying tables. The tables I'm attempting to map are as follows:
Customer CustomerAddress
-------------------------- ----------------------------
customerNumber:string (pk) customerNumber:string (pk_1)
name:string sequenceNumber:int (pk_2)
street:string
postalCode:string
I've approached this by creating a CustomerAddress class like this:
#Entity
#Table(name="CustomerAddress")
#IdClass(CustomerAddressKey.class)
public class CustomerAddress {
#Id
#AttributeOverrides({
#AttributeOverride(name = "customerNumber", column = #Column(name="customerNumber")),
#AttributeOverride(name = "sequenceNumber", column = #Column(name="sequenceNumber"))
})
private String customerNumber;
private int sequenceNumber;
private String name;
private String postalCode;
...
}
Where the CustomerAddressKey class is a simple Serializable object with the two key fields. The Customer object is then defined as:
#Entity
#Table(name = "Customer")
public class Customer {
private String customerNumber;
private List<CustomerAddress> addresses = new ArrayList<CustomerAddress>();
private String name;
...
}
So, my question is: how do I express the OneToMany relationship on the Customer table?
I may have an answer for my own question. Add the following to Customer:
#OneToMany(mappedBy="customer")
#JoinColumn(name="customerNumber")
List<CustomerAddress> addresses = new ArrayList<CustomerAddress>();
And the following to CustomerAddress:
#ManyToOne
#JoinColumn(name="customerNumber")
protected Customer customer;
This turns out to be a simpler problem to solve than I first thought. Typical.
I assume you have read the Bauer/King Hibernate book, which is extremely bad at explaining how do implement composite primary keys correctly. Don't let yourself be fooled by a bad book: composite primary key are not a property of legacy systems...

Categories

Resources