(Reprint) cascade type and FetchType of JPA

Posted by nykoelle on Fri, 08 May 2020 11:58:32 +0200

Link: https://www.jianshu.com/p/b8595aee06ac

 

CascadeType

Summary

Cascade refers to cascade operation, which is annotated in hibernate configuration @OneToOne,@OneToMany,@ManyToMany , @ ManyToOne properties

Cascading is often used when writing triggers. The function of triggers is to ensure that the data in the associated table is updated synchronously when the master table information changes. If the trigger is used to modify or delete the associated table records, the corresponding associated table information must be deleted. Otherwise, dirty data will be stored. Therefore, it is appropriate to delete the main table and the information of the associated table at the same time. In hibernate, you only need to set the cascade property value

Reference resources

Set CascadeType to indicate the permission for the currently set entity to operate another entity, such as:

public class Student {
    @ManyToMany(cascade=CascadeType.PERSIST,fetch=FetchType.LAZY)
    private Set<Course> courses = new HashSet<>();
    //Other codes are omitted.
}

In the above code, student is the current entity and Course is another entity. Student entities are allowed to perform PERSIST operations on Course entities. That is, when saving student entities, the Course entities associated with them will also be saved. Without this permission, the associated Course entities cannot be saved

There are several types of operations:

  • CascadeType.PERSIST: cascading persistence (save) operations

  • Cascade type.remove: cascade delete operation

    When you delete the current entity, the entities that have a mapping relationship with it will also be deleted

  • CascadeType.MERGE: cascading update (merge) operations

    When the data in Student changes, the data in Course will be updated accordingly

  • CascadeType.DETACH: cascaded off pipe / free operation

    If you want to delete an entity, but it has a foreign key that cannot be deleted, you need this cascading permission, which will revoke all related foreign key associations

  • Cascade type.refresh: cascade refresh

    Suppose there is an order in the scenario. There are many goods associated with the order. The order can be operated by many people. Then a modifies the order and the associated goods. At the same time, B performs the same operation. But B saves the data step by step compared with A. when a saves the data, it needs to refresh the order information and the associated goods information first, Then save the order and goods

  • CascadeType.ALL: includes all the above operations

Example

This part comes from This article , add some understanding and comments, thank the original author

 
 

background

There are many online explanations about the cascade type of JPA, but almost all of them are vague. This paper attempts to use a specific example to illustrate the specific differences of CascadeType.persist, CascadeType.merge, CascadeType.refresh, CascadeType.remove and CascadeType.all.
  we use an example of order and order item. The relationship between order and order item is two-way one to many. This example is widely spread in articles on the Internet that introduce the use of JPA CascadeType.

Code

/**
 * Order controlled party
 */
@Entity
@Table(name="t_order")
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @Column
    private String name;
    @OneToMany(mappedBy="order",targetEntity=Item.class,fetch=FetchType.LAZY)
    private List<Item> items;
}

/**
 *Order item master
 */
@Entity
@Table(name="t_item")
public class Item {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @Column
    private String name;
    @ManyToOne(fetch=FetchType.LAZY,targetEntity=Order.class)
    @JoinColumn(name="order_id")
    private Order order;/** Order Repository */
public interface OrderRepository extends JpaRepository<Order, Integer>,
    JpaSpecificationExecutor<Order> {

}

/** Item Repository */
public interface ItemRepository extends JpaRepository<Item, Integer>,
    JpaSpecificationExecutor<Item> {

}

Add (save) data (CascadeType.PERSIST)

Every time a customer places an Order, they need to save the Order, but the Order contains items. Therefore, when saving the Order, the items associated with the Order also need to be saved. Using the above model, use the following test code:

@Test
public void addTest(){
    Order order = new Order();
    order.setName("order1");

    Item item1 = new Item();
    item1.setName("item1_order1");
    item1.setOrder(order);

    Item item2 = new Item();
    item2.setName("item2_order1");
    item2.setOrder(order);

    List<Item> items = new ArrayList<Item>();
    items.add(item1);
    items.add(item2);
    order.setItems(items);
    //Using orderRepo to save an order
    orderRepository.save(order);
    Assert.assertEquals(1,orderRepository.count());
    Assert.assertEquals(2,itemRepository.count());

    //Code segment 1 is saved with itemRepo
    itemRepository.save(order);
    Assert.assertEquals(1,orderRepository.count());
    Assert.assertEquals(2,itemRepository.count());

    //Code segment 2 is saved with itemRepo
    itemRepository.save(items);
    Assert.assertEquals(1,orderRepository.count());
    Assert.assertEquals(2,itemRepository.count());
}

In this scenario, we test the following situations respectively:

  1. No CascadeType settings

    order can be saved to the database, but two items cannot

  2. Add CascadeType.PERSIST to the items property of the Order class separately

    With code segment 1, both order and items can be saved to the database; with code segment 2, neither order nor items can be saved to the database

  3. Add CascadeType.PERSIST to the order property of the Item class separately

    With code segment 1,order can be saved to the database, and items cannot be saved to the database; with code segment 2, both order and items can be saved to the database

  4. Use CascadeType.PERSIST in both Order and Item classes

    With code segment 1, both order and items can be saved to the database; with code segment 2, both order and items can be saved to the database

Conclusion:

So we can know,

Use CascadeType.PERSIST on the attribute of a class. When saving the class, you can save the object corresponding to this attribute level by level;

However, when saving the object corresponding to the attribute of this class, the class cannot be saved (both cannot be saved when there is a foreign key).

However, if you use CascadeType.PERSIST in the class corresponding to this class and its attributes, you can save both from this class and the objects corresponding to this class's attributes

Delete data (CascadeType.REMOVE)

Now there is a scenario where the customer needs to delete an order, and the order items in the order also need to be deleted. In order to achieve the effect of cascade deletion, we use the following test code:

private Order order;
private List<Item> items = new ArrayList<Item>();

@Before
public void setUp(){
    order = new Order();
    order.setName("order1");
    Item item1 = new Item();

    item1.setName("item1_order1");
    item1.setOrder(order);

    Item item2 = new Item();
    item2.setName("item2_order1");
    item2.setOrder(order);

    items.add(item1);
    items.add(item2);
    order.setItems(items);

    orderRepository.save(order);
    itemRepository.save(items);
}

@Test
public void testDelete(){
    //Code snippet 3
    orderRepository.delete(order);
    Assert.assertEquals(0, orderRepository.count());
    Assert.assertEquals(0, orderRepository.count());

    //Code snippet 4
    itemRepository.delete(items);
    Assert.assertEquals(0, orderRepository.count());
    Assert.assertEquals(0, itemRepository.count());
}

In this scenario, we test the following situations respectively:

  1. CascadeType.REMOVE is not used in both Order and Item

    It is not successful to delete the order object alone, because the database has order based foreign key constraints in the item table. Deleting items alone can succeed

  2. Use CascadeType.REMOVE on the items property of the Order class

    Using code segment 3, you can delete the Item object of items in order level by level (in the process of deletion, items will be deleted first, and then order will be deleted); but using code segment 4, although items can be deleted successfully, the associated order object cannot be deleted level by level

  3. Use CascadeType.REMOVE on the order property of the Item class

    Using code segment 3, the deletion of the order object fails because there is a foreign key that references the order table in the table where the item is stored; however, using code segment 4 can successfully delete the items and their associated order objects. The process is to update the foreign key of the Order referenced in items first, and set the reference of items to order to null. Then delete the items, and then delete the order ~ ~ (Note: if the items are multiple, one item will be deleted first, then the order, and then the rest items will be deleted)

  4. Use CascadeType.REMOVE in both Order and Item

