Spring Boot 使用ModelMapper更新托管实体上的子引用的正确方法是什么?

mm9b1k5b  于 2023-03-08  发布在  Spring
关注(0)|答案(1)|浏览(144)

我正在使用ModelMapper通过REST/API Spring微服务将DTOMap到数据库实体,在更新对子实体的引用时遇到了一个问题。在我的服务实现中,我首先使用JPA/Hibernate获取实体,将其添加到持久性上下文。子属性被急切地获取,因此整个实体对象被附加。一旦我拥有了实体,我正在使用ModelMapper用DTO更新它。我有一个ModelMapper配置,它定义了DTO上的ID到子实体上的ID的Map。初始创建工作正常,但如果子实体已经与父实体关联,并且我试图更新ID,Hibernate的抱怨是正确的,所以我改变了一个示例的标识符。我只关心引用,我没有通过这个更新来更新Dormitory实体上的任何数据。
这些实体并不完全是我正在使用的实体,但我尝试用它们来说明。假设我有一个学生实体,它与宿舍相关联。在数据库中,它与Student #123关联的是Dormitory #456。我传入的DTO将该关联更改为Dormitory #789。

public class Student {
  @Id
  @Column(name = "STUDENT_ID")
  private Long studentID;

  @ManyToOne(fetch = FetchType.EAGER)
  @JoinColumn(name = "DORMITORY_ID")
  private Dormitory dormitory;

  ... getters and setters
}

public class Dormitory {
  @Id
  @Column(name = "DORMITORY_ID")
  private Long dormitoryID;

  @Column(name = "DORMITORY_NAME")
  private String dormitoryName;

  ... getters and setters
}

public class StudentDTO {
  private Long studentID;
  private Long dormitoryID;

  ... getters and setters
}


public class StudentServiceImpl implements StudentService {
 
  @Autowired
  private StudentRepository studentRepo;

  @Autowired
  ModelMapper modelMapper;

  public void updateStudent(StudentDTO studentDTO) {
    Student studentToUpdate = studentRepo.findById(studentDTO.getStudentID());
    studentToUpdate.setDormitory(null);  // Doing this works but doesn't seem right.
    modelMapper.map(studentDTO, student)
    studentRepo.saveAndFlush(student);
  }

}

public class ModelMapperConfig {

  @Bean
  public ModelMapper modelMapper() {
    ModelMapper modelMapper = new ModelMapper();
    modelMapper.getConfiguration().setDeepCopyEnabled(true);
  
    PropertyMap<StudentDTO, Student> map = new PropertyMap<StudentDTO, Student>() {
      protected void configure() {
        map(source.getDormitoryID(), destination.getDormitory().getDormitoryID());
      }
    };
    modelMapper.addMappings(map)

  }
}

如果我在MapDTO之前将父实体上的子实体置为空,则会正常工作,因为新实体随后会通过ModelMapper创建。但这似乎不是正确的方法,我认为我做错了什么。

icnyk63a

icnyk63a1#

我对ModelMapper了解不多,但从您的描述来看,简单的JavaBeanMap器对于您的用例来说是远远不够的,我认为这是一个完美的Blaze-Persistence实体视图用例。
我创建这个库是为了允许在JPA模型和定制接口或抽象类定义的模型之间进行简单的Map,就像SpringDataProjectionson类固醇一样。(域模型)并Map属性(getters)Map到实体模型。然后,您可以基于该Map获取数据或将其刷新回来,并且库负责如何以最有效的方式刷新它的细节。
使用Blaze-Persistence Entity-Views时,您的用例的DTO模型可能如下所示:

@EntityView(Student.class)
@UpdatableEntityView
public interface StudentDto {
    @IdMapping
    Long getStudentID();
    DormitoryDto getDormitory();
    void setDormitory(DormitoryDto dormitory);
    default void setDormitoryId(Long dormitoryId) {
        setDormitory(evm().getReference(DormitoryDto.class, dormitoryId));
    }

    // Special method to access context
    EntityViewManager evm();

    @EntityView(Dormitory.class)
    @UpdatableEntityView
    interface DormitoryDto {
        @IdMapping
        Long getDormitoryID();
    }
}

查询就是将实体视图应用到查询中,最简单的查询就是按id查询。
StudentDto a = entityViewManager.find(entityManager, StudentDto.class, id);
Spring Data 集成允许您像使用Spring Data 投影一样使用它:www.example.comhttps://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

Page<StudentDto> findAll(Pageable pageable);

还允许您简单地刷新数据

void save(StudentDto student);

最好的部分是,它只获取实际需要的状态!

相关问题