composite primary key which contains a foreign key - jpa

I have an entity called UserWithRoles:
#Entity
#Getter
#Setter
public class UserWithRoles implements Serializable
{
#Id
#GeneratedValue( strategy = GenerationType.AUTO )
private int id;
private String name;
private String password;
#OneToMany( mappedBy = "user" )
private List<UserRole> roles;
}
A UserRole entity:
#Entity
#Getter
#Setter
#IdClass( UserRolePK.class )
#Inheritance( strategy = InheritanceType.JOINED )
#DiscriminatorColumn( name = "roleType", discriminatorType = DiscriminatorType.STRING, length = 10 )
abstract public class UserRole implements Serializable
{
#Id
// It should be mapped as a foreign PK by user.id (user field declared below)
private int userID;
#Id
private String roleType;
#ManyToOne
#JoinColumn( name="user_id", referencedColumnName = "id" )
private UserWithRoles user;
}
The primary key class UserRolePK:
#Data
public class UserRolePK implements Serializable
{
private int userID;
private String roleType;
}
I want to create a composite PK to UserRole: UserWithRoles.id + UserRole.roleType
How can I map it to the database? Should I use the UserWithRoles type in the PK class instead of the ID? Is it a good idea at all? Or I just should use normal PK to UserRole? The relation would be something like that between the ClientOrder and ClientOrdetItem entities: (ClientOrder.id + ClientOrderItem.num)

You are using Derived Identity.
You need to change UserRole to look like this:
#Entity
#Getter
#Setter
#IdClass( UserRolePK.class )
#Inheritance( strategy = InheritanceType.JOINED )
#DiscriminatorColumn( name = "roleType", discriminatorType = DiscriminatorType.STRING, length = 10 )
abstract public class UserRole implements Serializable
{
#Id
private String roleType;
#Id
#ManyToOne
#JoinColumn( name="user_id", referencedColumnName = "id" )
private UserWithRoles user;
}
That is, get rid of the userID field and add an #Id annotation to the user field.
And change UserRolePK to look like this:
#Data
public class UserRolePK implements Serializable
{
private int user;
private String roleType;
}
That is, change the name of the userID field to user, to match the name of the #Id field in UserRole (but its type must still match the type of the UserWithRoles PK field, id).
Derived identity is discussed in JPA 2.1 spec, section 2.4.1.

Related

#OneToOne Composite Primary Key of Entities

I need help for this case.
I have the following entities (I removed getters/setters/hash/toString for easy reading):
#Entity
public class Company implements Serializable{
#Id
private String id;
}
#Entity
public class Document implements Serializable{
#Id
private String id;
}
#Entity
#IdClass(Inbox.PK.class)
public class Inbox implements Serializable {
#Id
#ManyToOne(fetch = FetchType.LAZY)
private Company company;
#Id
#ManyToOne(fetch = FetchType.LAZY)
private Document document;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "inbox")
private Invoice invoice;
public class PK implements Serializable{
private Company company;
private Document document;
}
}
First question is, should I use Company and Document types in PK class or String and String?
And here ... the headache :
#Entity
#IdClass(Invoice.PK.class)
public class Invoice implements Serializable {
#Id
#OneToOne(fetch = FetchType.LAZY, mappedBy = "invoice")
// #MapsId // ???
#JoinColumn(name = "companyId")//, referencedColumnName = "company")// ???
#JoinColumn(name = "documentId")//, referencedColumnName = "document")// ???
// #PrimaryKeyJoinColumn // ????
private Inbox inbox;
#Data
public static class PK implements Serializable {
// private Inbox inbox; // ???
// private String company,document; // ???
// private String companyId,documentId; // ???
// private String inboxCompanyId,inboxDocumentId; // ???
}
}
The PK of the Invoice Entity is also the FK to Inbox (I would like constraints to be generated), and the PK of Inbox is composed of two Entities (Company and Document).
I prefer to use IdClass rather EmbeddedId.
How could I configure Invoice to have, at the end, (company_id,document_id) as PK AND FK to Inbox?
I saw your question posted in upwork. I think you should use string + string type fields with #Id and #Column annotations in PK class.

