模型映射器 - 使用自定义方法

发布于 2025-01-20 13:28:15 字数 2995 浏览 0 评论 0原文

我需要使用父母的提名字段和每个孩子的最新的指定字段来构造仪表板视图的DTO。

实体是plane,它与应答器,维护检查和发射器有一致的关系。

平面

@Entity
@Data
public class Plane {
    @Id
    @GeneratedValue(strategy = IDENTITY)
    private Long id;
    private String registration;
    @JsonIgnore
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "plane")
    private List<Transponder> listTransponder = new ArrayList<>();
    @JsonIgnore
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "plane")
    private List<Transmitter> listTransmitter = new ArrayList<>();
    @JsonIgnore
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "plane")
    private List<MaintCheck> listMaintCheck = new ArrayList<>();

应答器

@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Transponder {
    @Id
    @GeneratedValue(strategy = IDENTITY)
    private Long id;
    private String code;
    private LocalDate dateInserted;
    @ManyToOne(fetch = FetchType.LAZY, optional = true)
    private Plane plane;
}

维护检查发射器具有与局部达到字段相似的实体。

我试图在服务层中汇总此计划的likike

@Data
public class PlaneDTO {
private String registration;
private LocalDate maintCheck;      // date of most recent Maint Check
private String transponderCode;    // string of most recent Transponder code 
private Integer channel;           // Intger of most recent Transmitter Freq     
}

,但是我正在手动完成发夹,发射器和维护检查列表的大部分排序,以从这些列表中获取最新记录。

//DRAFT METHOD CONSTRUCT DTO
@Override
public PlaneSummaryDTO getPlaneSummaryDTOById(Long id) {
  Plane Plane = this.get(id);
  PlaneSummaryDTO PlaneSummaryDTO = new PlaneSummaryDTO();
    ModelMapper mapper = new ModelMapper();
    PlaneSummaryDTO = modelMapper.map(get(id), PlaneSummaryDTO.class);
    PlaneSummaryDTO.setTRANSPONDERCode(getNewestTRANSPONDERCode(Plane));
    PlaneSummaryDTO.setLastMaintCheck(getNewestMaintCheckDate(Plane));
    PlaneSummaryDTO.setChannel(getTransmitterCode(Plane));
    PlaneSummaryDTO.setChannelOffset(getTransmitterOffset(Plane));
    return PlaneSummaryDTO;
}

// RETURN NEWEST DATE OF MAINT CHECK BY CATCH DATE
public LocalDate getNewestMaintCheckDate(Plane Plane) {
    List<MaintCheck> listMaintCheck = new ArrayList<>(Plane.getListMaintCheck());
    MaintCheck newest = listMaintCheck.stream().max(Comparator.comparing(MaintCheck::getCatchDate)).get();
    return newest.getCatchDate();
}

// RETURN NEWEST TRANSPONDER CODE FROM Plane BY DATE INSERTED
public String getNewestTransponderCode(Plane Plane) {
    List<Transponder> listTransponder = new ArrayList<>(Plane.getListTransponder());
    Transponder newest = listTransponder.stream().max(Comparator.comparing(Transponder::getDateInserted)).get();
    return newest.getCode();
}

// OTHER METHODS TO GET MOST RECENT RECORD

问题是否有更好的方法可以更有效地使用模型映射器来计算孩子的最新记录(自定义方法?),

如果更好地支持获得最新的孩子,我愿意更改映射。

A springboot project where I need to construct a DTO for a dashboard view using nominated fields from the parent and nominated fields from the newest of each of the children.

The entities are Plane which has a OneToMany relationship with Transponder, Maint Check and Transmitter.

Plane

@Entity
@Data
public class Plane {
    @Id
    @GeneratedValue(strategy = IDENTITY)
    private Long id;
    private String registration;
    @JsonIgnore
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "plane")
    private List<Transponder> listTransponder = new ArrayList<>();
    @JsonIgnore
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "plane")
    private List<Transmitter> listTransmitter = new ArrayList<>();
    @JsonIgnore
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "plane")
    private List<MaintCheck> listMaintCheck = new ArrayList<>();

Transponder

@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Transponder {
    @Id
    @GeneratedValue(strategy = IDENTITY)
    private Long id;
    private String code;
    private LocalDate dateInserted;
    @ManyToOne(fetch = FetchType.LAZY, optional = true)
    private Plane plane;
}

