How to give multiple columns as primary key in entity - JPA - spring-boot

I have 3 entities - Course, Module, Timeline
In the timeline entity - I want to give 2 keys as primary key i.e Composite Key. How am I supposed to give that. Please tell me about the changes that are to be done in the code below:
Course:
#Id
#Column(name = "id")
Integer courseId;
#Column(name = "course_name")
String course_name;
Module:
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "module_id")
Integer module_id;
#Column(name = "module_type")
String module_type;
#Column(name = "module_name")
String module_name;
#Column(name = "duration")
Integer duration;
#OneToOne( cascade = CascadeType.ALL)
private Course course;
Timeline:
#Id
#Column(name = "timeline_id")
Integer timeline_id;
#ManyToOne( cascade=CascadeType.ALL )
private Module module;
#ManyToOne( cascade = CascadeType.ALL)
private Course course;
Now here in timeline, I want to have course_id and timeline_id as primary keys. Please help.
Thank you in advance.
Update:
I tried using Embeddable and EmbeddedId:
#Embeddable
public class TimelineId implements Serializable{
private Integer course_id;
private Integer timelineId;
getters and setters
hashcode and equals
}
Module:
#Entity
#Table (name = "timeline")
public class Timeline {
#EmbeddedId
private TimelineId timelinepk;
#ManyToOne( cascade=CascadeType.ALL )
private Module module;
#ManyToOne( cascade = CascadeType.ALL)
private Course course;
}
But this gives an error :
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: No identifier specified for entity: com.scb.axess.playbook.model.Timeline
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1762) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:593) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) ~[spring-beans-5.1.5.RELEASE.jar:5.1.5.RELEASE]

There are multiple possibilities to solve your problem:
Possibility 1: Using IdClass
Defining the IdClass type
This class has to implement the Serializable interface and the equals(..) and hashCode() methods. The class holds the parts of the composite primary key.
public class TimelineId implements Serializable {
private Integer timelineId;
private Integer courseId;
// getters & setters
#Override
public int hashCode() {
// your impl of hashCode
}
#Override
public boolean equals(Object obj) {
// your impl of equals
}
}
Modify your Timeline class
Here the #IdClass annotation is added to the entity class. Further, the class holds the same fields like the IdClass type (name and type should be identical), but annotated with #Id.
#Entity
#IdClass(TimelineId.class)
public class Timeline {
#Id
#Column(name = "timeline_id")
private Integer timelineId;
#Id
#Column(name = "course_id")
private Integer courseId;
#ManyToOne
#JoinColumn(name = "module_id")
private Module module;
// getters & setters
}
Possibility 2: Using EmbeddedId
Defining the EmbeddedId type
This class also holds the parts of the composite primary key.
#Embeddable
public class TimelineId {
#Column(name = "timeline_id")
private Integer timelineId;
#Column(name = "course_id")
private Integer courseId;
// getters & setters
}
Modify your Timeline class
In this case the single parts of the composite primary key can be omitted. Only a field of the embedded key type annotated with #EmbeddedId is defined.
#Entity
public class Timeline {
#EmbeddedId
private TimelineId timelineId;
#ManyToOne
#JoinColumn(name = "module_id")
private Module module;
// getters & setters
}
In both cases the corresponding repositories should be defined like this (TimelineId has to be used for parameter type ID) (here, JpaRepository is used):
public interface TimelineRepository extends JpaRepository<Timeline, TimelineId> {}
**Possibility 3: Don't use a composite PK, but make the columns unique**
Modify your Timeline class
#Entity
#Table(uniqueConstraints = {
#UniqueConstraint(columnNames = {
"course_id", "module_id"
})
})
public class Timeline {
#Id
#Column(name = "timeline_id")
Integer timeline_id;
#ManyToOne( cascade=CascadeType.ALL)
#JoinColumn(name = "module_id)
private Module module;
#ManyToOne( cascade = CascadeType.ALL)
#JoinColumn(name = "course_id)
private Course course;
// getters & setters
}

Related

Composite Primary Key and #OneToOne JPA - Hibernate

My entities are as follows
Office
#Entity
public class Office {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private int latitude;
private int longitude;
private String buildingName;
// getters and setters omitted for brevity
}
Point - which is an EmbeddedId
#Embeddable
public class Point implements Serializable {
private int latitude;
private int longitude;
// getters and setters omitted for brevity
}
Location
#Entity
public class Location {
#EmbeddedId
private Point point;
private String country;
#OneToOne // How to add a OneToOne Mapping
private Office office;
// getters and setters omitted for brevity
}
Question:
I want to add a one-to-one mapping between the Office and the Location entity.
What I've tried:
I added the following annotations along with #OneToOne mapping in the Location entity
#JoinColumn(name = "latitude", referencedColumnName = "latitude")
#JoinColumn(name = "longitude", referencedColumnName = "longitude")
but I got the following error
Provided id of the wrong type for class com.example.demo.entity.employee.o2m_cpk.Office. Expected: class java.lang.Integer, got class com.example.demo.entity.employee.o2m_cpk.Point
Use the following mappings:
#Entity
public class Office {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#OneToOne(mappedBy = "office")
private Location location;
private String buildingName;
}
#Embeddable
public class Point implements Serializable {
private int latitude;
private int longitude;
}
#Entity
public class Location {
#EmbeddedId
private Point point;
private String country;
#OneToOne
private Office office;
}

Composite primary key and ManyToOne hibernate

I'm learning Hibernate and I'm trying to make the Mapping work. My entities are as follows
Department:
#Entity
public class Department {
#Id
#GeneratedValue
private Integer id;
private String name;
private String hqLocation;
// getters and setters omitted for brevity
}
WorkerId:
#Embeddable
public class WorkerId implements Serializable {
private Integer workerId;
private Integer deptId;
// getters and setters omitted for brevity
}
Worker:
#Entity
public class Worker {
#EmbeddedId
private WorkerId id;
private String name;
private Integer age;
// How to make #ManyToOne mapping work?
private Department department;
// getters and setters omitted for brevity
}
Question: How to make #ManyToOne on the field private Department department; work? Simply adding the annotation results private Department department; as null.
I think you want to use a "derived identity"; so you should make Worker.department like this:
#Entity
public class Worker {
#EmbeddedId
private WorkerId id;
private String name;
private Integer age;
#MapsId("deptId")
#ManyToOne // <<<< edit
private Department department;
// getters and setters omitted for brevity
}
Derived identities are discussed (with examples) in the JPA 2.2 spec in section 2.4.1.
Question really is, which entity should be the owner of the relationship? would you like to map bidirectional or single way?
Here is the bidirectional example
#OneToMany(
fetch = FetchType.EAGER,
mappedBy = "department",
orphanRemoval = true,
cascade = CascadeType.ALL)
private List<Worker> workers;
#JoinColumn(name = "department_id", nullable = false)
#ManyToOne(targetEntity = Department.class, fetch = FetchType.LAZY)
private Department department;
fetch types are optional and depend on the use case

#OnetoMany entity in #ElementCollection

I have 2 entities and 1 embeddable object :
#Entity
class CourseDetails extends Course {
#Id
Integer id;
#ElementCollection
#CollectionTable(name = "course_section", joinColumns = #JoinColumn(name = "courseId"), foreignKey = #ForeignKey(name = "course_section_fk"))
private List<CourseSection> courseSection;
}
#Embeddable
public class CourseSection extends BaseBo {
#OneToMany
#JoinColumn(name="contentId")
private Set<CourseContent> courseContent = new HashSet<>();
}
#Entity
public class CourseContent {
private static final long serialVersionUID = 1856738483334146418L;
#Id
private Integer contentId;
private String contentSummary;
}
I want to store coursesection as an embedded object of course and course_section should contain reference of course_content. I tried the above structure but it gives error :
#ElementCollection cannot be used inside an #Embeddable that is also contained within an #ElementCollection
How to achieve this in spring boot-jpa ?

Building several one-to-many relationships

