create composite primary key using foreign key using hibernate - mysql

I have a table complaint master which contains a primary key which is further used as a foreign key inside another table named as complaint_treatment having one to many relationship .
#Entity
#Table(name = "complaint_master")
public class ComplaintMaster{
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
#Column(name = "complaint_id")
private long complaint_id;
#OneToMany(mappedBy= "complaintMasterForkey", cascade = CascadeType.ALL )
/*#JsonManagedReference(value="ComplaintTreatmentKey-ComplaintMaster")*/
#LazyCollection(LazyCollectionOption.FALSE)
private List<ComplaintTreatmentKey> complaintTreatmentKey = new ArrayList<>();
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "submitted_on")
private Date submitted_on;
#Column(name = "auth_code")
private String authCode;
// GETTERS AND SETTERS
}
I have another table complaint_treatment in which i have to create composite primary key but the key two fields that are participating are foreign key.
so i used embeded to solve this issue.
#SuppressWarnings("serial")
#Embeddable
public class ComplaintTreatmentKey implements Serializable {
#ManyToOne(cascade = CascadeType.ALL)
#JsonBackReference(value = "ComplaintTreatmentKey-ComplaintMaster")
#JoinColumn(name = "complaint_id")
protected ComplaintMaster complaintMasterForkey;
#ManyToOne(cascade = CascadeType.ALL)
#JsonBackReference(value = "ComplaintTreatmentKey-ServiceCategory")
protected ServiceCategory serviceCategoryForKey;
}
and then using #EmbeddedId use it.
#Entity
#Table(name = "complaint_treatment")
public class ComplaintsTreatment {
#EmbeddedId
private ComplaintTreatmentKey treatmentComplaintKey;
#Column(name = "description")
private String description;
#JoinColumn(name = "status_id")
private StatusMaster statusMaster;
#Column(name = "rca")
private String rca;
#JoinColumn(name = "priority_id")
private PriorityMaster priorityMaster;
#JoinColumn(name = "assigend_to")
private Employee assignedTo;
#Column(name = "closed_on")
private Date closedOn;
}
but it through an error
Invocation of init method failed; nested exception is org.hibernate.AnnotationException: Use of #OneToMany or #ManyToMany targeting an unmapped class: com.nxtlife.model.ComplaintMaster.complaintTreatmentKey

Related

Use same Entity multiple times in another Entity

I am implementing a Spring Boot server using JPA and Hibernate where there are 2 entities: Channel and Translation.
The Channel entity has two fields (nameTranslations and descriptionTranslations that should hold the name and description of a channel in 2 languages french and english) which are of type Translation as described as follow:
Class Channel
#Entity
#Table(name = "CHANNEL")
public class Channel {
#Id
#Column(name = "ID")
private String id;
#OneToOne(mappedBy = "channel", cascade = CascadeType.ALL)
private Translation nameTranslations;
#OneToOne(mappedBy = "channel", cascade = CascadeType.ALL)
private Translation descriptionTranslations;
}
and
Class Translation
#Entity()
#Table(name = "TRANSLATION")
public class Translation {
#Id
#Column(name = "ID")
private String id;
#Column(length = 1024)
private String en;
#Column(length = 1024)
private String fr;
}
My issue is: How can I implement the previously described logic so that there are 2 Translation fields in the Channel class? I have tried it so far using #OneToOne annotation, but it doesn't work.
I'm not sure what kind of mapping you are trying to achieve, but this will work:
#Entity
#Table(name = "CHANNEL")
public class Channel {
#Id
#Column(name = "ID")
private String id;
#OneToOne(cascade = CascadeType.ALL)
private Translation nameTranslations;
#OneToOne(cascade = CascadeType.ALL)
private Translation descriptionTranslations;
}
or, if you want the columns on the other entity table:
#Entity
#Table(name = "CHANNEL")
public class Channel {
#Id
#Column(name = "ID")
private String id;
#OneToOne(mapped="name", cascade = CascadeType.ALL)
private Translation nameTranslations;
#OneToOne(mapped="description", cascade = CascadeType.ALL)
private Translation descriptionTranslations;
}
#Entity
#Table(name = "TRANSLATION")
public class Translation {
#Id
#Column(name = "ID")
private String id;
#Column(length = 1024)
private String en;
#Column(length = 1024)
private String fr;
#OneToOne
private Channel name;
#OneToOne
private Channel description;
}
See the Hibernate ORM documentation for one-to-one associations.