JPA composite Foreign Key part of composite Primary Key unable to find ID

We have tables,
'Lin_Code_Groups' with fields,
Project_ID (PK),
CG_ID(PK),
CG_Name
Corresponding entity class,
public class Lin_Code_Groups implements Serializable {
#EmbeddedId
private LinCodeGroupPK pk;
private String CG_name;
#Embeddable
public static class LinCodeGroupPK implements Serializable {
private Integer Project_ID;
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer CG_ID;
}
#OneToMany(mappedBy = "lin_Code_Groups")
private List<Lin_CG_Params> lin_CG_Params;
}
table Lin_CG_Params with fields,
Project_ID (PK)..FK to Lin_Code_Groups,
CG_ID(PK)...FK to Lin_Code_Groups,
Param_name(PK),
Param_value
Corresponding entity class,
public class Lin_CG_Params implements Serializable {
#EmbeddedId
private LinCodeGroupParamPK pk;
private String Param_value;
#Embeddable
public static class LinCodeGroupParamPK implements Serializable {
private String Param_name;
private LinCodeGroupPK linCodeGroupPK;
}
#MapsId("linCodeGroupPK")
#ManyToOne
#JoinColumns( {
#JoinColumn(name = "Project_ID",referencedColumnName= "Project_ID"),
#JoinColumn(name = "CG_ID",referencedColumnName= "CG_ID")
})
private Lin_Code_Groups lin_Code_Groups;
}
in controller class, i am using JPA's .Save method to save the data in to the tables.
#PostMapping(value = {"/hello"}, consumes = "application/json", produces = "application/json")
public ResponseEntity<Object> saveNewCodeGroupsDetails(#RequestBody Lin_Code_Groups objLin_Code_Groups ) {
respository.save(objLin_Code_Groups);
}
but getting an error 'Unable to find Lin_CG_Params with id Lin_CG_Params.LinCodeGroupParamPK'
Can anyone is please help ?

JPA Get an entity by intermediate entity

I have 3 entities named Student, Course, and StudentCourse as follows
#Entity
#Table(name = "student")
public class Student {
#Id
#GeneratedValue
private Integer id;
private String fullName;
}
#Entity
#Table(name = "course")
public class Course {
#Id
#GeneratedValue
private Integer id;
private String courseName;
}
#Entity
#Table(name = "student_course")
public class StudeCourse {
#Id
#GeneratedValue
private Integer studentId;
private Integer courseId;
private String extraColumn;
}
Restrictions: There are a couple of restrictions
One student can have only one course or no course at all
An extra entity (StudentCourse) is required to hold the relation with primary key as studentId only
StudentCourse is required and hence cannot be skipped
Get Student with Course entity if there is one registered
Help required in some magical code to retrieve Course of Student if there is one assigned.
#Entity
#Table(name = "student")
public class Student {
#Id
#GeneratedValue
private Integer id;
private String fullName;
// this is not correct code but just what I want
#JoinEntity(entity=StudentCourse, column="courseId")
private Course course;
}
StudentCourse is required and hence cannot be skipped
Ok, lets work with that.
One student can have only one course or no course at all
Implies that there is a #OneToOne relationship between Student and StudentCourse.
With the given information, the following entity model will work:
#Entity
#Table(name = "student")
public class Student {
#Column(name = "id")
#Id
#GeneratedValue
private Integer id;
#Column(name = "full_name")
private String full_name;
#OneToOne
#PrimaryKeyJoinColumn
private StudentCourse studentCourse;
}
#Entity
#Table(name = "student_course")
public class StudentCourse {
#Column(name = "id")
#Id
#GeneratedValue
private Integer id;
#JoinColumn(name = "id", nullable = false, updatable = false)
#MapsId
#OneToOne
private Student student;
#JoinColumn(name = "course_id")
#ManyToOne
private Course course;
...
}
A quick review:
#OneToOne on the Student.studentCourse field signifies that for every Student, there can be only one StudentCourse, and no more.
#PrimaryKeyJoinColumn on the Student.studentCourse field signifies that the value of the primary key column for Student should be used as the foreign key for the related entity, that is, StudentCourse.
#OneToOne on the StudentCourse.student field signifies that for every StudentCourse, there can be only one Student.
#MapsId on the StudentCourse.student field signifies that the primary key column for StudentCourse should be used as the join column for the association.
To check if a student has a course assigned, simply check if student.getStudentCourse() != null and then get the assigned course as student.getStudentCourse().getCourse().

