使用自定义转换器进行推土机深度属性映射

发布于 2024-11-15 22:22:48 字数 2847 浏览 3 评论 0原文

我的应用程序中有深度属性映射(从域对象到 DTO,反之亦然),类似于下一个示例:

...

<field>
    <a>employee.id</a>
    <b>employeeId</a>
</field>

...

当 Dozer 将 Domain 转换为 DTO 时,它将 employee.id 映射到 employeeId< /code>,这样就可以了。
当 Dozer 将 DTO 转换为 Domain 时,它将 employeeId 映射到 id=employeeId 的新 Employee 实例。

我想为这个深层属性映射创建一些逻辑,但我只是想不出解决方案。我尝试实现 CustomConverter(或扩展 DozerConverter),但 Dozer 将 Integer 类型作为源类和目标类传递给我(并期望 Integer 作为结果)。

编辑: 更准确地说,如果 DTO 中的 employeeId 为 0,我需要将 Domain 中的 employee 映射到 null

这可能吗?

有什么建议吗?

根据答案进行编辑: 我使用字段级自定义转换器解决了问题。而不是前面提到的映射,现在我有这样的东西...

...

<field custom-converter="ManyToOneIdMapper" custom-converter-param="id">
    <a>employee</a>
    <b>employeeId</b>
</field>

...

在 ManyToOneIdMapper 中我有...

