How to use multiple foreign keys in JPA? - jpa

I'm starting a project to know more in detail JPA.
Context:
At the end of his internship, the student has a report to make and a presentation in front of his professor to do about the internship.
I've a database, which is called "grade_management". It must contains a "student", "presentation", "report", "professor" and a "mark" (there are several rating criteria such as expression, quality of powerpoint ...) table. But now it's empty, since I want to make it throught JPA.
I've a "Presentation" class. Which countain this:
#Entity
public class Presentation implements Serializable {
#Id
#GeneratedValue (strategy=GenerationType.AUTO)
private int presentation_id;
private Date date;
private mark_id;
private int professor_id;
public Soutenance() {}
public Soutenance(Date date) {
this.date = date;
}
}
But the Presentation table contain 2 foreign key: professor_id and mark_id.
My question is: How can I indicate that both of them are foreign key ?
I'm sorry if I'm not clear, don't hesitation to ask question.
Cordially

You shouldn't reference other entities by their ID, but by a direct reference to the entity.
Something like that :
#ManyToOne
#JoinColumn(name = "mark_id", referencedColumnName = "id")
private Mark mark; // supposed here that mark_id if link to entity `Mark`
#ManyToOne
#JoinColumn(name = "professor_id", referencedColumnName = "id") // suppose "id" is the column name of the PK inside the table Professor.
private Professor professor; // supposed here that professor_id if link to entity `Professor`
This code is supposing that you use an unidirectional relation.
For bidirectional you have to define this in the other side (Mark/Professor type)
#OneToMany(mappedBy = "professor")
private Presentation presentation;