Hibernate JPA Repeated column in mapping for entity error for bi-directional mapping

I have two entities, sale and sale_details. One sale will have many sale_details, but each sale_detail belongs to only one sale, my current code gives me a Repeated column in mapping for entity error.
Sale:
#Entity
#Table(name="Sales")
public class Sale implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "sale_id", nullable = false)
private int sale_id;
#Column(name = "promotionid_fk")
private int promotionid_fk;
#Column(name = "grand_total", nullable = false)
private double grand_total;
#Column(name = "salespersonid_fk", nullable = false)
private int salespersonid_fk;
#Column(name = "customerid_fk", nullable = false)
private int customerid_fk;
#Column(name = "storeid_fk", nullable = false)
private int storeid_fk;
#Column(name = "expected_date", nullable = false)
private Date expected_date;
#Column(name = "pickup_date")
private Date pickup_date;
#Column(name = "initial_deposit_date", nullable = false)
private LocalDateTime initial_deposit_date;
#Column(name = "initial_deposit_type", nullable = false)
private String initial_deposit_type;
#Column(name = "initial_deposit_amount", nullable = false)
private double initial_deposit_amount;
#Column(name = "final_payment_date")
private LocalDateTime final_payment_date;
#Column(name = "final_payment_type")
private String final_payment_type;
#Column(name = "final_payment_amount")
private double final_payment_amount;
//maps one sale to many sale details relationship
#OneToMany(mappedBy = "sale", fetch = FetchType.LAZY)
private List<SaleDetails> sale_detail_list; //stores list of sale_detail entries where FK saleid_fk field is the ID of this sale
//default constructor, never used
public Sale() {
}
//creates new sale
public Sale(int promotionid_fk, double grand_total, int salespersonid_fk, int customerid_fk, int storeid_fk, Date expected_date, LocalDateTime initial_payment_date, String initial_payment_type, double initial_payment_amount) {
this.promotionid_fk = promotionid_fk;
this.grand_total = grand_total;
this.salespersonid_fk = salespersonid_fk;
this.customerid_fk = customerid_fk;
this.storeid_fk = storeid_fk;
this.expected_date = expected_date;
this.initial_deposit_date = initial_payment_date;
this.initial_deposit_type = initial_payment_type;
this.initial_deposit_amount = initial_payment_amount;
}
Sale_details:
#Entity
#Table(name = "sale_Details")
public class SaleDetails implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "sale_detail_id")
private int saleDetailId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "saleid_fk")
private Sale sale;
#Column(name = "saleid_fk")
private int saleid_fk;
#Column(name = "productid_fk")
private int productid_fk;
#Column(name = "quantity_sold")
private int quantity_sold;
public SaleDetails(){
}
public SaleDetails(int saleid_fk, int productid_fk, int quantity_sold){
this.saleid_fk = saleid_fk;
this.productid_fk = productid_fk;
this.quantity_sold = quantity_sold;
}
Table structures:
Im trying to make the relationship bi-directional, what am I doing wrong here? From my understanding the owning side of the relationship is the many-to-one on the sale_details entity, and the mappedby in the sale entity is just referencing that there already is a mapping on the inverse side?
Full error stack trace:
Unable to build Hibernate SessionFactory
org.hibernate.MappingException: Repeated column in mapping for entity: com.owl.server.entities.Sale_Details column: saleid_fk (should be mapped with insert="false" update="false")
I would suggest you to follow java naming conventions. According to this roles:
Classes: Class names should be nouns, in mixed case with the first letter of each internal word capitalized.
Variables: Except for variables, all instance, class, and class constants are in mixed case with a lowercase first letter. Internal words start with capital letters.
So, I would suggest you to correct your mapping in the following way:
#Entity
#Table(name="Sales")
public class Sale implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "sale_id", nullable = false)
private int saleId;
#Column(name = "promotionid_fk")
private int promotionId;
// ...
//maps one sale to many sale details relationship
#OneToMany(mappedBy = "sale", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<SaleDetails> saleDetails;
// default constructor, should be present
// It is used by hibernate for entity instantiation
public Sale() {
saleDetails = new ArrayList<>();
}
// getters, setters
// The addSaleDetail() and removeSaleDetail() are utility methods that
// synchronize both ends whenever a child element is added or removed.
public void addSaleDetail(SaleDetails saleDetail)
{
saleDetails.add(saleDetail);
saleDetail.setSale(this);
}
public void removeSaleDetail(SaleDetails saleDetail)
{
saleDetails.remove(saleDetail);
saleDetail.setSale(null);
}
}
#Entity
#Table(name = "Sale_Details")
public class SaleDetails implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "sale_detail_id")
private int saleDetailId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "saleid_fk")
private Sale sale;
// This should be removed
// #Column(name = "saleid_fk")
// private int saleid_fk;
#Column(name = "productid_fk")
private int productId;
// ...
public SaleDetails(){
}
// getters, setters
}
The value in the mappedBy referred to the field name of another side of the association.
You can omit to use the referencedColumnName if the foreign key referred to the primary key field of target entity.
And an example of creation and saving a new Sale:
Sale sale = new Sale();
sale.setPromotionId(1);
// ...
SaleDetails saleDetail1 = new SaleDetails();
saleDetail1.setProductId(2);
// set other fields except sale
sale.addSaleDetail(saleDetail1);
SaleDetails saleDetail2 = new SaleDetails();
saleDetail2.setProductId(3);
// set other fields except sale
sale.addSaleDetail(saleDetail2);
entityManager.persist(sale);

generated primary key for secondary table coming null on #OneToMany relationship

I'm trying to do a simple #OneToMany relationship between contract and asset. But when hibernate tries to save , it's comming as null. What am I doing wrong?
#Entity
#Data
#EqualsAndHashCode
#NoArgsConstructor
#Table(name = "contracts")
public class Contract {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Integer id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "contractId")
private List<Asset> assets;
}
#Entity
#Data
#Table(name = "assets")
public class Asset {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Integer id;
#ManyToOne
#JoinColumn(name = "contractId", referencedColumnName = "id")
private Contract contractId;
}
#Repository
public interface ContractRepository extends CrudRepository<Contract, Integer> {
}
private void mapAndSave(ContractDTO contractDTO) {
Contract contractToSave = new Contract();
ModelMapper mapper = BiModelMapper.createModelMapperDtoToEntity();
mapper.map(contractDTO, contractToSave);
contractRepository.save(contractToSave);
}
Caused by: java.sql.SQLIntegrityConstraintViolationException: Column 'contractId' cannot be null
The solution I was able to do is change my column table Asset.contractId to NOT NULL. Because Hibernate tries to insert the row, and after that updates the contractId.
And I change to unidirectional relationship, using only #OneToMany on Contract side.

Nested one to many relationship jpa spring boot

