Delete just one side of a manytomany relationship Hibernate - spring

I have two tables that have a manytomany relationship:
first one is ad ( represents all the products)
#Entity
#Table(name = "ad")
public class Ad {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "admin_id")
private Admin admin;
#ManyToMany(mappedBy = "ads", fetch = FetchType.LAZY)
private List<Order> orders = new ArrayList<>();
Second one is order:
#Entity
#Table(name = "`order`")
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne( cascade=CascadeType.
#JoinColumn(name = "buyer_id")
private Buyer buyer;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "order_ad", joinColumns = {
#JoinColumn(name = "order_id", referencedColumnName = "id", nullable = false, updatable = false) }, inverseJoinColumns = {
#JoinColumn(name = "ad_id", referencedColumnName = "id", nullable = false, updatable = false) })
private List<Ad> ads = new ArrayList<>();
when I delete order using its repository that is representing a cancellation so I don't want the ads to be deleted as well.
How can I do that?
PS: I can't find a replacement for the orphanRemoval of the onetomany relationship

Related

Spring Data persisting Phantom Child with Null value - not null property references a null or transient value

I have the following Entities in my Project:
#Getter
#Setter
#Entity
#Table(uniqueConstraints = #UniqueConstraint(columnNames = { "purchaseId" }))
public class Purchase {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long purchaseId;
#Column(unique = true, nullable = false, length = 15)
private String purchaseNo;
#Column(nullable = false, length = 15)
private String batchCode;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "supplier.supplierId", foreignKey = #ForeignKey(name = "FK_purchase_supplier"), nullable = false)
private Supplier supplier;
#Column(nullable = false)
private LocalDate purchaseDate;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "purchaseId", nullable = false)
private List<PurchaseItem> purchaseItems;
private Double totalAmount;
#ManyToOne
#JoinColumn(name = "userId", nullable = false, foreignKey = #ForeignKey(name = "FK_invoice_purchases"))
private User staff;
#Column(length = 100)
private String remarks;
#Column(nullable = false, updatable = false)
#CreationTimestamp
private LocalDateTime createdAt;
private boolean isDeleted = false;
}
#Getter
#Setter
#Entity
#Table(uniqueConstraints = #UniqueConstraint(columnNames = {"purchaseItemId"}))
public class PurchaseItem {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long purchaseItemId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "purchaseId", insertable = false, updatable = false, foreignKey = #ForeignKey(name="FK_purchase_item"))
private Purchase purchase;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "productId", foreignKey = #ForeignKey(name="FK_product_item"), nullable = false)
private Product product;
private Double itemAmount;
#Column(nullable = false)
private Double quantity;
private Double itemTotalAmount;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
#PrimaryKeyJoinColumn(foreignKey = #ForeignKey(name = "FK_purchacase_item_batch"))
private PurchaseProductBatch productPurchaseBatch;
public void setPurchaseProductBatch() {
PurchaseProductBatch productPurchaseBatch = new PurchaseProductBatch();
productPurchaseBatch.setProduct(this.product);
productPurchaseBatch.setQuantity(this.quantity);
productPurchaseBatch.setPurchaseItem(this);
this.productPurchaseBatch = productPurchaseBatch;
}
}
#Getter
#Setter
#Entity
#Table()
public class PurchaseProductBatch{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long productBatchId;
#ManyToOne(cascade = CascadeType.DETACH)
#JoinColumn(name = "productId", foreignKey = #ForeignKey(name = "FK_product_purch"))
private Product product;
private Double quantity;
#OneToOne(fetch = FetchType.EAGER)
#MapsId
private PurchaseItem purchaseItem;
private boolean isDeleted = false;
#OneToMany(cascade = CascadeType.PERSIST)
#JoinColumn(name = "productBatchId", foreignKey = #ForeignKey(name = "FK_purchase_batch_qty"))
private Set<InvoicePurchaseBatchQuantity> invoicePurchaseBatchQuantities;
}
During Purchase Insert, everything works fine. However, if I update the Purchase record in the database and add new PurchaseItem entry, I encounter the issue below:
org.springframework.dao.DataIntegrityViolationException: not-null property references a null or transient value : com.be.entity.PurchaseItem.product; nested
I have debugged my application and I see that there is a Product instance inside all of the PurchaseItem. When I commented out the PurchaseProductBatch inside PurchaseItem, everything works fine so I conclude that it is the causing the issue. However, I don't understand how and why JPA seems to create phantom PurchaseItem Records with no value.
Also, if I only update an existing PurchaseItem entry in Purchase, I don't encounter any issues.

Application of ManyToMany - Spring does not work

I am implementing many to many relationship between Users and Permissions and I cannot conclude where am I wrong, it is not working (Unable to map collection rs.raf.demo.model.User.permissions):
#Data
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private Long userId;
#Column
private String username;
#Column
private String password;
#ManyToMany
#JoinTable(
name = "USERS_PERMISSIONS",
joinColumns = #JoinColumn(name = "USER_ID", referencedColumnName = "ID"),
inverseJoinColumns = #JoinColumn(name = "PERMISSION_ID", referencedColumnName = "ID")
)
private List<Permission> permissions = new ArrayList<>();
}
=================================================================================
#Data
#Entity
public class Permission {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "permission_id")
private Long userId;
private String type;
#ManyToMany
#JoinTable(
name = "USERS_PERMISSIONS",
joinColumns = #JoinColumn(name = "PERMISSION_ID", referencedColumnName = "ID"),
inverseJoinColumns = #JoinColumn(name = "USER_ID", referencedColumnName = "ID")
)
#JsonIgnore
private List<User> users = new ArrayList<>();
}
I implemented Users and Permissions with looking in this example, this is working (and I cannot see difference with Users and Permission):
#Data
#Entity
#Table(name = "STUD")
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String lastName;
private String firstName;
#ManyToMany
#JoinTable(
name = "STUDENTS_COURSES",
joinColumns = #JoinColumn(name = "STUDENT_ID", referencedColumnName = "ID"),
inverseJoinColumns = #JoinColumn(name = "COURSE_ID", referencedColumnName = "ID")
)
private List<Course> courses = new ArrayList<>();
}
================================================================================
#Data
#Entity
public class Course {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
#ManyToMany
#JoinTable(
name = "STUDENTS_COURSES",
joinColumns = #JoinColumn(name = "COURSE_ID", referencedColumnName = "ID"),
inverseJoinColumns = #JoinColumn(name = "STUDENT_ID", referencedColumnName = "ID")
)
#JsonIgnore
private List<Student> students = new ArrayList<>();
public void addStudent(Student student) {
students.add(student);
student.getCourses().add(this);
}
public void removeStudent(Student student) {
students.remove(student);
student.getCourses().remove(this);
}
}
JPA uses (and creates in your case probably) intermediate join table for many to many relationship and you cannot use different reference column name from name you specified in entity.
So you need to set:
in User class
#ManyToMany
#JoinTable(
name = "USERS_PERMISSIONS",
joinColumns = #JoinColumn(name = "USER_ID", referencedColumnName = "USER_ID"),
inverseJoinColumns = #JoinColumn(name = "PERMISSION_ID", referencedColumnName = "PERMISSION_ID")
)
in Permission
#ManyToMany
#JoinTable(
name = "USERS_PERMISSIONS",
joinColumns = #JoinColumn(name = "PERMISSION_ID", referencedColumnName = "PERMISSION_ID"),
inverseJoinColumns = #JoinColumn(name = "USER_ID", referencedColumnName = "USER_ID")
)
And same for other tables.

