Mapping Entity Relationships with Inheritance in Spring Boot - java

I am defining a class User which is a parent to my two other classes: Submitter and Assignee. User has all my attributes listed and Submitter and Assignee will just inherit all its attributes. A submitter can submit many Requests.
The models I have coded look like this:
User
package com.merck.trackertest.models;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;
import lombok.Data;
import lombok.NoArgsConstructor;
#Entity
#Data
#NoArgsConstructor
#Table(name = "users")
#Inheritance(strategy = InheritanceType.JOINED)
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
private String isid;
private String email;
public User(String firstName, String lastName, String isid, String email) {
this.firstName = firstName;
this.lastName = lastName;
this.isid = isid;
this.email = email;
}
}
Submitter
package com.merck.trackertest.models;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
#Entity
#Data
#NoArgsConstructor
#EqualsAndHashCode(callSuper = true)
public class Submitter extends User {
#OneToMany(
cascade = CascadeType.ALL,
orphanRemoval = true
)
#JoinColumn(
name = "submitter_id",
referencedColumnName = "id"
)
private List<Request> requests = new ArrayList<>();
public Submitter(String firstName, String lastName, String isid, String email) {
super(firstName, lastName, isid, email);
}
public void addToList(Request request) {
requests.add(request);
}
public void deleteFromList(Request request) {
requests.remove(request);
}
}
Request
package com.merck.trackertest.models;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import lombok.Data;
import lombok.NoArgsConstructor;
#Entity
#Data
#NoArgsConstructor
#Table(name = "requests")
public class Request {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String receivedDate;
private String startDate;
private String endDate;
private String requestNumber;
#ManyToOne
#JoinColumn(name = "submitter_id", referencedColumnName = "id")
private Submitter submitter;
private String assigneeId;
private String status;
public Request(String receivedDate, String startDate, String requestNumber, String status) {
this.receivedDate = receivedDate;
this.startDate = startDate;
this.requestNumber = requestNumber;
this.status = status;
}
}
I have not modelled the Assignee table as of yet.
My concern is the table Submitter does not show anything but the id, is there a way to present the data with the id to the list of requests. Would using #Embeddable and #Embedded make the most sense here, can I do that even though I have defined Request as an Entity. What is the correct way of referencing a OneToMany Bidirectional relationship which uses Inheritance.
Table looks like the below which doesn't provide any useful information.

If you want the Submitter and Assignee tables created with all the cloumns from the user class, you have 2 choices
Mapped super class :
You need make the user class mapped super class by adding the #MappedSuperClass annotation and removing the #Entity and #Table annotations.
Classes having the #MappedSuperClass annotation won't be persisted in the database (no table created).
Entities extending this MappedSuperClass will inherit its properties.
In the database, this will correspond to one Sumbitter table with columns for the declared and inherited fields of the User class.
Table per class strategy :
The Table per Class strategy maps each entity to its table, which contains all the properties of the entity, including the ones inherited.
For this you need to modify the inheritance strategy annotation :
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)

Related

Spring JPA: Error when trying to create the foreign key constraints