From your explanation, it looks like you have a Database named grade_management and in that database you have "student", "presentation", "report", "professor" and a "mark" tables (i.e: which are #Entity by themselves defined in their separate respective classes )
I'm not sure whether you have defined them or not. If not then you have to define them first and then use the refactored code mentioned below.
So, you will have many-to-one relation mapping. You can annotate your foreign keys belonging to different tables using #ManyToOne annotation to indicate relation type and #JoinColumn annotation to indicate that this entity has a foreign key to the referenced table.
You can redefine your Presentation class show below:
#Entity
#Table(name = "Presentation")
public class Presentation implements Serializable {
#Id
#Column(name="presentation_id")
#GeneratedValue (strategy=GenerationType.AUTO)
private int presentation_id;
private Date date;
#ManyToOne
#JoinColumn(name = "mark_id")
private Mark mark_id;
#ManyToOne
#JoinColumn(name = "professor_id")
private Professor professor_id;
public Soutenance() {}
public Soutenance(Date date) {
this.date = date;
}
//getter and setter
}
Also, if you need more information to read upon for yourself you can always checkout this Hibernate Documentation that explains everything you'll need to know.

Related

JPA: Update mapping table alone

I have a Project and Employee entities, which has ManyToMany relationship like below.
#Entity
public class Project {
#Id #GeneratedValue
private int projectId;
private String projectName;
// has some additional columns
#ManyToMany(mappedBy = "projects")
private List<Employee> emp = new ArrayList<Employee> ();
....
.....
}
#Entity
public class Employee {
#Id #GeneratedValue
private int id;
private String firstName;
private String lastName;
#ManyToMany(cascade=CascadeType.ALL)
List<Project> projects = new ArrayList<Project> ();
....
....
}
When I use above entities, JPA create a mpping table 'Employee_Project' like below.
create table Employee_Project (emp_id integer not null, projects_projectId integer not null)
My question is, whenever new employee is added, I want to update both employee table and Employee_Project mapping table only, assume I know project id that I would like to map this employee to. (without touching project table/entity, I mean why should I provide complete project object, while saving employee entity alone, how can I do this via jpa?)
You don't need to provide the entire Project object. Use EntityManager.getReference(projectId) or JpaRepository.getOne(projectId).
Those methods will create a proxy object with the appropriate id, rather than loading the entire Project entity from the data store.
EDIT Your service method should look pretty much like the following:
#Transactional
public void createEmployee(Employee employee, Long projectId) {
employee.setProjects(List.of(projectRepository.getOne(projectId));
employeeRepository.save(employee);
}
As a side note, CascadeType.ALL (in particular, because it includes CascadeType.MERGE and CascadeType.REMOVE) doesn't make sense for #ManyToMany. Unless you're planning to create a Project by creating an Employee, CascadeType.PERSIST makes no sense, either.

Can I use more than one #Id annotation in an Entity?

I want to define a table where the entire record is the primary key. The table has two columns which are references to other entities.
#Entity
public class ProtoList implements Serializable {
#Id
#ManyToOne ProtoObject listID;
#Id
#OneToOne ProtoObject po;
ProtoObject is an entity whose #Id is a regular generated Long.
The resulting relational data structure is intended allow any ProtoObject to be associated with an arbitrarily long List (actually a Set) of ProtoObjects. So the two table columns are just two Longs, always unique.
Will this work or do I have to define an #IdClass or something else?
After some experimentation I discovered that it was indeed necessary to use an #IdClass annotation. What is interesting is that in the Entity itself I have the #ManyToOne and #OneToOne annotations to create relational links to ProtoObjects, but in the IdClass the corresponding fields are inferred from the ProtoObject's own ID field.
So the result is:
#Entity
#IdClass(ProtoListKey.class)
public class ProtoList implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#ManyToOne ProtoObject listID;
#Id
#OneToOne ProtoObject po;
And the key is:
public class ProtoListKey {
private Long listID;
private Long po;
The primary key of ProtoList is Long so this works. The entire record is the primary key which is what I wanted. Lesson learned.

Jpa OneToMany with condition

I have 2 tables:
The first is "Persons":
person_id,
person_name
The second is "PersonsGraphs":
person_id1,
person_id2,
relation_type
I'm looking for a way to build a "family tree".
My first option is: load personGraphs into a HashTable and then recursively build the tree.
The second option I have come up with: use #OneToMany jpa-relation. This can work, but sometimes I have some relation_types that I want/don't want to include. Are there any options that would allow me to set some condition on the #OneToMany relation while using #JoinTable?
Thanks!
Oak
Try to use Hibernate #Where annotation, for example:
#Entity
public class Person {
#Id
#GeneratedValue
private Integer id;
private String name;
#Enumerated(EnumType.STRING)
private Gender gender;
#ManyToOne
private Person parent;
#Where(clause = "gender = 'MALE'")
#OneToMany(mappedBy = "person")
private List<Person> sons;
#Where(clause = "gender = 'FEMALE'")
#OneToMany(mappedBy = "person")
private List<Person> daughters;
}
public enum Gender {
MALE, FEMALE
}
I would recommend creating a Relationship class to model the join table.
Person - OneToMany - relations - Relationship - ManyToOne source - ManyToOne target
In EclipseLink you can add an Expression criteria to any relationship mapping,
http://wiki.eclipse.org/EclipseLink/Examples/JPA/MappingSelectionCriteria

JPA Relationship Mapping Concept

I have several questions on bidirectional mapping.
I have these entities:
Employee(1) - (1) Parking_Lot
Employee(*) - (1) Department
Employee(*) - (1) Project
What is the source and target entity for the above relationship?
What is the owner for ManyToOne relationship. I wonder the owner is
on Many entity or One entity?
Do mappedBy specify on owner side or inverse side?
Please help.
EDIT:
I have the following table:
Project - Proj_Client - Client (Many to Many relationship) and persist the project table but the client is not get persist. What wrong with it?
Thanks.
#Entity
#Table(name="empoyee")
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Integer id;
#ManyToOne
#JoinColumn(name="department_id", referencedColumnName="id")
private Department department;
#ManyToOne
#JoinColumn(name="project_id", referencedColumnName="id")
private Project projects;
#OneToOne(mappedBy="employee")
private ParkingLot parkingLot;
//Other properties,constructors, getters and setters and so on
}
#Entity
#Table(name="department")
public class Department implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#OneToMany(mappedBy="department")
private List<Employee> employees;
//Other properties,constructors, getters and setters and so on}
#Entity
#Table(name="parking_lot")
public class ParkingLot implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#OneToOne
#JoinColumn(name="employee_id",referencedColumnName="id")
private Employee employee;
//Other properties,constructors, getters and setters and so on}
#Entity
#Table(name="project")
public class Project implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#OneToMany(mappedBy="project")
private List<Employee> employees;
//Other properties,constructors, getters and setters and so on
}
If the relationship is unidirectional there really isn't an owning side, and there isn't any mappedBy annotations.
If the relationship is bidirectional there is a side with the mappedBy annotation - the other side is the owning side.
The owning side is the side that owns the relationship. So the term is not ment to be applied like a ParkingLot owns its Employee or an Employee owns its ParkingLot, but rather like the relationship between ParkingLot and Employee is owned by the Employee (or ParkingLot see below).
For ManyToOne there is no mappedBy, so it is always the entity specified under the OneToMany annotation that owns the relationship (makes sense, since the for example the project table can't have foreign keys to all its employees)
So for the two ManyToOne/OneToMany mappings in your example we don't have a choice in which side owns the relationship. Between Employee and ParkingLot we have a choice, I choosed ParkingLot.
When given a choice, what does it matter? Well, the main difference is that the mappedBy have the porperty cascade. Please note that it doesn't matter which table have the foreign key, or if the relationship is in its own table - JPA supports all cases (#InverseJoinColumn etc).
For bidirectional mappings there isn't a clear target and source for the mapping, it depends on which way you at the mapping from. The term is more applicable to unidirectional mappings, and there the source side is of course the entity with the mapping (that is possible knowledge of the target entity)
4) Not applicable (unless you make the relationship between ParkingLot and Employee unidirectional).
5) The owner of the relationship is always "on the One entity"
6) inverse side
Final note:
"owning side" is confusing, for example we could design so that a Department owns its Employees and if we delete a Department all its employees would also be deleted. This would be done by changing #OneToMany(mappedBy="department") into #OneToMany(mappedBy="department", cascade= CascadeType.REMOVE) then it would really make sense to say "the Department entity owns its Employee entities" but the relationship would still be owned by the Employee entity.