JPA - Issue with OneToOne relationship when two foreign keys are primary key to entity

Two foreign keys act as primary key in entity for OneToOne, I'm getting error "Provided id of the wrong type for class ....."
When I tried to POST data, It's getting inserted correctly but GET is not working.
If I change OneToOne to OneToMany it is working for POST & GET both.
Request:
{
"items": [
{
"applicant": {
"guests": [
{
"seqNumber": 1,
"name": "name",
"gender": "gender"
}
]
}
}
]
}
Back Reference:
reservation.getItems().forEach(i -> {
i.setReservation(reservation);
i.getApplicant().setItem(i);
i.getApplicant().getGuests().forEach(g -> g.setApplicant(i.getApplicant()));
});
Reservation Entity:
#Entity
#Getter
#Setter
public class Reservation {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID_RESERVATION", nullable = false, updatable = false)
private String reservationId;
#OneToMany(mappedBy = "reservation", cascade = CascadeType.ALL, orphanRemoval = true)
#JsonManagedReference
private Set<Item> items = new HashSet<>();
}
Item Entity:
#Entity
#Getter
#Setter
#IdClass(Item.ItemKey.class)
public class Item {
#Id
#Column(name = "ID_ITEM_RESERVATION", nullable = false, updatable = false)
private long itemReservationId;
#Id
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ID_RESERVATION", referencedColumnName = "ID_RESERVATION", nullable = false, updatable = false)
#JsonBackReference
private Reservation reservation;
#OneToOne(mappedBy = "item", cascade = CascadeType.ALL)
#JsonManagedReference
private Applicant applicant;
#Data
static class ItemKey implements Serializable {
private Reservation reservation;
private long itemReservationId;
}
}
Applicant Entity:
#Entity
#Getter
#Setter
#IdClass(Applicant.ApplicantKey.class)
public class Applicant {
#Id
#OneToOne(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "ID_RESERVATION", referencedColumnName = "ID_RESERVATION", nullable = false, updatable = false),
#JoinColumn(name = "ID_ITEM_RESERVATION", referencedColumnName = "ID_ITEM_RESERVATION", nullable = false, updatable = false)
})
#JsonBackReference
private Item item;
#OneToMany(mappedBy = "applicant", cascade = CascadeType.ALL, orphanRemoval = true)
#JsonManagedReference
private Set<Guest> guests = new HashSet<>();
#Data
static class ApplicantKey implements Serializable {
private Item item;
}
}
Guest Entity:
#Entity
#Getter
#Setter
#IdClass(Guest.GuestKey.class)
public class Guest {
#Id
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "ID_RESERVATION", referencedColumnName = "ID_RESERVATION", nullable = false, updatable = false),
#JoinColumn(name = "ID_ITEM_RESERVATION", referencedColumnName = "ID_ITEM_RESERVATION", nullable = false, updatable = false)
})
#JsonBackReference
private Applicant applicant;
#Id
#Column(name = "S_NUMBER", nullable = false, updatable = false)
private Short seqNumber;
#Column(name = "N_NAME")
private String name;
#Column(name = "CD_GENDER")
private String gender;
#Data
static class GuestKey implements Serializable {
private Applicant applicant;
private Short seqNumber;
}
}
Expected output must be same as Request but getting error " ... Provided id of the wrong type for class ..."
Here is the code.

