【问题标题】:One way mapping in Dozer using custom converter使用自定义转换器在推土机中进行映射的一种方式
【发布时间】:2021-06-15 16:57:47
【问题描述】:

请注意:虽然我会接受基于 XML 的解决方案,如果这确实是完成我正在寻找的唯一方法,但我更喜欢使用 Dozer 的 Java API 的解决方案。


我是 Dozer 的新手,我正在尝试弄清楚如何使用它的 API。它似乎默认为字段级映射(如果字段名称匹配)并允许自定义映射器和转换器,以防字段级映射(基于字段名称)对于您的应用程序需求是不可能或不合逻辑的。

我的应用程序将采用 DTO,例如,ReportedIssue(用户报告并通过 HTTP 发送到我的应用程序的问题)和 Issue 实体(将被持久化的数据实体)到 MySQL 数据库)。

这是我的两个对象:

@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;

}

因此,在应用程序前端,用户可以报告问题。前端将 ReportedIssue 的 JSON 版本发送到后端,在后端将 JSON 反序列化为 ReportedIssue DTO bean。然后我需要 Dozer 将我的 ReportedIssue 转换为 Issue 实体,然后我可以轻松地将其保存到我的 MySQL 数据库中。

这是我最好的尝试:

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");
    }

}

这里有几个问题。一方面,这样的定制转换器是否必要?或者是否有“更好”(符合更多标准或使用普遍接受的推土机实践)的方式来使用推土机 API 执行此转换?但主要是,这个DozerConverter 似乎是用于双向映射用例。而在我的应用程序中,我永远不会拥有Issue 实例,需要将其映射回ReportedIssue DTO 实例。所以我只需要来自ReportedIssue --&gt; Issue 的单向映射。我是通过抛出 UnsupportedOperationException 正确使用 Dozer,还是有其他接口或 API 技巧可以用来仅利用我需要的单向映射?

【问题讨论】:

    标签: java java-8 dozer


    【解决方案1】:

    实际上可以在没有自定义转换器的情况下使用 dto 类中的自定义 getter 方法来完成,该方法对应于Issue 中的字段。 Dozer 通过尝试调用源类中相应名称的 getter 方法来映射目标类中的每个字段。

    public class ReportedIssue {
        // fields.......
    
        public String getRefId() {
            UUID.randomUUID().toString()
        }
    
        public IssueType getType() {
            IssueUtils.determineType(this);
        }
    
        // similarly create getters for other required fields.
    
    }
    

    但是对于Issue 中的reporter 字段,您需要一个AuthService 对象。我建议编写如下静态方法:

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

    【讨论】:

      【解决方案2】:

      Gauntham 答案会起作用。另一种选择:

      实现一个 com.github.dozermapper.core.BeanFactory 你的自定义 BeanFactory 可以处理

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

      然后根据你的喜好,这个也可以进豆厂

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

      或者您可以在映射中单独处理。需要知道如何调用 IssueUtils,因此要么是 1) 客户转换器,要么是 2) 更改 DTO 或实体以通过 getter 或 setter 获得功能。

      最后,这一行将在 Dozer Java API 映射中处理

      issue.setInfo(destination.getInfo());
      

      就我个人而言,我喜欢 Dozer 的 com.github.dozermapper.core.loader.api.BeanMappingBuilder,您可以在其中明确告诉它如何映射 2 个 bean、指定要使用的 bean 工厂以及特定字段的自定义转换器。

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

      oneWay()、wildcard(boolean) 和 beanFactory(String) 在 Dozer 的 TypeMappingOptions 中找到,customConverter(Class.class) 在 Dozer 的 FieldMappingOptions 中找到。

      • oneWay() 使映射仅在 BeanMappingBuilder 中指定的方向上工作。
      • wildcard(true) 告诉 Dozer 自动映射匹配的字段(这是默认行为)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多