I am trying to map some columns of a big database with JPA, so i can fetch some data from it.
This database has composite primary keys, and some of these are also foreign keys. Im fairly new to JPA mapping, so i need some help.
Here's the error i get:
org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing DDL "alter table tbpedidoentrega add constraint FKiwjj63py270eqhfb1olp08oox foreign key (fk_cliente) references tbcadastro" via JDBC Statement
and also:
ERROR: number of referencing and referenced columns for foreign key disagree
It would seem that JPA is not specifying the columns that it needs to reference in the end of the command (the correct command would be alter table tbpedidoentrega add constraint FKiwjj63py270eqhfb1olp08oox foreign key (fk_cliente) references tbcadastro (codigo)), specifying just the table. But why?
Here's my code:
The Client class
package com.agilsistemas.construtordepedidos.model;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
#Entity
#Getter
#Setter
#NoArgsConstructor
#Table(name = "tbcadastro")
public class ClienteModel implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "codigo")
int idCliente;
#Column(name = "razao")
String razaoSocial;
#Column(name = "logradouro")
String rua;
#Column(name = "numero")
String numero;
#Column(name = "bairro")
String bairro;
#Column(name = "complemento")
String complemento;
#Column(name = "cidade")
String cidade;
#Column(name = "fixo")
String telefoneFixo;
#Column(name = "celular")
String celular;
#Column(name = "cliente")
String cliente;
}
The Order class:
package com.agilsistemas.construtordepedidos.model;
import java.io.Serializable;
import java.time.LocalDate;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
#Entity
#Getter
#Setter
#NoArgsConstructor
#Table(name = "tbpedidoentrega")
public class PedidoModel implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_pedido", nullable = false)
#SequenceGenerator(name = "sqpedido")
int idPedido;
#Column(name = "data_pedido", nullable = false)
LocalDate dataPedido;
#Column(name = "hora_pedido", nullable = false)
LocalDate horaPedido;
#ManyToOne
#JoinColumn(name = "fk_funcionario")
FuncionarioModel fkFuncionario;
#ManyToOne
#JoinColumn(name = "fk_cliente")
ClienteModel fkCliente;
#OneToMany(mappedBy = "pedido")
List<ItemPedidoModel> itensPedido;
}
I was expecting this to create the FKs and start the backend. I think the issue lies in the end of the SQL command generated by hibernate (It was supposed to be references tbcadastro (codigo)), but i dont know why it is generating like this.
After some medling and some research, I was able to solve the problem.
The problem was that the table Cliente had a composite primary key, so i needed to implement a special class to specify the composite key:
package com.agilsistemas.construtordepedidos.model;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
#Embeddable
#Getter
#Setter
#EqualsAndHashCode
#AllArgsConstructor
public class IdClienteModel implements Serializable {
#Column(name = "codigo")
private int idCliente;
#ManyToOne
#JoinColumn(name = "empresa")
private EmpresaModel idEmpresa;
}
Then i implemented the ClienteModel class like so:
package com.agilsistemas.construtordepedidos.model;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Table;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
#Entity
#Getter
#Setter
#NoArgsConstructor
#Table(name = "tbcadastro")
public class ClienteModel implements Serializable {
#EmbeddedId
private IdClienteModel idCliente; //using the object as the ID
#Column(name = "razao")
String razaoSocial;
#Column(name = "logradouro")
String rua;
#Column(name = "numero")
String numero;
#Column(name = "bairro")
String bairro;
#Column(name = "complemento")
String complemento;
#Column(name = "cidade")
String cidade;
#Column(name = "fixo")
String telefoneFixo;
#Column(name = "celular")
String celular;
#Column(name = "cliente")
String cliente;
}
And now, in the Pedido (Order) class, i can get the OneToOne relation like this:
#OneToOne
#JoinColumns({
#JoinColumn(name = "fk_cliente", referencedColumnName = "codigo", insertable = false, updatable = false),
#JoinColumn(name = "fk_empresa", referencedColumnName = "empresa", insertable = false, updatable = false) })
ClienteModel fkCliente;
I did this to all the other entities that had composite PKs also, and now the application starts propperly. I hope this awnser can help someone else.

Automatic Column name generation for JPA table mapping

I am trying to map entity tables with #ManyToOne and #OneToMany. The mapping column is in the child table named "internal_plan_id". As per the requirement I can not change the names. Below are the two entity tables:
PARENT TABLE
import java.sql.Timestamp;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
#Entity
#Getter
#Setter
//#Data
#NoArgsConstructor
#Table(name = "financial_plan_details", schema = "financialplanadmin")
public class FinancialPlanDao {
// This internalId is the primary key of the table.
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "internal_plan_id")
private int internalId;
// This stores the plan status into the database table.
#Column(name = "plan_status")
#Size(max = 10)
private String planStatus;
#Column(name = "presentation_file_key")
#Size(max = 500)
private String presentationFileKey;
#Column(name = "create_timestamp")
#NotNull
private Timestamp createdTimestamp;
#OneToMany(mappedBy = "financialPlan")
private List<FinancialSubPlan> subPlans;
}
CHILD TABLE :
#Entity
#Getter
#Setter
#NoArgsConstructor
#Table(name = "financial_plan_subplan", schema = "financialplanadmin")
#JsonInclude(Include.NON_NULL)
public class FinancialSubPlan {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "subplan_id")
private int subPlanId;
#Column(name = "external_subplan_id")
private String externalSubplanId;
#Column(name = "is_chosen")
private Boolean subPlanIsChosen;
#ManyToOne
#JoinColumn(name = "internal_plan_id")
private FinancialPlanDao financialPlan;
}
I am getting the error as :
ERROR: column "internal_plan_id_internal_plan_id" of relation "financial_plan_subplan" does not exist.
The existing column name for mapping in financial_subplan is "internal_plan_id". The above name "internal_plan_id_internal_plan_id" is automatically generated. So is there any way to reduce this to only "internal_plan_id".
The problem was with setting values of the mapped classes. The first thing after forming up the parent class, is to set the parent class into the child class, that is subPlans.set(financialPlan). Then after that we have to set the child class into the parent class, that is financialPlan.set(List of subPlan). I missed the setting up of parent into child.
You can also refer to this JPA / Hibernate One to Many Mapping Example with Spring Boot
In this you can see that after creation of Post object, the Comment object sets the Post object and after that the Post object sets the comment object, before saving it to the database.