Single Table Inheritance Query

i have an existing table for TransactionLogs which is either links to a External or to a InternalType. the id's corresponding to the cash adjustment & game transaction are stored in a single column called transaction id and a separate column called type indicates which table is it linked to
Because of the nature of the existing table, i mapped it in a single table inheritance:
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "TYPE", discriminatorType = DiscriminatorType.INTEGER)
public class TransLog implements Serializable {
#Id
#GeneratedValue
private Long id;
private Integer type;
// getters and setters
}
#Entity
public class InternalAdjustmentTransLog extends TransLog {
#ManyToOne
#JoinColumn(name = "TransID", nullable = false)
private InternalAdjustmentRecord internalAdjustmentRecord;
// getters and setters
}
#Entity
public class ExternalTransLog extends TransLog {
#ManyToOne
#JoinColumn(name = "TransID", nullable = false)
private ExternalAdjustmentRecord externalAdjustmentRecord;
}
each of these two subclasses has their subclasses with defined descriminator values..
With the setup given above, there are instances that i need to get a unified data of both
internal and external records. What is the best way to accomplish this? at first i thought it would be enough to use the TransLog as the root class for the query (i'm using jpa criteria). however, i need to get TransId (which are defined in the subclasses and points to 2 different objects of no relationship).
Thanks.
You can make abstract method in TransLog that returns what you need and implement it in both subclasses.