using #Embedabble with a foreign key and manyToMany relation

I wrote an example for the code i am trying to implement, i get an error with Constraint "Student_Teacher_FK" already exists.
the #embiddable class has a foreign key that is created twice with current code.
#Entity
public class Teacher {
#Id
#GeneratedValue
private Long id;
#Column(name = "Name")
private String name;
}
#Entity
public class Student{
#Id
#GeneratedValue
private Long id;
#Column(name = "Name")
private String name;
}
#Embeddable
public class StudentList implements Serializable {
#ManyToMany
#JoinTable(name = "Student_Teacher",
joinColumns =
#JoinColumn(name = "Student_ID", referencedColumnName = "ID"),
inverseJoinColumns =
#JoinColumn(name = "Teacher_ID", referencedColumnName = "ID")
)
#ForeignKey(name = "Student_Teacher_FK", inverseName = "Teacher_Student_FK")
public List<Student> studentList = new ArrayList<Student>();
}
#Entity
public class HistoryTeacher extends Teacher {
#Embedded
#NotNull
private StudentList StudentList = new StudentList ();
}
#Entity
public class LangTeacher extends Teacher {
#Embedded
#NotNull
private StudentList StudentList = new StudentList ();
}
#Entity
public class RetiredTeacher extends Teacher {
// has no students
}
#embeddable : Defines a class whose instances are stored as an intrinsic part of an owning entity and share the identity of the entity (http://docs.oracle.com/javaee/6/api/javax/persistence/Embeddable.html)
As you are declaring it in 2 different entity, jpa will create associated association table (student-teacher) 2 times with associated fk, which is explicitely named, and so created 2 times too with the same name. Here is your error.
I don't think using #embeddable is appropriated for what you're intending to do. A student has is own existence and is not part of teacher itself (not an uml composition / black diamond) so it's not an embeddable entity. Student list should be held by teacher entity using a simple manyToMany association.

Mapping JPA entity relationships

I would like some advice on how to best layout my JPA entity classes. Suppose I have 2 tables I would like to model as entities, user and role.
Create Table users(user_id primary key,
role_id integer not null )
Create table role(role_id primary key,
description text,
)
I create the following two JPA Entities:
#Entity
#Table(name="users")
#Access(AccessType.PROPERTY)
public class User implements Serializable {
private Long userId;
private Long roleId;
private Role role;
#Column(name = "user_id")
#Id
public Long getUserId() {}
#Column(name = "role_id")
public Long getRoleId() {}
#ManyToOne()
JoinColumn(name="role_id")
public Role getRole() {}
}
Role Entity:
#Entity
#Table(name="Role")
#Access(AccessType.PROPERTY)
public class Role implements Serializable {
private String description;
private Long roleId;
#Column(name = "role_id")
#Id
public Long getRoleId() {}
#Column(name = "description")
public Long getDescrition(){}
#ManyToOne()
#JoinColumn(name="role_id")
public Role getRole() {}
}
Would the correct way to model this relationship be as above, or would I drop the private Long roleId; in Users? Any advice welcomed.
When I map it this way, I receive the following error:
org.hibernate.MappingException: Repeated column in mapping for entity:
Yes, you would drop the private Long roleId mapping when you have a #ManyToOne on the same column.
As the error implies, you can only map each column in an #Entity once. Since role_id is the #JoinColumn for the #ManyToOne reference, you cannot also map it as a property.
You can, however, add a convenience method to return the role ID, like
public Long getRoleId() {
return role.getId();
}