How to link two tables by third?

I have three tables:
1) book: id (primary), name
2) shop: code (unique, not primary), name
3) book_shop: book_id, shop_id (code), price
I want to get shops in book like
book.getShop();
How to link this entities?
I tried:
#Data
#NoArgsConstructor
#Entity
#Table(name = "book", schema = "example")
#EntityListeners(AuditingEntityListener.class)
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
#OneToMany(mappedBy = "book", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<BookShop> bookShop;
}
.
#Data
#NoArgsConstructor
#Entity
#Table(name = "shop", schema = "example")
#EntityListeners(AuditingEntityListener.class)
public class Shop {
#Id
private int code;
private String name;
#OneToMany(mappedBy = "shop", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<BookShop> bookShop;
}
.
#Data
#NoArgsConstructor
#Entity
#Table(name = "book_shop", schema = "example")
public class BookShop implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Id
#ManyToOne
#JoinColumn(name = "book_id")
private Book book;
#Id
#ManyToOne
#JoinColumn(name = "shop_id")
private Shop shop;
#Column(name = "price")
private int fromDate;
}
This code return empty set: Book book = bookRepostiory.getById(1).get().getBookShop()
Try the many to many mapping implement like as below remove your book_shop table,
add this code to shop entity,
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
#JoinTable(name = "book_shop",
joinColumns = {#JoinColumn(name = "book_id", nullable = false)},
inverseJoinColumns = {#JoinColumn(name = "shop_id", nullable = false)})
private Set<Book> bookList = null;
add this code to book entity,
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL,
mappedBy ="bookList")
private Set<Shop> shopList=null;
if any issue inform!!
I would suggest, first - initialize the set in the entity
private Set<BookShop> bookShop = new HashSet<>();
Second, add fetch = FetchType.EAGER to your association, for e.g.
#OneToMany(fetch = FetchType.EAGER, mappedBy = "book", cascade = CascadeType.ALL)

Spring JpaRepository manyToMany bidirectional should save instead of update

if got a language table and a system table with a many-to-many relationship:
Language:
#JsonAutoDetect
#Entity
#Table(name = "language")
public class Language implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "language_id", nullable = false)
private int languageId;
#Column(name = "language_name", nullable = false)
private String languageName;
#Column(name = "language_isocode", nullable = false)
private String languageIsoCode;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "system_language", joinColumns = {#JoinColumn(name = "language_id", updatable = false)},
inverseJoinColumns = {
#JoinColumn(name = "system_id", updatable = false)}, uniqueConstraints = {
#UniqueConstraint(columnNames = {
"language_id",
"system_id"
})})
private List<System> systems;
public Language() {
}
// GETTER & SETTERS
// ....
}
System
#JsonAutoDetect
#Entity
#Table(name = "system")
public class System implements Serializable {
#Id
#Column(name = "system_id", nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer systemId;
#Column(name = "system_name", nullable = false, unique = true)
private String systemName;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "university_id", nullable = false)
private University university;
#JoinColumn(name = "calender_id", nullable = false)
#OneToOne(fetch = FetchType.EAGER)
private Calendar calender;
#OneToMany(mappedBy = "system")
#LazyCollection(LazyCollectionOption.FALSE)
private List<SystemUserRole> systemUserRoleList;
#OneToMany(mappedBy = "system")
#LazyCollection(LazyCollectionOption.FALSE)
private List<Role> roleList;
#OneToOne(mappedBy = "system")
#LazyCollection(LazyCollectionOption.FALSE)
private CsmUserEntity csmUserEntity;
#ManyToMany(mappedBy = "systems")
#LazyCollection(LazyCollectionOption.FALSE)
private List<Language> languages;
public System() {
}
// GETTER & SETTERS
// ....
}
When im writing a first dataset (systemId=1, language_id=20) into the table, everything works fine. But when i try to write a second dataset with the same language_id but with other system_id (systemId=2, language_id=20), then the existing dataset gets updated. But i want to have a new dataset instead. What can i do?
Thanks in advance!

Resources