简体   繁体   English

使用自定义转换器在推土机中进行映射的一种方式

[英]One way mapping in Dozer using custom converter

Please note: while I would accept an XML-based solution if that's truly the only way to accomplish what I'm looking for, I would greatly prefer a solution using Dozer's Java API.请注意:虽然我会接受基于 XML 的解决方案,如果这确实是完成我正在寻找的唯一方法,但我更喜欢使用 Dozer 的 Java API 的解决方案。


I am new to Dozer and am trying to figure out how to use its API.我是Dozer的新手,正在尝试弄清楚如何使用它的 API。 It seems to default to field-level mappings (if the field names match) and to allow for custom mappers and converters in the event that field-level mapping (based on field name) is either not possible or not logical for your application needs.它似乎默认为字段级映射(如果字段名称匹配)并允许自定义映射器和转换器,以防字段级映射(基于字段名称)对于您的应用程序需求是不可能或不合逻辑的。

I have a situation where my app will take a DTO, say, ReportedIssue (an issue reported by a user and sent to my application over HTTP), and an Issue entity (a data entity that will be persisted to a MySQL DB).我的应用程序将采用 DTO,例如ReportedIssue (用户报告并通过 HTTP 发送到我的应用程序的问题)和Issue实体(将持久保存到 MySQL 数据库的数据实体)。

Here are my two objects:这是我的两个对象:

@Data
public class ReportedIssue {

    private String typeRefId;
    private String reporterRefId;
    private String info;

}

@Entity
@Table(name = "issues")
@Data
public class Issue {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "issue_ref_id")
    private String refId;

    @Column(name = "issue_tracking_number")
    private String trackingNumber;

    @OneToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    @JoinColumn(name = "issue_type_id", referencedColumnName = "issue_type_id")
    private IssueType type;

    @Column(name = "issue_reported_on")
    private Date reportedOn;

    @OneToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    @JoinColumn(name = "issue_reporter_id", referencedColumnName = "account_id")
    private Account reporter;

    @Column(name = "issue_info")
    private String info;

}

So in the application frontend, a user can report an issue.因此,在应用程序前端,用户可以报告问题。 The frontend sends a JSON version of a ReportedIssue to the backend, where that JSON is deserialized into a ReportedIssue DTO bean.前端将ReportedIssue的 JSON 版本发送到后端,其中 JSON 被反序列化为ReportedIssue DTO bean。 Then I need Dozer to convert my ReportedIssue into an Issue entity that I can then easily save to my MySQL DB.然后我需要 Dozer 将我的ReportedIssue转换为Issue实体,然后我可以轻松地将其保存到我的 MySQL 数据库中。

Here is my best attempt:这是我最好的尝试:

public class ReportedIssueConverter extends DozerConverter<ReportedIssue, Issue> {

    private AuthService authService;

    public ReportedIssueConverter(AuthService authService, Class<ReportedIssue> prototypeA, Class<Issue> prototypeB) {
      super(prototypeA, prototypeB);
      this.authService = authService;
    }

    public ReportedIssueConverter(Class<ReportedIssue> prototypeA, Class<Issue> prototypeB) {
        super(prototypeA, prototypeB);
    }

    @Override
    public Issue convertTo(ReportedIssue source, Issue destination) {

        Issue issue = new Issue();
        issue.setRefId(UUID.randomUUID().toString());
        issue.setType(IssueUtils.determineType(source));
        issue.setReportedOn(DateTimeUtils.nowInUTC());
        issue.setReporter(authService.currentUser());
        issue.setInfo(destination.getInfo());

        return issue;

    }

    @Override
    public ReportedIssue convertFrom(Issue source, ReportedIssue destination) {
        throw new UnsupportedOperationException("we currently don't map from issues to reported issues");
    }

}

