简体   繁体   中英

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.


I am new to Dozer and am trying to figure out how to use its 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).

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. Then I need Dozer to convert my ReportedIssue into an Issue entity that I can then easily save to my MySQL DB.

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? But mainly, this DozerConverter seems to be intended for bi-directional mapping use cases. Whereas, in my application, I will never have an Issue instance and need to map it back to a ReportedIssue DTO instance. So I only need one-way mapping from 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?

It could actually be done without a custom converter using custom getter methods in your dto class corresponding to fields in Issue . Dozer works by mapping each field in destination class by trying to invoke the getter method of the corresponding name in the source 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. I would suggest writing a static method as below:

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

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

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.

Finally, this line would be handled in the Dozer Java API mapping

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.

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() makes the mapping work only in the direction specified in the BeanMappingBuilder.
  • wildcard(true) tells Dozer to automatically map matching fields (this is default behavior).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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