Spring hibernate JPA One to many mapping:Duplicate key error - spring

I have one to many relationships between the following 2 entities.
#Entity
public class OrderItem {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#OneToOne
#JoinColumn(name="ORDER_PRODUCT_ID")
private Product product;
private Long quantity;
private BigDecimal totalPrice;
#OneToMany
#JoinTable(name="orderItem_productaddition",joinColumns=#JoinColumn(name="orderItem_id"), inverseJoinColumns=#JoinColumn(name="ProductAddition_id"))
private Set<ProductAddition> listOfAdditions=new HashSet<>();
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(unique = true)
private ProductOption productOption;
}
Second Entity
#Entity
public class ProductAddition{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String additionDescription;
private BigDecimal additionPrice;
private BigDecimal additionsPriceForSmall;
private BigDecimal additionsPriceForNormal;
private BigDecimal additionsPriceForFamily;
private BigDecimal additionsPriceForParty;
#ManyToMany(fetch = FetchType.LAZY,mappedBy = "productAdditions")
#JsonBackReference
private Set<Product>product=new HashSet<>();
}
Following is the existing data in the join table order_item_productaddition.
For following insert i get the
Duplicate entry '41' for key 'UK_22djpp3b17x2x1vurh26crns'
insert into website_dev.order_item_productaddition values(379,41)
I do not understand what is wrong with my Join table mapping, it is one to many relationships and i expect multiple entries.
why does spring jpa adds unique key constraint to the join table.
Is there any way to make composite key constraint in Join table ? or how can i remove the constraint ?
Any advice will be highly appreciated.
Thanks.

Related

Is that possible in spring boot that join column (foreign key) with id

I want to join column without object reference. is that possible?
I want to do foreign key without object reference like that
#Data
#Entity
#Table(name = "HRM_EMPLOYEE_SALARY_INCREMENT")
public class EmployeeSalaryIncrement implements Serializable {
private static final long serialVersionUID = 9132875688068247271L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="ID")
private Integer id;
#Column(name = "REFERENCE_NO")
private String referenceNo;
#ManyToOne
#JoinColumn(name = "AUTHORITY", referencedColumnName = "id")
private Integer authority;
#ManyToOne
#JoinColumn(name = "PART_TWO_REGISTER_ID")
private Integer partTwoRegisterId;
#Column(name = "PART_TWO_ORDER_NO")
private String partTwoOrderNo;
#Column(name = "REMARKS")
private String remarks;
#Column(name = "HRM_TYPE")
private Integer hrmType;
}
If I found solve this problem, it will helpful for me.
Joining is not needed in this case. If you only need the foreign key value, then simply add the column as a #Column like any other:
#Data
#Entity
#Table(name = "HRM_EMPLOYEE_SALARY_INCREMENT")
public class EmployeeSalaryIncrement implements Serializable {
private static final long serialVersionUID = 9132875688068247271L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="ID")
private Integer id;
#Column(name = "AUTHORITY")
private Integer authority;
// other fields
// ...
}
No, I don't think that you can join columns between two entities without adding the reference of one to the related entity. You will have to create one entity class corresponding to each of your relational database table and add the reference of one to the other to establish relation.
However, I understand that you may not need all the attributes from your related table based upon your use case, and only wish to select one column from it. You can do that either by only adding required attributes in your joined table entity class (if you are sure you won't need other attributes for that table anywhere else).
Or you can use custom queries using JPQL in your repository class which selects only the required attributes from the tables that you have joined.
I will show you an example of the second way:
//Say, this is your entity class where you wish to join other table to fetch only one attribute from the joined table-
#Entity
#Table(name = "TABLE1", schema = "SCHEMA1")
public class Table1 {
#Id
#Column(name = "ID")
private String id;
#Column(name = "TABLE2_COLUMN")
private String table2Column;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "TABLE2_COLUMN1")
private Table2 table2; //refrence of the joined table entity object
}
// And this is the joined table entity class
#Entity
#Table(name = "TABLE2", schema = "SCHEMA1")
public class Table2 {
#Id
#Column(name = "ID")
private String id;
#Column(name = "TABLE2_COLUMN1")
private String table2Column1;
#Column(name = "TABLE2_COLUMN2")
private String table2Column2; // The column which we want to select from the joined table
}
In your repository class -
#Repository
public interface Table1Repository extends JpaRepository<Table1, String> {
#Query("SELECT t1 FROM Table1 t1 WHERE t1.id = :id")
public List<Table1> getTable1Rows(#Param("id") String id);
#Query("SELECT t1.table2.table2Column2 FROM Table1 t1 WHERE t1.id = :id")
public String getTable2Column2(#Param("id") String id);
}
Based upon the response from Markus Pscheidt below, I agree when he said there's no need to join the entities if you only need the attribute which is a foreign key. As foreign key is already present as an attribute in your entity (or table) you are working with.
If you need to fetch any other column apart from foreign key, then you may use JPQL to fetch the exact column that you wish to select.

Spring data jpa hibernate one to may duplicate issue

I have one to many relation ship between a User and UserRole.
public class User {
#Id
#GeneratedValue
private long id;
#Column(unique = true)
private String username;
private String password;
#OneToMany(fetch=FetchType.EAGER)
#JoinTable(
name = "user_roles",
joinColumns = {#JoinColumn(name="userId")},
inverseJoinColumns = {#JoinColumn(name="roleId")}
)
private Collection<UserRole> roles;
}
and
public class UserRole {
#Id
#GeneratedValue
private long id;
#Column(unique = true)
private String roleName;
}
I am able to save a user at first. But when I try to save another user it rejects with an exception of duplicated entry on the junction table
One thing I noticed is when we have One-to-Many association the many side foreign key will be a primary key on the junction table. So, I need to make the relation Many-to-Many. So that the combination of both foreign keys will serve as a composite key.

How to perform delete operation in classes which consists one to many relationship

This is my product Entity
#Entity
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#NotNull
private String name;
private String cancellable;
private String returnable;
#NotNull
private String brand;
private boolean active;
#JsonIgnore
#OneToMany(mappedBy = "product",cascade = CascadeType.ALL)
private Set<ProductVariation> productVariationSet;
}
This is Product Variant Entity
#Entity
public class ProductVariation {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String ProductName;
#NotNull
private int quantityavailable;
#NotNull
private int price;
private String details;
#JsonIgnore
#ManyToOne
#JoinColumn(name = "product_id")
private Product product;
}
when i am tring to delete products by ID i am getting error like
java.sql.SQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (mywebapp.product_variation, CONSTRAINT FKpryf02se86hpv5v7xn5afye4v FOREIGN KEY (product_id) REFERENCES product (id))
How i can correct this error and delete product so its all variant is also delete.
The cascade operation in Product class as you defined it deletes the product's variants automatically.
Just remove #JsonIgnore from field 'private Set productVariationSet' and from field 'private Product product'.

Map primary key to composite key in JPA

I have 2 tables namely user & user_session.
User table has user_id as a primary key which is referrers to user_session table.
Plus user_session has composite key including session_intime and user_id.
I have designed my entity in JPA. Now I want to map these two entities. I have tried to map these two tables. But my application build failed. Can you please help me out?
#Entity
#Table(name="user")
public class User {
#Id
#Email
#Column(name = "user_id")
private String userId;
#Column(name = "password")
private String password;
#Column(name = "fname")
private String fname;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "userId", referencedColumnName = "user_id")
private UserSession userSession;
}
#Entity
#Table(name="user_session")
public class UserSession{
#EmbeddedId
private UserSessionPK userSessionPK;
#Column(name = "remote_ip")
private String remoteIp;
}
#Embeddable
public class UserSessionPK implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "user_id")
private String userId;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "time_in")
private Date timeIn;
}
I want to map user_id of User table to user_id of UserSessionPK. I am new to JPA, so I don't know how to map with embeddable class.
Remove the mappedBy attribute. This attribute is used when you have bidirectional relationship to indicate which side of the relationship is the owner.
But you will need to set the Foreign Key aka JoinColumn
#JoinColumn("user_id")
#OneToMany(fetch = FetchType.LAZY)
private UserSession userSession;