    Using code snippet 3 and code snippet 4 can be successful, which shows that when both of them use CascadeType.REMOVE, you can delete the items by deleting the order, and delete the items, and delete the order by deleting the items

Conclusion:

From the above analysis, we can see that in general business scenarios, the requirement is to delete items at the same time when deleting an order, but conversely, when deleting an item, it is also required to delete an order, which is not necessarily suitable for business scenarios. Even if you delete all items related to the order, you may need to keep the order without items. So the suggestion here is that it is better not to use CascadeType.REMOVE in both order and item, that is, it is better not to use CascadeType.REMOVE in the maintenance end of the relationship (this refers to the item class, because there will be a foreign key of order in the item table, so item is the maintenance end of the relationship)

Update data (CascadeType.MERGE)

In business, there is often a similar need: after finding a business entity, you need to update the entity, as well as other business entities associated with the entity. In our case, we need to update both the Order and the Item it is associated with. We use the following test code:

private Order order;
private List<Item> items = new ArrayList<Item>();

@Before
public void setUp(){
    order = new Order();
    order.setName("order1");
    Item item1 = new Item();

    item1.setName("item1_order1");
    item1.setOrder(order);

    Item item2 = new Item();
    item2.setName("item2_order1");
    item2.setOrder(order);

    items.add(item1);
    items.add(item2);
    order.setItems(items);

    orderRepository.save(order);
    itemRepository.save(items);
}


@Test
public void testUpdate(){
    order.setName("order1_updated");

    items.get(0).setName("item1_order1_updated");
    items.get(1).setName("item2_order1_updated");

    //Code snippet 5
    orderRepository.save(order);
    Assert.assertEquals(1, orderRepository.count(new Specification<Order>(){
        public Predicate toPredicate(Root<Order> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
            return cb.equal(root.get("name").as(String.class), "order1_updated");
        }
    }));
    Assert.assertEquals(1, itemRepository.count(new Specification<Item>() {

        public Predicate toPredicate(Root<Item> root,CriteriaQuery<?> cq, CriteriaBuilder cb) {
            return cb.equal(root.get("name").as(String.class), "item1_order1_updated");
        }
    }));

    //Code snippet 6
    itemRepository.save(items);
    Assert.assertEquals(1, itemRepository.count(new Specification<Item>() {
        public Predicate toPredicate(Root<Item> root,CriteriaQuery<?> cq, CriteriaBuilder cb) {
            return cb.equal(root.get("name").as(String.class), "item1_order1_updated");
        }
    }));
    Assert.assertEquals(1, orderRepository.count(new Specification<Order>(){
        public Predicate toPredicate(Root<Order> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
            return cb.equal(root.get("name").as(String.class), "order1_updated");
        }
    }));
}

In this scenario, we test the following situations respectively:

  1. CascadeType.MERGE is not used in both Order and Item

    Using code segment 5, the test results show that the update order is successful, but there is no cascading update of items; using code segment 6, the update of items is successful, but there is no cascading update of the order object associated with items

  2. Use CascadeType.MERGE on the items property of Order separately

    Using code segment 5, the test results show that the update order is successful, and even the level update items are successful; using code segment 6, the test results show that the update items are successful, but the level update order is not

  3. Use CascadeType.MERGE on the Item's property order separately

    Using code segment 5, the test results show that when updating items, the order object associated with them can be updated level by level; using code segment 6, the update of items is successful, but the order object associated with items is not updated level by level

  4. Use CascadeType.MERGE in both Order and Item

    Using code snippet 5 and code snippet 6 can be successful, which shows that when both of them use CascadeType.MERGE, you can update both the order and the items at the same time, or you can update both the items and the order at the same time

Conclusion:

Through the above analysis, we can learn that by using CascadeType.MERGE, we can update the data at one end of the relationship by updating the objects at the same time

Refresh data (CascadeType.REFRESH)

The refresh data here corresponds to the following business scenario: for A business system, half of the users will have multiple users. If user A obtains the order and its corresponding items, and modifies the order and items, user B also does the same, but user B saves the data first, and then user A needs to refresh the items associated with the order before user A saves the data Changes to the database. This scenario corresponds to the requirements of CascadeType.REFRESH

FetchType

Set load policy for associated objects

The significance and difference of FetchType optional values are as follows:

  • FetchType.LAZY: lazy load, load when accessing associated objects (that is, read in memory from database)

    General manytomany, used in onetomany relationship

  • FetchType.EAGER: load immediately, and load the associated object when querying the main object

    Used in general onetoone, manytoone relationship

Topics: Database Attribute Hibernate REST