Springboot JPA Hibernate CascadeType confusion - java

How to make a column cascade enable only for insertion not update and delete.
Here are two class. There are #ManyToOne relationship between Qualification and Department. At the time of insertion I want to insert new department with qualification. But at the time of update of qualification I don't want to update department. How can I configure that??
#Table(name = "department")
public class Department {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
}
#Table(name = "qualification")
public class Qualification implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Integer id;
private Integer passingYear;
private String result;
#ManyToOne(optional = true, cascade = { CascadeType. ? }, fetch = FetchType.EAGER)
#JoinColumn(name = "department_id", referencedColumnName = "id")
private Department department;
}

CascadeType.PERSIST is what you are looking for. The various CascadeType enums essentially say, which persistence operation of EntityManager persist/merge/remove etc. should cascade to the objects of an association.

Related

can't find the pattern of setting cascade logic

I have 3 tables which are Person Login and Account.
Person and Login is OneToOne relation and Login has one FK which is connected Person's id column called PERSON_ID.
Person(one) and Account(many) is OneToMany relation and Account has one FK which is connected Person's id column called PERSON_ID as well .
what i want to do is when i delete one data from Account , nothing happen to Person and Login.
if i delete one data from Person which id=1, Login's PERSON_ID=1 data will be deleted , and all of the data PERSON_ID=1 from Account will be deleted as well.
if i delete one data from Login which PERSON_ID=1, Person 's id=1 data will be deleted , and all of the data PERSON_ID=1 from Account will be deleted as well.
how should i set the cascade ?
i've tried dozens of times and still can't find the logic in there, thanks!!
here's my code of all 3 tables without setting cascade:
`
#Entity
#Table(name = "PERSON")
public class Person {
#Id
#Column(name = "ID", nullable = false, unique = true)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "NAME")
private String name;
#Column(name = "SEX")
private String sex;
#OneToMany(mappedBy = "person",fetch = FetchType.LAZY)
private List<Account> account;
#OneToOne(mappedBy = "person")
private Login login;
#get..
#set..
}
`
#Entity
#Table(name = "ACCOUNT")
public class Account {
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "ACCOUNT")
private String account;
#Column(name = "AMOUNT")
private String amount;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "PERSON_ID",referencedColumnName = "ID")
public Person person;
#get..
#set..
}
`
#Entity
#Table(name = "LOGIN")
public class Login {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private long id;
#Column(name = "USERNAME")
private String userName;
#Column(name = "PASSWORD")
private String password;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "PERSON_ID", referencedColumnName = "ID")
private Person person;
#get..
#set..
}
It's been a while, but if I'm not mistaken you need to use the cascade=REMOVE option on the OneToMany and OneToOne relationships. In the OneToOne I think you need to specify cascade=REMOVE on the side that does NOT own the relationship, that is, the side that also contains the "mappedBy" property.
Finally, I believe JPA will NOT automatically load lazy relationships and then cascade them. I'm thinking you may need to fetch the relationship before you delete the parent entity (otherwise JPA will not know what to delete).

How to remove children from parent entity record in JPA?

I have Product entity and ProductRating entity, each Product can have many ProductRatings. When Product is deleted I want to have associated ratings deleted too, but nothing works so far (also orphanRemoval set to true)...
Classes:
#Getter
#Setter
#Entity
#Table(name = "PRODUCT")
public class Product extends AbstractEntity<Long> {
#Column(nullable = false)
private String name;
private String description;
#Column(nullable = false)
#Min(value = 0)
private Float cost;
#OneToMany(mappedBy = "product",
orphanRemoval = true, cascade = CascadeType.PERSIST,
fetch = FetchType.EAGER)
//#OnDelete(action = OnDeleteAction.CASCADE)
#Fetch(value = FetchMode.SELECT)
private Set<ProductRating> productRatings;
}
#Getter
#Setter
#Entity
#Table(name = "PRODUCT_RATING")
public class ProductRating extends Rating {
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "product_id")
#NotNull(message = "Rating must be in context of Product")
private Product product;
}
After Product deletion ratings stay with deleted Product's ID
AbstractEntity implementation:
#Getter
#Setter
#MappedSuperclass
public abstract class AbstractEntity<I> implements Serializable {
private static final long serialVersionUID = 1700166770839683115L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID", unique = true, nullable = false)
private I id;
}
In the #OneToMany relation you need to add the cascade type delete: cascade = {CascadeType.PERSIST, CascadeType.REMOVE}
Or if you don't mind having all cascade types you can just put: cascade = CascadeType.ALL
EDIT:
Also check the name of the Product primary key in the database.
It should match the defined in the #JoinColumn annotation of ProductRating
The default database field for the attribute id of the Product class would be product_id.
However you have defined the id in AbstractEntity as name = "ID" so the #JoinColumn should be something like: #JoinColumn(name = "ID")
My alternative approach to fix this problem is to:
On parent-side relation create method with #PreRemove annotation
in this method iterate over collection with #[One/Many]ToMany annotation and call delete(obj) method for corresponding repository on child
On child-side relation create method with #PreRemove annotation
In this method set parent to null

JPA: Reference column in the child entity is null when using unidirectional #OneToMany

I have two entity classes.
Order.java
#Entity
#Table(name = "order_table")
public class Order implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "order_id", referencedColumnName = "id", nullable = false, insertable=false, updatable=false)
private Set<Item> items;
// getters & setters & toString
Item.java
#Entity
#Table(name = "item")
public class Item implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#Column(name = "order_id", nullable = false)
private Long orderId;
// getters & setters && toString
I created a test class like this:
#Test
public void createOrderWithItems() {
Item item = new Item();
item.setName("Iron Man");
Order order = new Order();
order.setName("Toy");
order.getItems().add(item);
Order created = service.createOrder(order);
Order orderById = service.getOrderById(order.getId());
System.out.println("Created Order: " + orderById);
Item itemById = service.getItemById(item.getId());
System.out.println("Created item: " + itemById);
Assert.notNull(created.getId(), "Order ID is Null");
}
Test is green but if you check output, you'll see that orderId field in the Item class is null.
Created Order: Order{id=1, name='Toy', items=[Item{id=2, name='Iron Man', orderId=null}]}
Created item: Item{id=2, name='Iron Man', orderId=null}
Does JPA not update this column in the db automatically? Is this column is redundant? If so, how can I retrieve this information from test code?
You need to set orderId explicitly.
item.setOrderId(order.getId());
order.getItems().add(item);
You can create a method addItem(Item item) in your Order class and hide this logic within it.
Cascading will create an entry in db but it won't initialize field. JPA annotations just indicate to JPA provider how to perform mapping between entity and table.
Moreover, check your annotations. #JoinColumn should be used in the entity which owns the relationship (the corresponding table has column as a foreign key). Check the top answer for this question for detailed explanations: What's the difference between #JoinColumn and mappedBy when using a JPA #OneToMany association

Hibernate merge parent and update Lazy Child Collection List

I am having a problem when I perform merge parent table, the existing child records will be insert rather than update.
For example, Member(Parent) and Address(Child) tables,
public class Member implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private int id;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "member", cascade = CascadeType.ALL)
public List<Address> addressList;
}
public class Address implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int address_id;
#Column(name = "member_id")
private int mem_id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumns({
#JoinColumn(name = "member_id", referencedColumnName = "id", insertable = false, updatable = false)
})
public Member member;
}
While I perform changes and merge(Member), the address record is being inserted rather than update the existing record.
Does anyone know what would be the reason?
when you perform changes do you set correct address_id ? . Do some debugging and find address_id match with existing id in Address table.
in my approach class will look like below. I don't see need of member_id attribute. because it is same as id attribute of member.
public class Address implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int address_id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "member_id", nullable = false)
public Member member;
public void setMember(Member member) {
this.member = member;
}
}
I can't see any parent child relationship in your snapshot code.
Please amend the code for child class with below code to create the inheritance relationship.
public class Child extends Member implements Serializable{
Extending the Child class to the Parent(Member) will reflect the required changes related to lazy loading.

How to properly delete a complex object using Hibernate?

Here is the problem. When I'm trying to delete a Catalog object from database, Hibernate also removing all Catalog objects with associated Type and Genre Ids. For example, if I’m removing Catalog with Type.id=1 and Genre.id=1 Hibernate delete every Catalogs with such Ids. Any ideas how to fix it? I need to delete only one Catalog object without deleting Type and Genre objects with id=1.
#Entity
#Table(name = "catalog", catalog = "media_store_db")
public class Catalog implements Serializable {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Column(name = "product_name", length = 100)
private String productName;
#ManyToOne(cascade = {CascadeType.ALL})
#JoinColumn(name = "genre_id", referencedColumnName = "genre_id")
private Genre genre;
#ManyToOne(cascade = {CascadeType.ALL})
#JoinColumn(name = "type_id", referencedColumnName = "type_id")
private Type type;
#Entity
#Table(name = "genres", catalog = "media_store_db")
public class Genre implements Serializable {
#Id
#Column(name = "genre_id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Column(name = "genre_name")
private String name;
#OneToMany(mappedBy = "genre", cascade = CascadeType.ALL)
Collection<Catalog> catalogs = new ArrayList<Catalog>();
#Entity
#Table(name = "types", catalog = "media_store_db")
public class Type implements Serializable {
#Id
#Column(name = "type_id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Column(name = "type_name")
private String name;
#OneToMany(mappedBy = "type", cascade = CascadeType.ALL)
Collection<Catalog> catalogs = new ArrayList<Catalog>();
My method which delete a Catalog object
public void deleteCatalog(Integer catalogId) {
Session session = config.getSession();
Transaction tx = session.beginTransaction();
session.delete(session.get(Catalog.class, catalogId));
tx.commit();
session.close();
}
This is because of Cascade.ALL. If you delete a parent if would also delete all related child if you are using Cascade.ALL.
Instead ALL choose only what you need from the below
CascadeType.PERSIST: cascades the persist (create) operation to associated entities if persist() is called or if the entity is managed
CascadeType.MERGE: cascades the merge operation to associated entities if merge() is called or if the entity is managed
CascadeType.REMOVE: cascades the remove operation to associated entities if delete() is called
CascadeType.REFRESH: cascades the refresh operation to associated entities if refresh() is called
CascadeType.DETACH: cascades the detach operation to associated entities if detach() is called
CascadeType.ALL: all of the above

Categories