JPA中orphanRemoval与级联删除的使用效果对比


3个月前 223次点击 来自 后端

标签: JPA

有以下类:

  • 汽车 Car
  • 车轮 Wheel

Car 和 Wheel 的关系是一对多,使用了 mappedBy 属性,表示该类放弃主键的维护

@Entity
@Setter
@Getter
@Table(name = "t_wheel")
public class Wheel {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private Integer number;

    @ManyToOne
    private Car car;
}

@Entity
@Setter
@Getter
@Table(name = "t_car")
public class Car {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToMany(mappedBy = "car")
    private List<Wheel> wheels;
}

测试添加1辆汽车 4个轮子:

@Test
@Transactional
@Rollback(false)
void testNewCar() {
    //生产汽车
    Car car = new Car();
    car.setName("BMW");
    carRepository.save(car);
    //生产4个轮子
    for (int i = 1; i <= 4; i++) {
        Wheel wheel = new Wheel();
        wheel.setNumber(i);
        wheel.setCar(car);
        //没有设置级联关系,得手动持久化wheel
        wheelRepository.save(wheel);
    }
}

SQL:

Hibernate: insert into t_car (name) values (?)
Hibernate: insert into t_wheel (car_id, number) values (?, ?)
Hibernate: insert into t_wheel (car_id, number) values (?, ?)
Hibernate: insert into t_wheel (car_id, number) values (?, ?)
Hibernate: insert into t_wheel (car_id, number) values (?, ?)

此时数据库中Wheel维护了一个外键car_id,如果你尝试直接在数据库中删除新增的Car,你会得到报错:

Unexppected number of rows removed!

One row was not removed. Reload the table to be sure that the contents have not changed in the meantime. Check the Console for possible errors inside the primary key(s) of this table!

只有当你删除了 Car 关联的所有 Wheel 数据才能将 Car 删除

车轮坏了

一辆车有4个轮子,假设有这样的场景:当一辆车的某一个车轮坏掉,我们强制报废汽车。

1. 一般做法:

  1. 找到Car关联的所有Wheel并将外键置空
  2. 删除Car
    @Test
    @Transactional
    @Rollback(false)
    void testDelCar() {
        Car car = carRepository.getById(9L);
        List<Wheel> wheels = car.getWheels();
        wheels.forEach(wheel -> wheel.setCar(null));
        carRepository.delete(car);
    }

SQL:

Hibernate: select car0_.id as id1_25_0_, car0_.name as name2_25_0_ from t_car car0_ where car0_.id=?
Hibernate: select wheels0_.car_id as car_id3_32_0_, wheels0_.id as id1_32_0_, wheels0_.id as id1_32_1_, wheels0_.car_id as car_id3_32_1_, wheels0_.number as number2_32_1_ from t_wheel wheels0_ where wheels0_.car_id=?
Hibernate: update t_wheel set car_id=?, number=? where id=?
Hibernate: update t_wheel set car_id=?, number=? where id=?
Hibernate: update t_wheel set car_id=?, number=? where id=?
Hibernate: update t_wheel set car_id=?, number=? where id=?
Hibernate: delete from t_car where id=?

2. 使用 orphanRemoval

orphanRemoval 这个属性只存在两类关系注解中 @OneToOne@OneToMany
使用此属性可达到类似级联删除的效果,也就是在没有设置 CascadeType.REMOVE的情况下,
删除Car的同时删除所有与Car相关联的Wheel

修改Car.java

@OneToMany(mappedBy = "car",orphanRemoval = true)
private List<Wheel> wheels;

测试

@Test
@Transactional
@Rollback(false)
void testDelCarOrphan() {
    Car car = carRepository.getById(10L);
    carRepository.delete(car);
}

SQL

Hibernate: select car0_.id as id1_25_0_, car0_.name as name2_25_0_ from t_car car0_ where car0_.id=?
Hibernate: select wheels0_.car_id as car_id3_32_0_, wheels0_.id as id1_32_0_, wheels0_.id as id1_32_1_, wheels0_.car_id as car_id3_32_1_, wheels0_.number as number2_32_1_ from t_wheel wheels0_ where wheels0_.car_id=?
Hibernate: delete from t_wheel where id=?
Hibernate: delete from t_wheel where id=?
Hibernate: delete from t_wheel where id=?
Hibernate: delete from t_wheel where id=?
Hibernate: delete from t_car where id=?

3. 设置级联

修改Car.java

@OneToMany(mappedBy = "car", cascade = {CascadeType.PERSIST, CascadeType.REMOVE})
private List<Wheel> wheels;

测试级联保存与级联删除

@Test
@Transactional
@Rollback(false)
void testNewCarWithCascade() {
    /**
     * 设置了级联关系
     */
    //生产汽车
    Car car = new Car();
    car.setName("BMW");
    car.setWheels(new ArrayList<>());
    //生产4个轮子
    for (int i = 1; i <= 4; i++) {
        Wheel wheel = new Wheel();
        wheel.setNumber(i);
        wheel.setCar(car);
        car.getWheels().add(wheel);
    }
    carRepository.save(car);
}

@Test
@Transactional
@Rollback(false)
void testDelCarWithCascade() {
    Car car = carRepository.getById(11L);
    carRepository.delete(car);
}

SQL

Hibernate: select car0_.id as id1_25_0_, car0_.name as name2_25_0_ from t_car car0_ where car0_.id=?
Hibernate: select wheels0_.car_id as car_id3_32_0_, wheels0_.id as id1_32_0_, wheels0_.id as id1_32_1_, wheels0_.car_id as car_id3_32_1_, wheels0_.number as number2_32_1_ from t_wheel wheels0_ where wheels0_.car_id=?
Hibernate: delete from t_wheel where id=?
Hibernate: delete from t_wheel where id=?
Hibernate: delete from t_wheel where id=?
Hibernate: delete from t_wheel where id=?
Hibernate: delete from t_car where id=?

Made with in Shangrao,China By Devler.

Copyright © Devler 2012 - 2022

赣ICP备19009883号-1

Top ↑