保存在OneToMany关系中时,JPA不清除Set迭代器

时间:2018-01-20 13:25:29

标签: java spring jpa persistence javax

我有一个基本的SpringBoot应用程序。使用Spring Initializer,JPA,嵌入式Tomcat,Thymeleaf模板引擎和包作为可执行的JAR文件。

我有这个域类:

    Entity
    @DiscriminatorValue("sebloc")
    public class SeblocDevice extends Device {

        /**
         * 
         */
        private static final long serialVersionUID = 1L;


        public SeblocDevice() {
            super();
        }

        public SeblocDevice(String deviceKey, String devicePAC) {
            super(deviceKey, devicePAC);
        }

        @OneToMany(mappedBy = "device", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
        private Set<DeviceDriver> driverDevices = new HashSet<>();

        public Set<DeviceDriver> getDriverDevices() {
            return driverDevices;
        }

        public void setDriverDevices(Set<DeviceDriver> driverDevices) {
            this.driverDevices = driverDevices;
        }

      public void clearDriverDevices()  {
    for (DeviceDriver deviceDriver : deviceDrivers) {
        deviceDriver.setDriver(null);
        driverDevices.remove(deviceDriver);
    }

public void removeDriverDevice(DeviceDriver deviceDriver)  {
    deviceDriver.setDriver(null);
    driverDevices.remove(deviceDriver);
}
}
    ...
    }

和其他域对象

@Entity
@Table(name = "t_device_driver")
public class DeviceDriver implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    public DeviceDriver() {

    }

    public DeviceDriver (SeblocDevice device, Driver driver) {
        this.device = device;
        this.driver = driver;
    }


    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "device_id")
    private SeblocDevice device;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "driver_id")
    private Driver driver;

    public SeblocDevice getDevice() {
        return device;
    }

    public void setDevice(SeblocDevice device) {
        this.device = device;
    }

    public Driver getDriver() {
        return driver;
    }

    public void setDriver(Driver driver) {
        this.driver = driver;
    }
}

和这个JUnit测试,在最后一个测试中,我除了1个驱动程序,但我得到2个(清除所有驱动程序,并添加1个)

    @Test
    public void testUpdateAuthorizedDriver() {

        SeblocDevice seblocDevice = (SeblocDevice) deviceService.findById((long)1);

        assertEquals (1,seblocDevice.getDriverDevices().size());

        Driver authorizedDriver   = (Driver) driverService.findById((long)2);

        DeviceDriver dd  = new DeviceDriver (seblocDevice, authorizedDriver);

DeviceDriver ddToRemove = seblocDevice.getDeviceDrivers().iterator().next();

        seblocDevice.removeDriverDevice(ddToRemove);

        seblocDevice.clearDriverDevices()
        seblocDevice.getDriverDevices().clear();
        seblocDevice.getDriverDevices().add(dd);

        deviceService.save(seblocDevice);

        assertEquals (1,    seblocDevice.getDriverDevices().size());
        assertEquals (1,    Iterators.size(deviceService.findSeblocDeviceAll().iterator()));

        SeblocDevice seblocDeviceRetrieved  = deviceService.findSeblocDeviceAll().iterator().next();

        assertEquals (1,    seblocDeviceRetrieved.getDriverDevices().size());

    }

我还试图在服务水平

中创建一个方法
public interface DeviceDriverRepository extends CrudRepository<DeviceDriver, Long> {

}
@Transactional
     public SeblocDevice cleanDrivers (SeblocDevice seblocDevice) {
         deviceDriverRepository.delete(seblocDevice.getDeviceDrivers());
     seblocDevice.getDeviceDrivers().clear();
     seblocDevice.setDeviceDrivers(null);
     return seblocDeviceRepository.save (seblocDevice);
     }

然后deviceService.cleanDrivers(seblocDevice);

但驱动程序再次出现

2 个答案:

答案 0 :(得分:1)

crizzis是对的,你必须将设备设置为空。

保持双向关联一致的最佳方法是创建方便的方法,如:

public void addDriverDevice(DeviceDriver deviceDriver)  {
    deviceDriver.setDriver(deviceDriver);
    driverDevices.add(deviceDriver);
}

public void removeDriverDevice(DeviceDriver deviceDriver)  {
    deviceDriver.setDriver(null);
    driverDevices.remove(deviceDriver);
}

如果你想清除所有

public void clearDriverDevices()  {
    for (DeviceDriver deviceDriver : deviceDrivers) {
        deviceDriver.setDriver(null);
        driverDevices.remove(deviceDriver);
    }
}

答案 1 :(得分:0)

为了让您的代码按预期工作,您需要在orphanRemoval=true属性中的@OneToMany关系中添加SeblocDevice.driverDevices参数,如下所示:

@OneToMany(mappedBy = "device", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
    private Set<DeviceDriver> driverDevices = new HashSet<>();

清楚地了解JPA映射。你需要记住,在一段关系中,始终存在所有者方面。 例如,在@OneToMany@ManyToOne关系中,@ManyToOne是所有者,因为它具有对其他实体的引用。

基本上,实体管理器只关心所有者方面的更改,即如果您调用DeviceDriver.setDevice(null),则将执行删除。但恰恰相反  (SeblocDevice.getDriverDevices().clear())不是真的。

出于这个原因,有 orphanRemoval 参数,这是自我解释的。分配此参数后,实体管理器现在将作为所有者控制集合的元素,SeblocDevice.getDriverDevices().clear()将删除数据库中不在SeblocDevice.getDriverDevices集合中的DeviceDrivers,即使DeviceDriver.device 1}}不为空。