I have three classes - Document, Page, Sentence. A Document will have multiple Pages & each Page will have multiple Sentences. I'm trying to map One to Many relationship using Spring Data JPA annotation. But it only works when there are only one layer like - Document>Page. Doesn't work while it's Document>Page>Sentence.
Can anyone please give me a solution for how to do it for nested one to many relationship ?
My classes are given below.
#Entity
#Table(name = "DOCUMENT")
public class Document implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "FILEID")
#GeneratedValue(strategy = GenerationType.AUTO)
private long idFile;
#Lob
#Column(name = "CONTENT")
private byte[] content;
#Column(name = "NAME")
private String name;
#Column(name = "ID_MAIL_USER")
private String idMailUser;
#Column(name = "NUM_PAGES")
private int numPages;
#Column(name = "TO_ANALIZE")
private boolean toAnalize;
#Column(name = "HASH")
private String hash;
#Column(name = "EXTENSION")
private String extension;
#Column(name = "SIZE")
private double size;
#Column(name = "LINK_TO_DRIVE_FILE")
private String linkToDriveFile;
#Column(name="PATH")
private String path;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#Column(name = "PAGES")
private List<Page> pages = new ArrayList<>();
// Setter Getters
}
.
#Entity
#Table(name = "PAGE")
public class Page implements Serializable {
#Id
#Column(name = "PAGE_ID")
#GeneratedValue(strategy = GenerationType.AUTO)
private long idPage;
#Column(name = "PAGE_NUMBER")
private int pageNum;
#Lob
#Column(name = "CONTENT")
private String content;
#ManyToMany(cascade = CascadeType.ALL)
#Column(name = "SENTENCES")
private List<Sentence> sentences = new ArrayList<>();
// Setter Getters
}
.
#Entity
#Table(name = "SENTENCE")
public class Sentence implements Serializable {
//private long idFile;
//private long idPage;
#Id
#Column(name = "SENTENCE_ID")
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "PAGE_NUMBER")
private int pageNumber;
#Column(name = "ORDER")
private int ord;
#Column(name = "CONTENT")
private String content;
#Column(name = "HASH")
private String hash;
// Setter Getters
}
Your OneToMany mappings are incorrect. Correct it as follows
#Entity
#Table(name = "DOCUMENT")
public class Document implements Serializable {
......
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "DOCUMENT_ID") //Name the foreign key column in PAGE table to DOCUMENT_ID
private List<Page> pages = new ArrayList<>();
}
#Entity
#Table(name = "PAGE")
public class Page implements Serializable {
....
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "PAGE_ID") //Name the forein key column in PAGE table to PAGE_ID
private List<Sentence> sentences = new ArrayList<>();
}
Use #JoinColumn annotation instead of #Column to give the name of the foreign key that do the physical mapping between tables in your database.
#Entity
#Table(name = "DOCUMENT")
public class Document implements Serializable {
private static final long serialVersionUID = 1L;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy="document")
#Column(name = "PAGES")
private List<Page> pages = new ArrayList<>();
}
#Entity
#Table(name = "PAGE")
public class Page implements Serializable {
#ManyToOne
#JoinColumn(name="DOCUMENT_ID")
private Document document;
#ManyToMany(cascade = CascadeType.ALL, mappedBy="pages")
#Column(name = "SENTENCES")
private List<Sentence> sentences = new ArrayList<>();
}
#Entity
#Table(name = "SENTENCE")
public class Sentence implements Serializable {
#ManyToMany(mappedBy="sentences")
private List<Page> pages;
}
Here a Document One to Many relationship with Pages.
So.. we need define mappedBy in the entity we want to map another entity.. so in this case
#OneToMany(mappedBy="document",cascade = CascadeType.ALL, orphanRemoval = true)
and in referenced entity i.e. Pages we want foreign key DOCUMENT_ID, so we have to define it using
#ManyToOne
#JoinColumn(name="DOCUMENT_ID")

Foreign Keys in Spring Boot (MySQL, Hibernate, JPA)

I am trying to write a RESTful API using Spring Boot and I am not able to figure out a way to map my relations in the database. I have a User and a Reports table. Each User can have multiple Reports, and a single report consists of "FROM USER" and "TO USER" columns to indicate who sent the report and to whom. My User ID is the primary key and for the Report table, I am generating REPORT ID as the primary key using AUTO INCREMENT. Here is my User model class -
#Entity
#Table (name = "user")
#EntityListeners(AuditingEntityListener.class)
public class User {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
private String email;
private String password;
#OneToMany(mappedBy = "user",cascade = CascadeType.ALL)
private List<Report> reportReceivedList;
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Report> reportSentList;
/* Getters and setters ..... */
}
Here is my Report Model class -
#Entity
#Table (name = "report")
#EntityListeners(AuditingEntityListener.class)
public class Report {
#Id
#Column (name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne()
#JoinColumn(name = "from_user_id")
private Long fromUserId; //THIS SHOULD BE FROM "USER" TABLE
#ManyToOne()
#JoinColumn(referencedColumnName = "to_user_id")
private Long toUserId; //THIS SHOULD BE FROM "USER" TABLE
#Temporal(TemporalType.DATE)
#CreatedDate
private Date createdAt;
private String observation;
private String context;
//Other variables and getters and setters .....
}
Can someone please show me a way to correctly define this relationship. My current model doesn't work. Also, I want rows from REPORT class to be deleted as soon as a user is deleted. Thanks!
I finally fixed it by changing my User class as follows -
#OneToMany(cascade = CascadeType.ALL, targetEntity = Report.class)
#JoinColumn(name = "to_user_id")
private List<Report> reportReceivedList;
#OneToMany(cascade = CascadeType.ALL, targetEntity = Report.class)
#JoinColumn(name = "from_user_id")
private List<Report> reportSentList;
And by changing my Report class as -
#Column(name = "from_user_id")
private Long fromUserId;
#Column(name = "to_user_id")
private Long toUserId;