In this post, we will see what are JPA cascade types with an example.
JPA Cascade Types
JPA allows you to propagate the state transition from a parent entity to a child. For this purpose, the JPA javax.persistence.CascadeType defines various cascade types:
- ALL - cascades all entity state transitions
- PERSIST - cascades the entity's persist operation.
- MERGE - cascades the entity merge operation.
- REMOVE - cascades the entity to remove operation.
- REFRESH - cascades the entity refresh operation.
- DETACH - cascades the entity detach operation.
JPA Cascade Types with Examples
The following examples will explain some of the aforementioned cascade operations using the following entities:
@Entity
public class Person {
@Id
private Long id;
private String name;
@OneToMany(mappedBy = "owner", cascade = CascadeType.ALL)
private List < Phone > phones = new ArrayList < > ();
//Getters and setters are omitted for brevity
public void addPhone(Phone phone) {
this.phones.add(phone);
phone.setOwner(this);
}
}
@Entity
public class Phone {
@Id
private Long id;
@Column(name = "`number`")
private String number;
@ManyToOne(fetch = FetchType.LAZY)
private Person owner;
//Getters and setters are omitted for brevity
}
CascadeType.PERSIST
The CascadeType.PERSIST allows us to persist a child entity along with the parent one.
CascadeType.PERSIST example
Person person = new Person();
person.setId( 1L );
person.setName( "John Doe" );
Phone phone = new Phone();
phone.setId( 1L );
phone.setNumber( "123-456-7890" );
person.addPhone( phone );
entityManager.persist( person );
The above hibernate code prints following SQL statements at the console:
INSERT INTO Person ( name, id )
VALUES ( 'John Doe', 1 )
INSERT INTO Phone ( `number`, person_id, id )
VALUE ( '123-456-7890', 1, 1 )
From above SQL statements proves that when a Person persists into a database it also persists it's child Phone object.
CascadeType.MERGE
The CascadeType.MERGE allows us to merge a child entity along with the parent one.
CascadeType.MERGE example
Phone phone = entityManager.find( Phone.class, 1L );
Person person = phone.getOwner();
person.setName( "John Doe Jr." );
phone.setNumber( "987-654-3210" );
entityManager.clear();
entityManager.merge( person );
The above hibernate code prints following SQL statements at the console:
SELECT
p.id as id1_0_1_,
p.name as name2_0_1_,
ph.owner_id as owner_id3_1_3_,
ph.id as id1_1_3_,
ph.id as id1_1_0_,
ph."number" as number2_1_0_,
ph.owner_id as owner_id3_1_0_
FROM
Person p
LEFT OUTER JOIN
Phone ph
on p.id=ph.owner_id
WHERE
p.id = 1
During a merge, the current state of the entity is copied onto the entity version that was just fetched from the database. That’s the reason why Hibernate executed the SELECT statement which fetched both the Person entity along with its children.
CascadeType.REFRESH
The CascadeType.REFRESH is used to propagate the refresh operation from a parent entity to a child. The refresh operation will discard the current entity state, and it will override it using the one loaded from the database.
CascadeType.REFRESH example
Person person = entityManager.find( Person.class, 1L );
Phone phone = person.getPhones().get( 0 );
person.setName( "John Doe Jr." );
phone.setNumber( "987-654-3210" );
entityManager.refresh( person );
assertEquals( "John Doe", person.getName() );
assertEquals( "123-456-7890", phone.getNumber() );
The above hibernate code prints following SQL statements at the console:
SELECT
p.id as id1_0_1_,
p.name as name2_0_1_,
ph.owner_id as owner_id3_1_3_,
ph.id as id1_1_3_,
ph.id as id1_1_0_,
ph."number" as number2_1_0_,
ph.owner_id as owner_id3_1_0_
FROM
Person p
LEFT OUTER JOIN
Phone ph
ON p.id=ph.owner_id
WHERE
p.id = 1
In the aforementioned example, you can see that both the Person and Phone entities are refreshed even if we only called this operation on the parent entity only.
Person person = entityManager.find( Person.class, 1L );
Phone phone = person.getPhones().get( 0 );
person.setName( "John Doe Jr." );
phone.setNumber( "987-654-3210" );
entityManager.refresh( person );
assertEquals( "John Doe", person.getName() );
assertEquals( "123-456-7890", phone.getNumber() );
SELECT
p.id as id1_0_1_,
p.name as name2_0_1_,
ph.owner_id as owner_id3_1_3_,
ph.id as id1_1_3_,
ph.id as id1_1_0_,
ph."number" as number2_1_0_,
ph.owner_id as owner_id3_1_0_
FROM
Person p
LEFT OUTER JOIN
Phone ph
ON p.id=ph.owner_id
WHERE
p.id = 1
CascadeType.REMOVE
The CascadeType.REMOVE allows us to remove a child entity along with the parent one. Traditionally, Hibernate called this operation delete, that’s why the org.hibernate.annotations.CascadeType provides a DELETE cascade option. However, CascadeType.REMOVE and org.hibernate.annotations.CascadeType.DELETE is identical.
CascadeType.REMOVE example
Person person = entityManager.find( Person.class, 1L );
entityManager.remove( person );
The above hibernate code prints following SQL statements at the console:
DELETE FROM Phone WHERE id = 1
DELETE FROM Person WHERE id = 1
CascadeType.DETACH
CascadeType.DETACH is used to propagate the detach operation from a parent entity to a child.
CascadeType.DETACH example
Person person = entityManager.find( Person.class, 1L );
assertEquals( 1, person.getPhones().size() );
Phone phone = person.getPhones().get( 0 );
assertTrue( entityManager.contains( person ));
assertTrue( entityManager.contains( phone ));
entityManager.detach( person );
assertFalse( entityManager.contains( person ));
assertFalse( entityManager.contains( phone ));
References
Hibernate Framework
Java
JPA
Comments
Post a Comment