Caused by: java.lang.IllegalArgumentException: Not a managed type: & Caused by: org.hibernate.AnnotationException: No identifier specified for entity:

I have created two entity classes.
RoleEntity:
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table (name ="roles")
public class RoleEntity {
#Id
#Column(name = "role_id")
private Integer roleId;
#Column(name = "role_name")
private String roleName;
//Getters
//Setters
UserEntity:
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name="user_master")
public class UserEntity {
#Id
private Integer id;
#Column(name = "user_name")
private String username;
#Column(name = "user_password")
private String password;
//getters
//setters
Now i have a simple pojo which will take data from these two entities and later will be used in a service.
import java.util.Set;
public class UserRoleAssociationEntity {
UserEntity user;
Set<RoleEntity> roles;
//getters
//setters
Now I am getting error when I run the project.
Caused by: java.lang.IllegalArgumentException: Not a managed type: class com.dataGuru.BusDirV3.Entities.UserRoleAssociationEntity
If I annotate the UserRoleAssociationEntity class with #entity i get the following error:
Caused by: org.hibernate.AnnotationException: No identifier specified for entity: com.dataGuru.BusDirV3.Entities.UserRoleAssociationEntity
What is the problem which i am facing here & solution for this problem.
You need to have a unique field in your class which acts as an identifier for this entity. (Field with #Id annotation`)
Instead of creating a new POJO, add a many-to-many relation ship in the UserEntity Class like.
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name="user_master")
public class UserEntity
{
#Id
private Integer id;
#Column(name = "user_name")
private String username;
#Column(name = "user_password")
private String password;
#Column(name = "user_password")
private String password;
#ManyToMany(cascade=CascadeType.MERGE, fetch = FetchType.EAGER) //
#JoinTable(
name="USERROLE_ASSOCIATION",
joinColumns={#JoinColumn(name="USER_ID", referencedColumnName="ID")},
inverseJoinColumns={#JoinColumn(name="ROLE_ID", referencedColumnName="ID")})
private Set<RoleEntity> UserRoleAssociationEntity ;

Is-a and Has-a Problem mapping foreign key

In my program I realize a problem when it get a inheritance and a oneToMany relationship. That is:
I put to run. It will create the tables, without the foreign key. But when I remove the Inheritance annotations, and run again. It creates as foreign.
Class Employer
package com.example.TablePerConcreteClassExample.model;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
#Entity
#Table(name="Employer")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="type")
#DiscriminatorValue(value="Employer")
public class Employer {
#Id
#Column (name = "empNo", nullable=false)
#GeneratedValue(strategy = GenerationType.AUTO)
private int empNo;
#Column(name = "name", nullable=false)
private String name;
#OneToMany (mappedBy = "employer",
fetch=FetchType.LAZY)
private List<Vehicle> vehicles;
public int getEmpNo() {
return empNo;
}
public void setEmpNo(int empNo) {
this.empNo = empNo;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Vehicle> getVehicles() {
return vehicles;
}
public void setVehicles(List<Vehicle> vehicles) {
this.vehicles = vehicles;
}
}
And Class Vehicle
package com.example.TablePerConcreteClassExample.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
#Entity
#Table(name="Vehicle")
public class Vehicle {
#Id
#Column (name = "id", nullable=false)
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column(name = "name", nullable=false)
private String name;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "empNo", nullable=false)
private Employer employer;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Employer getEmployer() {
return employer;
}
public void setEmployer(Employer employer) {
this.employer = employer;
}
}
And my application.properties:
spring.datasource.url=jdbc:mysql://localhost:3306/TablePerConcreteClass?createDatabaseIfNotExist=true&useSSL=false&useTimezone=true&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=
spring.jpa.database=MYSQL
spring.datasource.platform=mysql
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
spring.database.driverClassName=com.mysql.cj.jdbc.Driver
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true
spring.servlet.multipart.enabled=true
I expected a inheritance and in the vehicle table a Employer's foreign key.
The actual results are the inheritance, but no foreign key. But when I remove inheritance annotations, the foreing key will be created.

save Student using hibernate with current date and dob and enum

and so far i have made bean class like that
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
#NoArgsConstructor
#Getter
#Setter
#Entity
#Table(name="logintableetech")
public class Student {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="student_id")
private int id;
#Column(name="full_name")
private String name;
#Column(name="bebo_reg_id")
private String regId;
#Column(name="password")
private String password;
#Column(name="email")
private String email;
}
Please help me to choose which class should i use for appropariate date ,i want birthdate as date entered by user and date_registed for date as current timestamp and also tell me how to save for enum in databse for mysql what data type should i use String (if possible) or Enum.

Categories