mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#754 Moving qualifier section to conversion chapter
This commit is contained in:
parent
650a53318e
commit
da5274ea54
@ -693,6 +693,138 @@ The algorithm for finding a mapping or factory method resembles Java's method re
|
|||||||
When working with JAXB, e.g. when converting a `String` to a corresponding `JAXBElement<String>`, MapStruct will take the `scope` and `name` attributes of `@XmlElementDecl` annotations into account when looking for a mapping method. This makes sure that the created `JAXBElement` instances will have the right QNAME value. You can find a test which maps JAXB objects https://github.com/mapstruct/mapstruct/blob/{mapstructVersion}/integrationtest/src/test/java/org/mapstruct/itest/jaxb/JaxbBasedMapperTest.java[here].
|
When working with JAXB, e.g. when converting a `String` to a corresponding `JAXBElement<String>`, MapStruct will take the `scope` and `name` attributes of `@XmlElementDecl` annotations into account when looking for a mapping method. This makes sure that the created `JAXBElement` instances will have the right QNAME value. You can find a test which maps JAXB objects https://github.com/mapstruct/mapstruct/blob/{mapstructVersion}/integrationtest/src/test/java/org/mapstruct/itest/jaxb/JaxbBasedMapperTest.java[here].
|
||||||
====
|
====
|
||||||
|
|
||||||
|
[[selection-based-on-qualifiers]]
|
||||||
|
=== Mapping method selection based on qualifiers
|
||||||
|
|
||||||
|
In many occasions one requires mapping methods with the same method signature (appart from the name) that have different behavior. MapStruct has a handy mechanism to deal with such situations: `@Qualifier`. A ‘qualifier’ is a custom annotation that the user can write, ‘stick onto’ a mapping method which is included as used mapper, and can be referred to in a bean property mapping, iterable mapping or map mapping. Multiple qualifiers can be ‘stuck onto’ a method and mapping.
|
||||||
|
|
||||||
|
So, lets say there is a hand-written method to map titles with a `String` return type and `String` argument amongst many other referenced mappers with the same `String` return type - `String` argument signature:
|
||||||
|
|
||||||
|
.Several mapping methods with identical source and target types
|
||||||
|
====
|
||||||
|
[source, java, linenums]
|
||||||
|
[subs="verbatim,attributes"]
|
||||||
|
----
|
||||||
|
public class Titles {
|
||||||
|
|
||||||
|
public String translateTitleEG(String title) {
|
||||||
|
// some mapping logic
|
||||||
|
}
|
||||||
|
|
||||||
|
public String translateTitleGE(String title) {
|
||||||
|
// some mapping logic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
And a mapper using this handwritten mapper, in which source and target have a property 'title' that should be mapped:
|
||||||
|
|
||||||
|
.Mapper causing an ambiguous mapping method error
|
||||||
|
====
|
||||||
|
[source, java, linenums]
|
||||||
|
[subs="verbatim,attributes"]
|
||||||
|
----
|
||||||
|
@Mapper( uses = Titles.class )
|
||||||
|
public interface MovieMapper {
|
||||||
|
|
||||||
|
GermanRelease toGerman( OriginalRelease movies );
|
||||||
|
|
||||||
|
}
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
Without the use of qualifiers, this would result in an ambiguous mapping method error, because 2 qualifying methods are found (`translateTitleEG`, `translateTitleGE`) and MapStruct would not have a hint which one to choose.
|
||||||
|
|
||||||
|
Enter the qualifier approach:
|
||||||
|
|
||||||
|
.Declaring a qualifier type
|
||||||
|
====
|
||||||
|
[source, java, linenums]
|
||||||
|
[subs="verbatim,attributes"]
|
||||||
|
----
|
||||||
|
@Qualifier
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface TitleTranslator {
|
||||||
|
}
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
And, some qualifiers to indicate which translator to use to map from source language to target language:
|
||||||
|
|
||||||
|
.Declaring qualifier types for mapping methods
|
||||||
|
====
|
||||||
|
[source, java, linenums]
|
||||||
|
[subs="verbatim,attributes"]
|
||||||
|
----
|
||||||
|
@Qualifier
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface EnglishToGerman {
|
||||||
|
}
|
||||||
|
----
|
||||||
|
[source, java, linenums]
|
||||||
|
[subs="verbatim,attributes"]
|
||||||
|
----
|
||||||
|
@Qualifier
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface GermanToEnglish {
|
||||||
|
}
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
Please take note of the retention `TitleTranslator` on class level, `EnglishToGerman`, `GermanToEnglish` on method level!
|
||||||
|
|
||||||
|
Then, using the qualifiers, the mapping could look like this:
|
||||||
|
|
||||||
|
.Mapper using qualifiers
|
||||||
|
====
|
||||||
|
[source, java, linenums]
|
||||||
|
[subs="verbatim,attributes"]
|
||||||
|
----
|
||||||
|
@Mapper( uses = Titles.class )
|
||||||
|
public interface MovieMapper {
|
||||||
|
|
||||||
|
@Mapping( target = "title", qualifiedBy = { TitleTranslator.class, EnglishToGerman.class } )
|
||||||
|
GermanRelease toGerman( OriginalRelease movies );
|
||||||
|
|
||||||
|
}
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
.Custom mapper qualifying the methods it provides
|
||||||
|
====
|
||||||
|
[source, java, linenums]
|
||||||
|
[subs="verbatim,attributes"]
|
||||||
|
----
|
||||||
|
@TitleTranslator
|
||||||
|
public class Titles {
|
||||||
|
|
||||||
|
@EnglishToGerman
|
||||||
|
public String translateTitleEG(String title) {
|
||||||
|
// some mapping logic
|
||||||
|
}
|
||||||
|
|
||||||
|
@GermanToEnglish
|
||||||
|
public String translateTitleGE(String title) {
|
||||||
|
// some mapping logic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
[WARNING]
|
||||||
|
====
|
||||||
|
A class / method annotated with a qualifier will not qualify anymore for mappings that do not have the `qualifiedBy` element.
|
||||||
|
====
|
||||||
|
|
||||||
|
[TIP]
|
||||||
|
====
|
||||||
|
The same mechanism is also present on bean mappings: `@BeanMapping#qualifiedBy`: it selects the factory method marked with the indicated qualifier.
|
||||||
|
====
|
||||||
|
|
||||||
[[mapping-collections]]
|
[[mapping-collections]]
|
||||||
== Mapping collections
|
== Mapping collections
|
||||||
|
|
||||||
@ -1181,138 +1313,6 @@ public interface SourceTargetMapper {
|
|||||||
----
|
----
|
||||||
====
|
====
|
||||||
|
|
||||||
[[selection-based-on-qualifiers]]
|
|
||||||
== Selection based on Qualifiers
|
|
||||||
|
|
||||||
In many occasions one requires mapping methods with the same method signature (appart from the name) that have different behavior. MapStruct has a handy mechanism to deal with such situations: `@Qualifier`. A ‘qualifier’ is a custom annotation that the user can write, ‘stick onto’ a mapping method which is included as used mapper, and can be referred to in a bean property mapping, iterable mapping or map mapping. Multiple qualifiers can be ‘stuck onto’ a method and mapping.
|
|
||||||
|
|
||||||
So, lets say there is a hand-written method to map titles with a `String` return type and `String` argument amongst many other referenced mappers with the same `String` return type - `String` argument signature:
|
|
||||||
|
|
||||||
.Several mapping methods with identical source and target types
|
|
||||||
====
|
|
||||||
[source, java, linenums]
|
|
||||||
[subs="verbatim,attributes"]
|
|
||||||
----
|
|
||||||
public class Titles {
|
|
||||||
|
|
||||||
public String translateTitleEG(String title) {
|
|
||||||
// some mapping logic
|
|
||||||
}
|
|
||||||
|
|
||||||
public String translateTitleGE(String title) {
|
|
||||||
// some mapping logic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
----
|
|
||||||
====
|
|
||||||
|
|
||||||
And a mapper using this handwritten mapper, in which source and target have a property 'title' that should be mapped:
|
|
||||||
|
|
||||||
.Mapper causing an ambiguous mapping method error
|
|
||||||
====
|
|
||||||
[source, java, linenums]
|
|
||||||
[subs="verbatim,attributes"]
|
|
||||||
----
|
|
||||||
@Mapper( uses = Titles.class )
|
|
||||||
public interface MovieMapper {
|
|
||||||
|
|
||||||
GermanRelease toGerman( OriginalRelease movies );
|
|
||||||
|
|
||||||
}
|
|
||||||
----
|
|
||||||
====
|
|
||||||
|
|
||||||
Without the use of qualifiers, this would result in an ambiguous mapping method error, because 2 qualifying methods are found (`translateTitleEG`, `translateTitleGE`) and MapStruct would not have a hint which one to choose.
|
|
||||||
|
|
||||||
Enter the qualifier approach:
|
|
||||||
|
|
||||||
.Declaring a qualifier type
|
|
||||||
====
|
|
||||||
[source, java, linenums]
|
|
||||||
[subs="verbatim,attributes"]
|
|
||||||
----
|
|
||||||
@Qualifier
|
|
||||||
@Target(ElementType.TYPE)
|
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
|
||||||
public @interface TitleTranslator {
|
|
||||||
}
|
|
||||||
----
|
|
||||||
====
|
|
||||||
|
|
||||||
And, some qualifiers to indicate which translator to use to map from source language to target language:
|
|
||||||
|
|
||||||
.Declaring qualifier types for mapping methods
|
|
||||||
====
|
|
||||||
[source, java, linenums]
|
|
||||||
[subs="verbatim,attributes"]
|
|
||||||
----
|
|
||||||
@Qualifier
|
|
||||||
@Target(ElementType.METHOD)
|
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
|
||||||
public @interface EnglishToGerman {
|
|
||||||
}
|
|
||||||
----
|
|
||||||
[source, java, linenums]
|
|
||||||
[subs="verbatim,attributes"]
|
|
||||||
----
|
|
||||||
@Qualifier
|
|
||||||
@Target(ElementType.METHOD)
|
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
|
||||||
public @interface GermanToEnglish {
|
|
||||||
}
|
|
||||||
----
|
|
||||||
====
|
|
||||||
|
|
||||||
Please take note of the retention `TitleTranslator` on class level, `EnglishToGerman`, `GermanToEnglish` on method level!
|
|
||||||
|
|
||||||
Then, using the qualifiers, the mapping could look like this:
|
|
||||||
|
|
||||||
.Mapper using qualifiers
|
|
||||||
====
|
|
||||||
[source, java, linenums]
|
|
||||||
[subs="verbatim,attributes"]
|
|
||||||
----
|
|
||||||
@Mapper( uses = Titles.class )
|
|
||||||
public interface MovieMapper {
|
|
||||||
|
|
||||||
@Mapping( target = "title", qualifiedBy = { TitleTranslator.class, EnglishToGerman.class } )
|
|
||||||
GermanRelease toGerman( OriginalRelease movies );
|
|
||||||
|
|
||||||
}
|
|
||||||
----
|
|
||||||
====
|
|
||||||
|
|
||||||
.Custom mapper qualifying the methods it provides
|
|
||||||
====
|
|
||||||
[source, java, linenums]
|
|
||||||
[subs="verbatim,attributes"]
|
|
||||||
----
|
|
||||||
@TitleTranslator
|
|
||||||
public class Titles {
|
|
||||||
|
|
||||||
@EnglishToGerman
|
|
||||||
public String translateTitleEG(String title) {
|
|
||||||
// some mapping logic
|
|
||||||
}
|
|
||||||
|
|
||||||
@GermanToEnglish
|
|
||||||
public String translateTitleGE(String title) {
|
|
||||||
// some mapping logic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
----
|
|
||||||
====
|
|
||||||
|
|
||||||
[WARNING]
|
|
||||||
====
|
|
||||||
A class / method annotated with a qualifier will not qualify anymore for mappings that do not have the `qualifiedBy` element.
|
|
||||||
====
|
|
||||||
|
|
||||||
[TIP]
|
|
||||||
====
|
|
||||||
The same mechanism is also present on bean mappings: `@BeanMapping#qualifiedBy`: it selects the factory method marked with the indicated qualifier.
|
|
||||||
====
|
|
||||||
|
|
||||||
[[determining-result-type]]
|
[[determining-result-type]]
|
||||||
== Determining the result type
|
== Determining the result type
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user