close
close
mapstruct 同名不同

mapstruct 同名不同

2 min read 22-02-2025
mapstruct 同名不同

Mapping with MapStruct: Handling Same-Name, Different-Type Properties

MapStruct is a powerful Java annotation processor that simplifies the creation of mapping code between Java bean objects. However, one common challenge arises when dealing with source and target objects that have properties with the same name but different types. This article explores different strategies to elegantly handle this scenario using MapStruct.

Understanding the Problem

Let's illustrate the problem with a simple example. Suppose we have two classes:

class Source {
    private String name;
    private int age;
}

class Target {
    private String name;
    private Long age;
}

A naive MapStruct mapper might look like this:

@Mapper
public interface MyMapper {
    Target map(Source source);
}

This will compile, but it won't correctly map the age property. MapStruct will likely throw an exception because it can't implicitly convert an int to a Long. This is where we need to employ different techniques to explicitly define the mapping.

Solution 1: Using @Mapping annotation with custom mapping

The most straightforward solution is to use the @Mapping annotation to explicitly specify the mapping for the conflicting age property. We can use a custom mapping method to handle the type conversion.

@Mapper
public interface MyMapper {

    @Mapping(target = "age", source = "age", qualifiedByName = "intToLong")
    Target map(Source source);

    @Named("intToLong")
    default Long intToLong(int age) {
        return (long) age;
    }
}

This approach clearly defines how the int age from the Source object is converted to a Long for the Target object. The @Named annotation allows us to reuse this conversion method in other mappings if needed.

Solution 2: Using a dedicated mapping method

For more complex conversions or when you have multiple properties with the same name and different types, using a dedicated mapping method can improve readability and maintainability.

@Mapper
public interface MyMapper {
    Target map(Source source);

    default Target mapAge(int sourceAge) {
        return new Target(null, (long)sourceAge); //Example only, adapt to your Target constructor.
    }

}

This requires a careful design of your mapper interface. It might need adjustments to the Target constructor to allow setting individual properties. You can still use the @Mapping annotation to map the name property leaving the age to be handled by this explicit method.

Solution 3: Using Type Converters

For more sophisticated type conversions or reusable logic across multiple mappers, MapStruct's type converters offer a flexible solution.

@Converter
public class IntToLongConverter {
    public Long convert(int source) {
        return (long) source;
    }
}

@Mapper(uses = IntToLongConverter.class)
public interface MyMapper {
    Target map(Source source);
}

This creates a reusable converter that can be used across different mappers needing the same int to Long conversion. The @Mapper(uses = ...) annotation registers the converter for use within the mapper.

Choosing the Right Approach

The best approach depends on the complexity of your mapping and the reusability of your conversion logic. For simple type conversions, the @Mapping annotation with a named method or a dedicated mapping method is sufficient. For more complex or reusable conversions, type converters provide a more maintainable and scalable solution. Remember to always prioritize readability and maintainability in your code. Clearly defining your mappings prevents unexpected behavior and makes your code easier to understand and debug. Always test your mapping thoroughly to ensure accurate data transformation.

Related Posts