Maint Check and Transmitter have similar entities with a LocalDate field.

PlaneDTO looks liike

@Data
public class PlaneDTO {
private String registration;
private LocalDate maintCheck;      // date of most recent Maint Check
private String transponderCode;    // string of most recent Transponder code 
private Integer channel;           // Intger of most recent Transmitter Freq     
}

I have attempted to consruct this PlaneDTO in the service layer, but I am manually doing much of the sorting of the lists of Transponder, Transmitter and Maint Check to get the most recent record from these lists.

//DRAFT METHOD CONSTRUCT DTO
@Override
public PlaneSummaryDTO getPlaneSummaryDTOById(Long id) {
  Plane Plane = this.get(id);
  PlaneSummaryDTO PlaneSummaryDTO = new PlaneSummaryDTO();
    ModelMapper mapper = new ModelMapper();
    PlaneSummaryDTO = modelMapper.map(get(id), PlaneSummaryDTO.class);
    PlaneSummaryDTO.setTRANSPONDERCode(getNewestTRANSPONDERCode(Plane));
    PlaneSummaryDTO.setLastMaintCheck(getNewestMaintCheckDate(Plane));
    PlaneSummaryDTO.setChannel(getTransmitterCode(Plane));
    PlaneSummaryDTO.setChannelOffset(getTransmitterOffset(Plane));
    return PlaneSummaryDTO;
}

// RETURN NEWEST DATE OF MAINT CHECK BY CATCH DATE
public LocalDate getNewestMaintCheckDate(Plane Plane) {
    List<MaintCheck> listMaintCheck = new ArrayList<>(Plane.getListMaintCheck());
    MaintCheck newest = listMaintCheck.stream().max(Comparator.comparing(MaintCheck::getCatchDate)).get();
    return newest.getCatchDate();
}

// RETURN NEWEST TRANSPONDER CODE FROM Plane BY DATE INSERTED
public String getNewestTransponderCode(Plane Plane) {
    List<Transponder> listTransponder = new ArrayList<>(Plane.getListTransponder());
    Transponder newest = listTransponder.stream().max(Comparator.comparing(Transponder::getDateInserted)).get();
    return newest.getCode();
}

// OTHER METHODS TO GET MOST RECENT RECORD

QUESTION Is there a better way to calculate the most recent record of the child, using model mapper more efficiently (custom method?)

I am open to changing to MapStruct if it better supports getting the most recent child.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

菩提树下叶撕阳。 2025-01-27 13:28:15

我过去曾短暂使用过 ModelMapper。我建议使用mapstruct,因为我个人发现它更容易使用。我知道你的映射可以在那里完成;)。在 Mapstruct 中,您的映射器可能如下所示:

@MapperConfig(
        componentModel = "spring",
        builder = @Builder(disableBuilder = true)
)
public interface PlaneMapper {

    @Mapping(target = "lastMaintCheck", ignore = true)
    PlaneDTO planeToPlaneDTO(Plane plane);

    @AfterMapping
    default void customCodePlaneMapping(Plane source, @MappingTarget PlaneDTO target) {
        target.setLastMaintCheck(source.getListMaintCheck.stream().max(Comparator.comparing(Transponder::getDateInserted)).get())
   }

您的映射器调用将只有一行:

@Service
@RequiuredArgsConstructor
public class someService{

    private final PlaneMapper planeMapper;

    public void someMethod(){
        ....
        PlaneDTO yourMappedPlaneDTO = planeMapper.planeToPlaneDTO(plane);
        ....
    }

我没有填写所有值。但我希望这个概念是清楚的。

编辑:

您还必须添加“mapstruct-processor”的依赖项,以便可以生成 MapperImpl 类。

    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct-processor</artifactId>
        <version>${org.mapstruct.version}</version>
        <scope>provided</scope>
        <optional>true</optional>
    </dependency>

I briefly used ModelMapper in the past. I would suggest using mapstruct since I personaly find it easier to use. I know your mapping can be done there ;). In Mapstruct your Mapper could look something like this:

@MapperConfig(
        componentModel = "spring",
        builder = @Builder(disableBuilder = true)
)
public interface PlaneMapper {

    @Mapping(target = "lastMaintCheck", ignore = true)
    PlaneDTO planeToPlaneDTO(Plane plane);