public class ManyToOneIdMapper implements ConfigurableCustomConverter{

//...
//parameter field declaration, setParameter and getParameter implementations etc.
//...

public Object convert(Object existingDestinationFieldValue, Object sourceFieldValue, 
        Class<?> destinationClass, Class<?> sourceClass) {
    try {

        if(sourceClass.equals(Integer.class)){
            Integer src=(Integer)sourceFieldValue;

            if(src==null || src==0)
                return null;

            String setterName=formatMethodName("set", getParameter());
            Method setterMethod=destinationClass.getMethod(setterName, Integer.class);
            Object instance=destinationClass.newInstance();

            setterMethod.invoke(instance, src);

            return instance;
        }else{    
            if(sourceFieldValue==null)
                return 0;

            String getterName=formatMethodName("get", getParameter());
            Method getterMethod=sourceClass.getMethod(getterName);
            Object instance=getterMethod.invoke(sourceFieldValue);

            return instance;
        }
    } catch (Exception e){}
    return null;
}

/**
 * @return - method name (most often setter or getter)  according to fieldName.
 * For example formatMethodName("get", "id") returns "getId"
 */
protected String formatMethodName(String methodPrefix, String fieldName){
    String trimmedFieldName=fieldName.trim();
    String firstLetter=String.valueOf(trimmedFieldName.charAt(0));
    String capitalizedFirstLetter=firstLetter.toUpperCase();
    String methodName=methodPrefix+""+capitalizedFirstLetter+""+fieldName.substring(1);

    return methodName;
}

custom-converter-param 只是域对象中 id 字段的名称。使用这个名称,我只需在转换器中调用 setter 或 getter 方法。也许,这不是最快乐的解决方案,但它适用于我的问题场景。

I have deep property mapping in my application (from domain objects to DTO, and the reverse), similar to next example:

...

<field>
    <a>employee.id</a>
    <b>employeeId</a>
</field>

...

When Dozer converts Domain to DTO, it maps employee.id to employeeId, and that is ok.
When Dozer converts DTO to Domain, it maps employeeId to a new Employee instance with id=employeeId.

I want to create some logic for this deep property mapping, but i just can't figure out solution. I tried to implement CustomConverter(or extend DozerConverter) but Dozer passes me Integer type as source and destination class(and expect Integer as result).

EDIT:
More precisely, what i need is to map employee in Domain to null if employeeId in DTO is 0.

Is this possible?

Any advice?

EDIT ACCORDING TO ANSWERS:
I solve problem with field-level custom converter. Instead of earlier, above mentioned, mapping, now i have something like this...

...

<field custom-converter="ManyToOneIdMapper" custom-converter-param="id">
    <a>employee</a>
    <b>employeeId</b>
</field>

...

In ManyToOneIdMapper i have...

public class ManyToOneIdMapper implements ConfigurableCustomConverter{

//...
//parameter field declaration, setParameter and getParameter implementations etc.
//...

public Object convert(Object existingDestinationFieldValue, Object sourceFieldValue, 
        Class<?> destinationClass, Class<?> sourceClass) {
    try {

        if(sourceClass.equals(Integer.class)){
            Integer src=(Integer)sourceFieldValue;

            if(src==null || src==0)
                return null;

            String setterName=formatMethodName("set", getParameter());
            Method setterMethod=destinationClass.getMethod(setterName, Integer.class);
            Object instance=destinationClass.newInstance();

            setterMethod.invoke(instance, src);

            return instance;
        }else{    
            if(sourceFieldValue==null)
                return 0;

            String getterName=formatMethodName("get", getParameter());
            Method getterMethod=sourceClass.getMethod(getterName);
            Object instance=getterMethod.invoke(sourceFieldValue);

            return instance;
        }
    } catch (Exception e){}
    return null;
}

/**
 * @return - method name (most often setter or getter)  according to fieldName.
 * For example formatMethodName("get", "id") returns "getId"
 */
protected String formatMethodName(String methodPrefix, String fieldName){
    String trimmedFieldName=fieldName.trim();
    String firstLetter=String.valueOf(trimmedFieldName.charAt(0));
    String capitalizedFirstLetter=firstLetter.toUpperCase();
    String methodName=methodPrefix+""+capitalizedFirstLetter+""+fieldName.substring(1);

    return methodName;
}

custom-converter-param is just name of id-field in Domain object. With that name, i just call setter or getter method in my converter. Probably, it is not the happiest solution, but it works for my problem scenario.

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

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

发布评论

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

评论(3

噩梦成真你也成魔 2024-11-22 22:22:48

您可以使用 CustomConverter (根据其他答案),或者使用 DozerEventListener 在映射完成后将员工对象设置回 null(如果 ID 为 0) 。

You can either use a CustomConverter (per the other answer), or use a DozerEventListener to set the employee object back to null after the mapping has finished, if the ID is 0.

离线来电— 2024-11-22 22:22:48

您希望使用 CustomConverter 来映射父对象,例如:

Domain

class PersonA {
    ...
    int employeeId;
    ...
}

DTO

class PersonB {
    ...
    Employee employee;
    ...
}

class Employee {
    ...
    int id;
    ...
}

您希望使用 PersonAPersonB 来映射两个类>CustomConverter,这将让您可以按照您想要的方式构建它们。

You want a CustomConverter to map the parent object, for example:

Domain

class PersonA {
    ...
    int employeeId;
    ...
}

DTO

class PersonB {
    ...
    Employee employee;
    ...
}

class Employee {
    ...
    int id;
    ...
}

You want to map the two classes PersonA and PersonB using a CustomConverter, this will let you construct them what ever way you want.

旧话新听 2024-11-22 22:22:48

您可以查看ModelMapper(作者在这里)。它将智能地映射您描述的场景,无需任何配置或自定义转换器。考虑模型:

class Person {
  Employee employee;
}

class Employee {
  int id;
}

class PersonDTO {
  int employeeId;
}

映射很简单:

ModelMapper modelMapper = new ModelMapper();
PersonDTO personDTO = modelMapper.map(person, PersonDTO.class);

要在 PersonDTO.employeeId 不为零时有条件地映射 Person.employee,我们只需创建一个条件并为 Person.employee 添加属性映射条件:

Condition<?, ?> empIdIsNotZero = new Condition<PersonDTO, Employee>() {
  public boolean applies(MappingContext<PersonDTO, Employee> context) {
    return context.getSource().getEmployeeId() != 0;
  }
};

modelMapper.addMappings(new PropertyMap<PersonDTO, Person>() {
  protected void configure() {
    when(empIdIsNotZero).map(source).setEmployee(null);
  }
});

empIdIsNotZero 条件适用时,映射将正常发生。否则映射将被跳过,并且 Person.employee 将被设置为 null。

查看 ModelMapper 网站以获取更多文档和示例:

http://modelmapper.org

You might check out ModelMapper (author here). It will intelligently map the scenario you describe without requiring any configuration or custom converters. Considering the model:

class Person {
  Employee employee;
}

class Employee {
  int id;
}

class PersonDTO {
  int employeeId;
}

Mapping is simple:

ModelMapper modelMapper = new ModelMapper();
PersonDTO personDTO = modelMapper.map(person, PersonDTO.class);

To conditionally map Person.employee when PersonDTO.employeeId is not zero, we simply create a condition and add a property mapping for Person.employee with the condition:

Condition<?, ?> empIdIsNotZero = new Condition<PersonDTO, Employee>() {
  public boolean applies(MappingContext<PersonDTO, Employee> context) {
    return context.getSource().getEmployeeId() != 0;
  }
};

modelMapper.addMappings(new PropertyMap<PersonDTO, Person>() {
  protected void configure() {
    when(empIdIsNotZero).map(source).setEmployee(null);
  }
});

When the empIdIsNotZero condition applies, mapping will occur as normal. Otherwise mapping will be skipped and Person.employee will be set to null.

Check out the ModelMapper site for more docs and examples:

http://modelmapper.org

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