更换所有孩子的父母

问题描述

考虑数据库MysqL-InnoDB)中的以下状态:

car表:

cartable

wheel表:

wheel_table

使用实体:

@Entity
public class Car {
    @Id
    private String id;

    @OnetoMany(fetch = FetchType.LAZY,cascade = CascadeType.ALL,orphanRemoval = true)
    @Fetch(value = FetchMode.SUBSELECT)
    @OnDelete(action = OnDeleteAction.CASCADE)
    @JoinColumn(name = "car_id")
    private List<Wheel> wheels = new ArrayList<>();

    public Car() {
    }

    public Car(String id) {
        this.id = id;
    }

    public List<Wheel> getWheels() {
        return wheels;
    }
}

@Entity
public class Wheel {

    @Id
    private String id;

    public Wheel() {
    }

    public Wheel(String id) {
        this.id = id;
    }

    public String getId() {
        return id;
    }
}

我想要的是将所有Honda个滚轮移动到Nissan。我尝试了很多事情,包括本地查询TransactionTemplate。但是,我无法使其成功运行。 @Service的主要思想是这样(最简单的东西-我的第一次尝试-我想工作的东西):

@Service
public class CarService {
    @Autowired
    private CarRepository carRepository;

    @Transactional
    public void transferAllHondaWheelsToNissan() {
        Car nissan = carRepository.findById("Nissan").get();
        Car honda = carRepository.findById("Honda").get();

        List<Wheel> wheels = new ArrayList<>(honda.getWheels());

        wheels.forEach(w -> nissan.getWheels().add(w));

        honda.getWheels().clear();

        carRepository.save(honda);
        carRepository.save(nissan);
    }
}

调用carService.transferAllHondaWheelsToNissan不会导致异常(就像我说的那样,我已经尝试了很多方法使其正常工作,其中许多都是导致异常的原因)。但是,该轮子被删除,该表为空。我的第一个想法是这是由@OnDelete(action = OnDeleteAction.CASCADE)引起的。即使我删除了它,桌子仍然是空的,轮子也没了。

有没有办法改变方向盘的父级,使实体之间具有这种特定的关联?

是的,必须手动分配@Id中的Wheel。是的,我希望两者之间无方向性联系。

即使我将其设为双向,该表也将为空:

@OnetoMany(fetch = FetchType.LAZY,orphanRemoval = true,mappedBy = "car")
@Fetch(value = FetchMode.SUBSELECT)
@OnDelete(action = OnDeleteAction.CASCADE)
private List<Wheel> wheels = new ArrayList<>();

Wheel @Entity中:

@ManyToOne
private Car car;

@Service

@Service
public class CarService {
    @Autowired
    private CarRepository carRepository;

    @Transactional
    public void transferAllHondaWheelsToNissan() {
        Car nissan = carRepository.findById("Nissan").get();
        Car honda = carRepository.findById("Honda").get();

        honda.getWheels().forEach(w -> {
            w.setCar(nissan);
            nissan.getWheels().add(w);
        });
        honda.getWheels().clear();

        carRepository.save(honda);
        carRepository.save(nissan);
    }
}

我想遵循的概念是

  1. 将本田的车轮保持在温度变量中
  2. 本田的透明车轮
  3. save(更新)本田(提交事务???)以释放主键值
  4. 为日产添加车轮
  5. 日产商店

那么,我在做什么错了?

我知道我可以使用EntityManager方法而不使用spring的@Transactional,但是由于关系是LAZY,因此我也必须注意它。另外,在主要应用程序中,关联更加“复杂”,并且需要更多的精力来完成。

编辑:

UPDATE wheel SET car_id = 'Nissan' WHERE car_id = 'Honda'将不起作用,因为Nissan不存在(并且可以肯定)。日产也是在此过程中添加的。

解决方法

嗯,您可以使用JPA,但应该删除orphanRemoval = true或设置orphanRemoval = false

顺便说一句。您的服务可以改善一点:

这是多余的

    wheels.forEach(wheel -> {
        nissan.getWheels().add(wheel);
    });

=>

    nissan.setWheels(wheels);
,

我处理了您的用例,并亲自创建了实体并进行了检查,对我来说很好。我正在直接使用休眠模式,但这并不重要。还使用龙目岛来减少样板代码。

以下是我的代码和测试用例:

汽车实体:

@Entity
@Getter
@Table(name = "car")
@NoArgsConstructor(access = PRIVATE)
@AllArgsConstructor(staticName = "of")
public class Car {
    @Id
    private Integer id;
    private String name;

    @OneToMany(fetch = FetchType.LAZY,cascade = CascadeType.ALL,orphanRemoval = true)
    private List<Wheel> wheels = new ArrayList<Wheel>();

    public void addWheels(List<Wheel> wheels) {
        this.wheels = wheels;
    }
}

车轮实体

@Entity
@Table(name = "wheel")
@NoArgsConstructor(access = PRIVATE)
@AllArgsConstructor(staticName = "of")
public class Wheel {
    @Id
    private Integer id;

    @Column(name = "car_id")
    private Integer carId;
}

使用休眠的测试用例

// Provided the Integer ID myself but should work for other type i.e String

@Test
    void shouldTransferHondaWheelsToNissan() {
        doInTransaction(em -> {
            Car car = Car.of(1,"Honda",new ArrayList<Wheel>());

            Wheel wheel1 = Wheel.of(1,1);
            Wheel wheel2 = Wheel.of(2,1);

            car.addWheels(Arrays.asList(wheel1,wheel2));

            em.persist(car);
        });

        doInTransaction(em -> {
            Car honda = em.find(Car.class,1);
            List<Wheel> wheels = honda.getWheels();

            Car nissan = Car.of(2,"Nissan",new ArrayList<>());
            nissan.addWheels(new ArrayList<>(wheels));

            wheels.clear();

            em.persist(nissan);
        });

        doInTransaction(em -> {
            Car nissan = em.find(Car.class,2);
            Car honda = em.find(Car.class,1);

            List<Wheel> hondaWheels = honda.getWheels();

            List<Wheel> nissanWheels = nissan.getWheels();

            Assertions.assertEquals(hondaWheels.size(),0);

            Assertions.assertNotNull(nissanWheels);
            Assertions.assertEquals(nissanWheels.size(),2);
        });
    }

已执行的Sql语句:

 // Create Honda car instance
Hibernate: insert into car (name,id) values (?,?)

// Adding wheels to honda class
Hibernate: insert into wheel (car_id,?)
Hibernate: insert into wheel (car_id,?)

// when use unidirectional mapping,hibernate creates junction table
Hibernate: insert into car_wheel (Car_id,wheels_id) values (?,?)
Hibernate: insert into car_wheel (Car_id,?)

// Second transaction - Fetching honda car instance
Hibernate: select car0_.id as id1_0_0_,car0_.name as name2_0_0_ from car car0_ where car0_.id=?

// Fetching wheels for honda car instance
Hibernate: select wheels0_.Car_id as Car_id1_1_0_,wheels0_.wheels_id as wheels_i2_1_0_,wheel1_.id as id1_2_1_,wheel1_.car_id as car_id2_2_1_ from car_wheel wheels0_ inner join wheel wheel1_ on wheels0_.wheels_id=wheel1_.id where wheels0_.Car_id=?

// Creating nissan car instance
Hibernate: insert into car (name,?)

// Deleting wheels from honda
Hibernate: delete from car_wheel where Car_id=?

// Inserting into wheels of nissan
Hibernate: insert into car_wheel (Car_id,?)

// Selecting honda instance to verify
Hibernate: select car0_.id as id1_0_0_,car0_.name as name2_0_0_ from car car0_ where car0_.id=?

// Selecting nissan instance to verify
Hibernate: select car0_.id as id1_0_0_,car0_.name as name2_0_0_ from car car0_ where car0_.id=?

// select for wheels
Hibernate: select wheels0_.Car_id as Car_id1_1_0_,wheel1_.car_id as car_id2_2_1_ from car_wheel wheels0_ inner join wheel wheel1_ on wheels0_.wheels_id=wheel1_.id where wheels0_.Car_id=?

// same here
Hibernate: select wheels0_.Car_id as Car_id1_1_0_,wheel1_.car_id as car_id2_2_1_ from car_wheel wheels0_ inner join wheel wheel1_ on wheels0_.wheels_id=wheel1_.id where wheels0_.Car_id=?

我们可以清楚地看到orphanRemoval=true是有效的,就像看到delete语句,然后是insert语句进入日产汽车一样。

测试中的数据库差异在于,我在存储器测试中使用了h2

我希望这会有所帮助。