Several concerns here.这里有几个问题。 For one, is such a custom converter even necessary?一方面,这样的定制转换器是否必要? Or is there a "better" (more standards compliant or using generally-accepted Dozer practices) way to use the Dozer API to perform this conversion?或者是否有“更好”(符合更多标准或使用普遍接受的推土机实践)的方式来使用推土机 API 执行此转换? But mainly, this DozerConverter seems to be intended for bi-directional mapping use cases.但主要是,这个DozerConverter似乎适用于双向映射用例。 Whereas, in my application, I will never have an Issue instance and need to map it back to a ReportedIssue DTO instance.然而,在我的应用程序中,我永远不会Issue实例,需要将其 map 回传给ReportedIssue DTO 实例。 So I only need one-way mapping from ReportedIssue --> Issue .所以我只需要来自ReportedIssue --> Issue的单向映射。 Am I using Dozer correctly by throwing an UnsupportedOperationException or is there another interface or API trick I can use to only leverage the one-way mapping I need?我是通过抛出UnsupportedOperationException正确使用推土机,还是有另一个接口或 API 技巧可以用来仅利用我需要的单向映射?

It could actually be done without a custom converter using custom getter methods in your dto class corresponding to fields in Issue .它实际上可以在没有自定义转换器的情况下使用您的 dto class 中对应于Issue中的字段的自定义 getter 方法来完成。 Dozer works by mapping each field in destination class by trying to invoke the getter method of the corresponding name in the source class. Dozer 的工作原理是通过尝试调用源 class 中相应名称的 getter 方法来映射目标 class 中的每个字段。

public class ReportedIssue {
    // fields.......

    public String getRefId() {
        UUID.randomUUID().toString()
    }

    public IssueType getType() {
        IssueUtils.determineType(this);
    }

    // similarly create getters for other required fields.

}

But for reporter field in Issue , you need an AuthService object.但是对于Issue中的reporter字段,您需要一个AuthService object。 I would suggest writing a static method as below:我建议编写如下 static 方法:

public static Issue getIssue(AuthService auth, ReportedIssue dto) {
    Issue issue = //map using dozer
    issue.setReporter(authService.currentUser());
    return issue;
}

Gauntham answer will work.冈瑟姆的回答会奏效。 Another option:另外的选择:

Implement a com.github.dozermapper.core.BeanFactory Your custom BeanFactory can handle实现一个 com.github.dozermapper.core.BeanFactory 你的自定义 BeanFactory 可以处理

Issue issue = new Issue();
issue.setRefId(UUID.randomUUID().toString());
issue.setReportedOn(DateTimeUtils.nowInUTC());
issue.setReporter(authService.currentUser());

Then depending on your preferences, this could also go into the bean factory然后根据你的喜好,这个也可以 go 进豆厂

issue.setType(IssueUtils.determineType(source));

Or you could handle that separately in the mapping.或者您可以在映射中单独处理。 Something would need to know how to call IssueUtils, so that is either 1) a customer converter or 2) a change to the DTO or entity to have the functionality through a getter or setter.有些东西需要知道如何调用 IssueUtils,因此要么是 1) 客户转换器,要么是 2) 更改 DTO 或实体以通过 getter 或 setter 获得功能。

Finally, this line would be handled in the Dozer Java API mapping最后,这条线将在 Dozer Java API 映射中处理

issue.setInfo(destination.getInfo());

Personally, I like Dozer's com.github.dozermapper.core.loader.api.BeanMappingBuilder where you can explicitly tell it how to map 2 beans, specify the bean factory to use and the custom converter for a specific field. Personally, I like Dozer's com.github.dozermapper.core.loader.api.BeanMappingBuilder where you can explicitly tell it how to map 2 beans, specify the bean factory to use and the custom converter for a specific field.

mapping(ReportedIssue.class, Issue.class, oneWay(), wildcard(true), beanFactory(IssueBeanFactory.class.getName()).fields("this", "type", customConverter(IssueTypeConverter.class)

oneWay(), wildcard(boolean), and beanFactory(String) are found in Dozer's TypeMappingOptions and customConverter(Class.class) is found in Dozer's FieldMappingOptions. oneWay()、wildcard(boolean) 和 beanFactory(String) 在 Dozer 的 TypeMappingOptions 中找到,customConverter(Class.class) 在 Dozer 的 FieldMappingOptions 中找到。

  • oneWay() makes the mapping work only in the direction specified in the BeanMappingBuilder. oneWay() 使映射仅在 BeanMappingBuilder 中指定的方向上工作。
  • wildcard(true) tells Dozer to automatically map matching fields (this is default behavior). wildcard(true) 告诉 Dozer 自动 map 匹配字段(这是默认行为)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM