【问题标题】:When saving entity converted from a DTO, hibernate throws TransientPropertyValueException: object references an unsaved transient instance"保存从 DTO 转换的实体时,休眠会抛出 TransientPropertyValueException:对象引用了未保存的瞬态实例”
【发布时间】:2020-04-19 08:38:18
【问题描述】:

首先 - 我知道,同一个问题似乎已被问过一百万次。但是,这与 DTO 相关,而不是实体或缺少的级联。如果我自己创建一个实体并保存它,一切都很好。当我创建 DTO,使用 ModelMapper 对其进行转换,然后尝试保存转换后的实体时会出现问题。 如果您查看测试类,第一个测试(saveCarByEntity)通过但第二个(saveCarByDto)产生错误。 每个连接的类都可以在下面看到。 提前谢谢你。

实体

@Data
@Entity
public class Car {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToOne(mappedBy = "car", cascade = CascadeType.PERSIST)
    private CarDetails carDetails;
}
@Data
@Entity
public class CarDetails {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToOne(mappedBy = "carDetails", cascade = CascadeType.PERSIST)
    private Bumper bumper;

    @OneToOne
    private Car car;
}
@Data
@Entity
public class Bumper {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToOne
    private CarDetails carDetails;
}

DTO-s:

@Data
public class CarDto {
    private Long id;
    private CarDetailsDto carDetails;
}
@Data
public class CarDetailsDto {
    private Long id;
    private BumperDto bumper;
    private CarDto car;
}
@Data
public class BumperDto {
    private Long id;
    private CarDetailsDto carDetails;
}

测试类:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
class CarTests {
    @Autowired
    private ModelMapper modelMapper;

    @Autowired
    private CarRepository carRepository;

    @BeforeEach
    public void setup() {
    }

    @Test
    public void saveCarByEntity() {
        Car car = new Car();
        CarDetails carDetails = new CarDetails();
        Bumper bumper = new Bumper();

        car.setCarDetails(carDetails);
        carDetails.setCar(car);
        carDetails.setBumper(bumper);
        bumper.setCarDetails(carDetails);

        Car savedEntity = carRepository.save(car);
    }

    @Test
    public void saveCarByDto() {
        CarDto carDto = new CarDto();
        CarDetailsDto carDetails = new CarDetailsDto();
        BumperDto bumper = new BumperDto();

        carDto.setCarDetails(carDetails);
        carDetails.setCar(carDto);
        carDetails.setBumper(bumper);
        bumper.setCarDetails(carDetails);

        Car car = modelMapper.map(carDto, Car.class);

        Car savedEntity = carRepository.save(car);
    }
}

产生的错误:

nested exception is java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.somepackage.model.Bumper.carDetails -> com.somepackage.model.CarDetails

汽车存储库:

@Repository
public interface CarRepostiory extends JpaRepository<Car, Long> {
}

【问题讨论】:

  • 能否提供ModelMapperCarRepository课程的代码?
  • 也可以尝试使用cascade = CascadeType.ALL 而不是PERSIST,然后告诉我进展如何
  • 我已经尝试过使用 CascadeType.ALL 而不是 PERSIST 但没有效果。至于 ModelMapper,我不使用自定义类。我只是用了包外的modelmapper.org/user-manual上面添加了CarRepostiory类
  • @JoinColumn(name = "car_id")@JoinColumn(name = "carDetails_id")分别添加到CarDetails#carBumper#carDetails
  • 我认为你的 modelMapper 打破了父子关系,好像 Bumper 没有引用它的父“carDetails”。从错误“瞬态实例”是 Bumper 的父级,而 orm 尝试保存子级时,它看到未保存的父级(carDetails)

标签: java hibernate jpa dto modelmapper


【解决方案1】:

我不知道您的模型映射器是做什么的,但我敢打赌,如果您在没有保险杠和保险杠的情况下保留汽车细节,那么它会起作用。也许您也可以通过在 Bumper for carDetails 中使用 CascadeType.PERSIST 来使其工作?

【讨论】:

  • 将 CascadeType.PERSIST 添加到关系的双方会使 hibernate 将关系的双方保存两次,这样就不会成功。如果我要分别保留 car_details 和保险杠,我将不得不删除级联部分。
  • 持久化两个对象两次听起来不正确,因为这是在 Hibernate 中通过对象标识处理的。也许您的对象图是错误的,并且您的保险杠 carDetails 引用了与容器不同的 carDetails 对象?
  • 这是我一直在想的事情。当我在调试器上运行测试时,我发现一个有趣的事情是,如果我运行第一个测试,从头开始创建实体并保存它,CarDetails 对象具有相同的内存引用?在汽车和保险杠类中。但是,当我创建 DTO,将其映射到一个实体,然后查看内存引用时,Car 和 Bumper 类中的引用是不同的。我想我对 ModelMapper 工作原理的了解不足以理解错误是否存在。
  • 模型映射器应该如何知道您要引用同一个对象?这绝对是问题所在。您必须确保在模型映射器完成工作后引用相同的对象。
猜你喜欢
  • 2019-01-14
  • 2018-01-28
  • 2012-02-20
  • 2020-01-11
  • 2023-04-05
  • 1970-01-01
  • 2020-03-08
  • 1970-01-01
  • 2012-02-27
相关资源
最近更新 更多