Spring boot entities,relationships and repository confusion

I am confused as to how relationships work in entities and what this means with regard to my JPA repository.
I have a class called Loan which stores a list of albums for each loan.
I have a loan repository and an album repository. The album repository is filled with albums when I start the application. The albumId is autogenerated.
When I create a new loan and try to add an album from the repository I get an exception :
Caused by: org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: com.library.demo.entity.Album; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: com.library.demo.entity.Album
If I create a new loan and add a new album on the fly then it works as expected. During the debug I realised that this is because albumId is null when adding a new album on the fly to the loan, presumably because it adds the album to the repository and generates a new albumId when the loan is created.
My album entity looks like this :
#Entity
public class Album implements Serializable {
private static final long serialVersionUID = 0x63A6DA99AA12AAA8L;
#Column #GeneratedValue(strategy = GenerationType.AUTO) #Id private Integer albumId;
#Column (unique=true) private String barcode;
#Column private String band;
#Column private String title;
#Column private String genre;
#Column private Integer year;
#Column private String artworkFilename;
#Column private Boolean enabled;
#Column private Boolean isLoanable;
#Column private Integer numberOfCopies;
#ManyToOne
private Loan loan;
And my loan looks like this :
public class Loan implements Serializable {
private static final long serialVersionUID = 0x62B6DA99AA12AAA8L;
public void setLoanId(Integer loanId) {
this.loanId = loanId;
}
#Column #GeneratedValue(strategy = GenerationType.AUTO) #Id private Integer loanId;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Album> albums= new ArrayList<>();
#Column private Integer customerId;
#Column private Date dateLoaned;
#Column private Date dateToReturn;
#Column private Boolean expired;
I am also confused as to why the album has to refer back to the loan with a ManyToOne annotation. Why does the album have to refer to the loan?
I am mostly used to relation databases so maybe I am thinking about things in the wrong way. If I can only add new albums to the loan then it defeats the purpose of what I am trying to do.
Loan and Album tables have a one-to-many relationship.
So in Album your mapping should be like below:
#ManyToOne
#JoinColumn(name = "loan_id")
private Loan loan;
Considering loan_id is the primary key of Loan.
And in Loan your mapping should be like below:
#OneToMany(mappedBy = "loan", cascade = CascadeType.ALL)
private List<Album> albums;
#OneToMany and #ManyToOne defines a one-to-many and many-to-one relationship between 2 entities. #JoinColumn indicates the entity is the owner of the relationship: the corresponding table has a column with a foreign key to the referenced table. mappedBy indicates the entity is the inverse of the relationship
I have updated my source as you have described and made some changes to the start up.
If I create the loan first and then save it to the loan repository and add the album set afterwards, it no longer crashes.
Loan loan = new Loan(1, new Date(), calendar.getTime(),null);
loanRepository.save(loan);
List<Album> albumList = AlbumImport.getAlbumList();
albumRepository.save(albumList);
List<Album> albums = new ArrayList<>();
albums.add(albumList.get(1));
albums.add(albumList.get(5));
loan.setAlbums(albums);
However, when I run my getLoan test the album list is empty
Removing cascade = CascadeType.ALL fixed my problem.

Resources