Spring JPA: primary key is a foreign key - This class does not define an IdClass - spring

I'm creating an Entity with a primary key that is also a foreign key at the same time. However, I'm getting the following error "This class does not define an IdClass". What could be the problem?
class Quote
#Entity
#Data
#IdClass(Instrument.class)
public class Quote implements Serializable {
#Id
#OneToOne
#JoinColumn(name = "instrument")
#NotNull
private Instrument instrument;
#NotNull
private String time;
#NotNull
private double bid;
#NotNull
private double ask;
#CreatedDate
#Temporal(TIMESTAMP)
#NotNull
protected Date creationDate = new Date();
}
class Instrument
#Entity
#Data
public class Instrument implements Serializable {
#Id
private String instrument;
#NotNull
private Currency currencyPrimary;
#NotNull
private Currency currencySecondary;
}

You missing constructor contain field take primary key #id.(instrumentId : It is my example) and missing equal and hashCode in Instrument class.

Related

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

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
}

OneToMany relationship using non-primary composite key

I have a table structure like this
Good Assignments Entity
#Embeddable
public class GoodAssignmentId {
String clientId,
String assignmentNumber;
LocalDate effectiveDate;
// Getters and setters
}
#Entity
#IdClass(GoodAssignmentId.class)
class GoodAssignment {
#id
String clientId;
#Id
String assignmentNumber;
#Id
LocalDate effectiveDate;
#OneToMany(mappedBy = "parentKey")
Set<GoodTasks> children;
String description;
// getters and setters goes below
}
Bad Assignments Entity
#Entity
#IdClass(BadAssignmentId.class)
class BadAssignment {
#id
String clientId;
#Id
String assignementNumber;
#Id
LocalDate effectiveDate;
String description;
// Getters and setters goes below
}
Child entities
#Entity
#IdClass(ParentTasksId.class)
#DiscriminatorColumn(name = "fieldD", discriminatorType = DiscriminatorType.STRING)
class ParentTasks {
#Id
String clientId;
#Id
String assignmentNumber;
#Id
String taskNumber;
}
#Entity
#DiscriminatorValue("G")
class GoodTasks extends ParentTasks {
#ManyToOne
#JoinColumns({
#JoinColumn(name = "clientId", referencedColumName = "clientId"),
#JoinColumn(name = "assignmentNumber", referencedColumName = "assignmentNumber")
})
GoodAssignments parentKey;
other fields....
}
This shows the error referencedColumnNames(fieldA, fieldB) of .... not mapped to a single property.
Unfortunately I cannot change the table structure. TableA has 3 columns as primary key, but only two of them forms the primary key in table B along with another field (fieldD via #DiscriminatorValue used by multiple classes). How can I map this relationship to get list of TableB items in TableA?
Example Class Diagram:
Effective date in the assignments tables is not part of tasks. So this is not a perfect relationship in JPA terms. It's a legacy design which cannot be changed for some reasons.

i'm getting null value in a child table as a foreign key of parent table using spring data rest or spring data jpa accosiation

enter image description here In this image first address for empId 1 and last two records are empid 2 (empid 2 haveing to address)
file:///home/user/Pictures/fk.png
#Entity
#Table(name = "Employee")
public class Employee {
#Id
#GeneratedValue
private Integer id;
private String name;
private Integer sal;
#OneToMany(cascade = CascadeType.ALL,mappedBy="employee")
private List<Address> addresses;
//getter setter
Child entity
#Entity(name="Address")
public class Address {
#Id
#GeneratedValue
private Integer aid;
private String city;
private String state;
#ManyToOne
#JoinColumn(name="id")
private Employee employee;
//getter setter
Repository
#Repository
#RepositoryRestResource(path="employee")
public interface EmployeeRepo extends JpaRepository<Employee,Integer> {
}
Input from RestClient
{
"name":"rdhe",
"sal":"20000",
"addresses":[{
"city":"hyd",
"state":"ts"
}]
}
if i use spring data jpa then code will be
// jpa Repository
public interface EmployeeRepo extends JpaRepository<Employee,Integer> {
}
// EmployeeServer class
#Service
public class EmployeeService {
#Autowired
EmployeeRepo employeeRepo;
public void saveEmployee(Employee employee){
employeeRepo.save(employee);
}
}
// controller
#RestController
public class EmployeeController {
#Autowired
EmployeeService employeeService;
#PostMapping(path="/save")
public void saveEmp(#RequestBody Employee employee){
employeeService.saveEmployee(employee);
}
}
if i'll use spring-data-rest at that time no need to create employeeService and controller class
I was getting the same problem until JsonManagedReference came to my rescue.
Try changing your entities to include them like this:
In the Employee Entity:
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy ="employee")
#JsonManagedReference
private List<Address> addresses;
In the Address Entity:
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id", nullable = false, updatable = false, insertable =true)
#JsonBackReference
private Employee employee;
I was not able to find why it works this way, so please let me know if you come to know :)
It is probably due to the fact that your mentioning #JoinColumn(name="id"). The name attribute in #JoinColumn defines the name of the foreign key field in the child table. Since you are specifying foreign key column as id on hibernate, it could be the issue. Please update it to the same name(ie fk_empid) as specified in database, it should work...

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.

Spring and Hibernate Error -- not-null property references a null or transient value: com.tharaka.model.Employee.designation

im new to Spring and hibernate, i got the error above when trying to persist the transaction data. please try to help this problem
Here's my Entity:
#Entity #NamedQuery(name="Employee.findAll", query="SELECT e FROM Employee e")
public class Employee implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private String city;
private String civil;
#Temporal(TemporalType.DATE)
#Column(name="dob", length=11)
private Date dob;
private String email;
private int epf;
private String fname;
private String gender;
private int landtp;
private String lname;
#Temporal(TemporalType.DATE)
#Column(name="salaryincrement", length=11)
//bi-directional many-to-one association to Designation
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="designation_id", nullable=false)
private Designation designation;
public Employee() { }
#Entity
#NamedQuery(name="Designation.findAll", query="SELECT d FROM Designation d")
public class Designation implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private String type;
//bi-directional many-to-one association to Employee
#OneToMany(mappedBy="designation")//, cascade=CascadeType.ALL
private List<Employee> employees;
public Designation() {
}
this is my Entity class,
Entities have a getters ans setters
designation is set nullable = false. However employees variable isn't initialized in Designation. So, you'll have to initialize as
#OneToMany(mappedBy="designation")//, cascade=CascadeType.ALL
private List<Employee> employees = new LinkedList<>();
I'm not sure that you can go with primitive type int as your Id - you should probably use Integer - because int has default zero value and cannot be null, your new record can be rather seen as a detached entity with Id ZERO and not as a transient one.
The same mistake is in Designation class.
See Primitive or wrapper for hibernate primary keys

Resources