It implements the model as seen below:
I implement three classes of models:
#Entity
public class Home implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(mappedBy = "home")
private Set<UserHome> userHomes;
}
#Entity
public class UserHome implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
#JoinColumn(name = "home_id")
private Home home;
#OneToMany(mappedBy = "userhome")
private Set<Key> keys;
}
#Entity
public class Key implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
#JoinColumn(name = "userhome_id")
private UserHome userHome;
}
When you try to compile it gets an error:
Invocation of init method failed; nested exception is org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: com.example.homeUser.Key.userhome in com.example.homeUser.UserHome.keys
I do not know what's wrong with my code?
There is a typo in your code, the lowercase h in userhome:
#OneToMany(mappedBy = "userhome")
private Set<Key> keys;
Should be (uppercase H):
#OneToMany(mappedBy = "userHome")
private Set<Key> keys;
The fields/properties you reference in a field like mappedBy should have the exact name and case of the field in the JavaBean.

How to prevent saving of referred entity when using #IdClass?

I have two entities, Type and TypeValue. Each Type can have several TypeValues. While trying to persist a new TypeValue, I get a database error that Type already exists (which is correct, but I don't want to add it again, I want to add just a new 'TypeValue'). I have similar classes without IdClass that are working, so I assume that either the #IdClass definition is wrong or I forgot to define something so that the referred object is not updated.
How to prevent saving of the referred entity Type when using #IdClass for TypeValue?
Class definitions:
#Entity
#Table(name = "TYPE", schema = "VOC")
public class Type implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "TYPEID")
private String typeID;
#Column(name = "NAME")
private String name;
#OneToMany(mappedBy = "type")
private List<TypeValue> listTypeValue;
// constructor, getter, setter, equals, hashcode, ...
}
#Entity
#IdClass(TypeValueID.class)
#Table(name = "TYPE_VALUE", schema = "VOC")
public class TypeValue implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#ManyToOne
#JoinColumn(name = "TYPEID")
#ForeignKey(name = "TYPEVALUE_FK")
private Type type;
#Id
#Column(name = "VALUE")
private String value;
// constructor, getter, setter, equals, hashcode, ...
}
public class TypeValueID implements Serializable {
private static final long serialVersionUID = 1L;
String type;
String value;
// equals, hashcode
}
Example of usage:
Type type = ... // get existing type with typeID "DETAIL"
Session session = sessionFactory.getCurrentSession();
TypeValue newTypeValue = new TypeValue(type, "new value");
session.save(newTypeValue);
session.flush();
Thrown exception:
SEVERE: Servlet.service() for servlet [spring] in context with path [/project] threw exception [Request processing failed; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement] with root cause
org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "type_pkey"
Detail: Key (typeid)=(DETAIL) already exists.
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2455)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2155)
...
please change your String typeID to int or long. Then use #GeneratedValue for auto-increment.
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
private int typeID ;
Check this example
#Entity
#Table(name = "USERS")
#Proxy(lazy = false)
public class User {
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
private int uID ;
private String uName ;
private String uEmail ;
private String uPassword;
#OneToMany(cascade=CascadeType.ALL,fetch = FetchType.EAGER)
private List<Reminder> uReminders = new ArrayList<>();
Next Entity
#Entity
#Proxy(lazy = false)
public class Reminder {
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
private int reminderID ;
private Date reminderDate ;
private String reminderDescription ;
You have defined the foreign key column with #Id.
#Id
#ManyToOne
#JoinColumn(name = "TYPEID")
#ForeignKey(name = "TYPEVALUE_FK")
private Type type;
So it is expecting unique value in the column "type".Hope this may help.
The type attribute in the TypeValueID class is wrong, the class should look like this:
public class TypeValueID implements Serializable {
private static final long serialVersionUID = 1L;
Type type;
String value;
// equals, hashcode
}
The JPA Persistence API 2.1 documentation states:
The names of the fields or properties in the primary key class and the
primary key fields or properties of the entity must correspond and
their types must match according to the rules specified in Section
2.4, “Primary Keys and Entity Identity” and Section 2.4.1, “Primary Keys Corresponding to Derived Identities”.
And the rule that applies in this case is:
If the composite primary key class is represented as an id class, the
names of primary key fields or properties in the primary key class and
those of the entity class to which the id class is mapped must
correspond and their types must be the same.

Resources