    @AfterMapping
    default void customCodePlaneMapping(Plane source, @MappingTarget PlaneDTO target) {
        target.setLastMaintCheck(source.getListMaintCheck.stream().max(Comparator.comparing(Transponder::getDateInserted)).get())
   }

Your mapper call would then only be one line:

@Service
@RequiuredArgsConstructor
public class someService{

    private final PlaneMapper planeMapper;

    public void someMethod(){
        ....
        PlaneDTO yourMappedPlaneDTO = planeMapper.planeToPlaneDTO(plane);
        ....
    }

I did not fill in all values. But i hope the concept is clear.

EDIT:

You would also have to add the dependency of "mapstruct-processor" so that the MapperImpl classes can be gererated.

    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct-processor</artifactId>
        <version>${org.mapstruct.version}</version>
        <scope>provided</scope>
        <optional>true</optional>
    </dependency>
时光磨忆 2025-01-27 13:28:15

因此,这里有一种不同的数据模型方法。我将使用应答器,因为其他都是模拟的。

目标域模型可能如下所示:

@Data
class Plane {
 Long id;
 String registration;
 Transponder activeTransponder;
}
@Data
class Transponder {
 Long id;
 Integer code; // this is a 4-digit octal number, why String? your call though
 Long planeId; 
 Instant assignStart;
 Instant assignEnd;
}

在数据库中,存储飞机的 ID 和注册就足够了,因为您可以通过对数据库实体进行适当的查询来确定当前的应答器,例如 where transponder。 planeId=id 且 transponder.assignEnd IS NULL。当然,您也可以存储 transponderId,但是您需要注意保持表之间的数据一致。

如果您想要所有应答器的历史记录 - 这对我来说似乎是一个完全不同的用例,您可以使用查询 getTransponderHistoryByPlane(long planId) 轻松地在单独的服务中检索它,查询如下来自应答器 t,其中 t.planeId=$planeId 按 t.assignStart 排序

同样,这确实取决于您的用例,并假设您通常只需要给定平面一个应答器,除非特殊情况,例如来自不同端点的情况。

不管怎样,这是我对领域模型的想法,而你的目标是 Dto;然而,这可以很容易地用mapstruct来映射(假设你对maintCheck和发射器做同样的事情)

@Mapper
interface PlaneDtoMapper {
  @Mapping(target = "transponderCode", source = "transponder.code")
  @Mapping(target = "maintCheck", source = "maintCheck.date")
  @Mapping(target = "channel", source = "transmitter.channel")
  PlaneDTO fromPlane(Plane p);
}

你不需要“注册”映射,因为plane和dto中的字段是相同的,并且@Mapping< /code> 注释告诉mapstruct 使用plane 的哪些字段的哪些子字段。

So here's a different approach to the data model. I'll be using Transponder because the others are analog.

The target domain model could look like this:

@Data
class Plane {
 Long id;
 String registration;
 Transponder activeTransponder;
}
@Data
class Transponder {
 Long id;
 Integer code; // this is a 4-digit octal number, why String? your call though
 Long planeId; 
 Instant assignStart;
 Instant assignEnd;
}

In the database, it would be sufficient to store the id and registration for the plane, because you can determine the current transponder with a proper query on the db entity, eg where transponder.planeId=id and transponder.assignEnd IS NULL. You can also store the transponderId of course, but then you'd need to take care to keep the data consistent between the tables.

If you want a history of all transponders - which to me seems like an entirely different use case to me, you can easily retrieve it in a separate service with a query getTransponderHistoryByPlane(long planeId) with a query like from transponders t where t.planeId=$planeId sorted by t.assignStart.

Again, this does depend on your use cases, and assumes that you usually only need one transponder for a given plane except in special cases, like from a different endpoint.

Anyway, this were my thoughts on the domain model, and you were aiming for your Dto; however, this is then easily mapped with mapstruct like (assuming you do the same for maintCheck and transmitter)

@Mapper
interface PlaneDtoMapper {
  @Mapping(target = "transponderCode", source = "transponder.code")
  @Mapping(target = "maintCheck", source = "maintCheck.date")
  @Mapping(target = "channel", source = "transmitter.channel")
  PlaneDTO fromPlane(Plane p);
}

You don't need the "registration" mapping because the fields in plane and dto are the same, and the @Mapping annotations tell mapstruct which subfields of which fields of plane to use.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文