I'm upgrading project mapstruct dependency from 1.2.0.Final to 1.4.2.Final. Everything was going right util falling on a complex mapper which looks like the below snippet code:
@Mapper(componentModel = "spring")
public abstract class CustomMapper{
@Named("mapTestla")
@Mapping(target = "f", source = "t.f")
public abstract TestlaDTO mapTestla(Testla t) {}
@Named("mapBar")
@Mapping(target = "t", source = "b.t", qualifiedByName = "mapTestla")
public abstract BarDTO mapBar(Bar b) {}
@Mappings({@Mapping(target = "a", source = "f.a"),
@Mapping(target = "b", source = "f.b", qualifiedByName = "mapBar")})
public abstract FooDTO mapFoo(Foo f) {}
}
class Foo {
String a;
Bar b;
}
class Bar {
Tesla t;
}
class Testla {
String f;
}
class FooDTO {
String a;
Bar b;
}
class BarDTO {
TeslaDTO t;
}
class TestlaDTO {
String f;
}
As You see in the error, mapstruct didn't detect the referenced method despite they are annotated with @Named
. It looks like a spring aop proxy problem..
Any idea of this issue?
PS: I don't want to split the mapper into multiple mappers as we're running behind schedule and it may lead to impact.
Error got:
Error : Qualifier error. No method found annotated with @Named#value: [ mapBar]. See https://mapstruct.org/faq/#qualifier for more info.
Error : Qualifier error. No method found annotated with @Named#value: [ mapTestla]. See https://mapstruct.org/faq/#qualifier for more info.
CodePudding user response:
It seems you have imported an incorrect @Named
annotation from a different than MapStruct library.
Your example is working well with MapStruct 1.4.2.Final.
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.Named;
@Mapper(componentModel = "spring")
public abstract class CustomMapper{
@Named("mapTestla")
@Mapping(target = "f", source = "t.f")
public abstract TestlaDTO mapTestla(Testla t);
@Named("mapBar")
@Mapping(target = "t", source = "b.t", qualifiedByName = "mapTestla")
public abstract BarDTO mapBar(Bar b);
@Mappings({@Mapping(target = "a", source = "f.a"),
@Mapping(target = "b", source = "f.b", qualifiedByName = "mapBar")})
public abstract FooDTO mapFoo(Foo f) ;
}
Generated code:
@Component
public class CustomMapperImpl extends CustomMapper {
@Override
public TestlaDTO mapTestla(Testla t) {
if ( t == null ) {
return null;
}
TestlaDTO testlaDTO = new TestlaDTO();
testlaDTO.f = t.f;
return testlaDTO;
}
@Override
public BarDTO mapBar(Bar b) {
if ( b == null ) {
return null;
}
BarDTO barDTO = new BarDTO();
barDTO.t = mapTestla( b.t );
return barDTO;
}
@Override
public FooDTO mapFoo(Foo f) {
if ( f == null ) {
return null;
}
FooDTO fooDTO = new FooDTO();
fooDTO.a = f.a;
fooDTO.b = mapBar( f.b );
return fooDTO;
}
}
According to documentation: Mapping object references.
You do not need to specify inner objects mappings explicitly via method qualifier. It is enough to specify mapping methods for all objects that's all. MapStract will automatically qualify them.
@Mapper(componentModel = "spring")
public abstract class CustomMapper {
public abstract TestlaDTO mapTestla(Testla t);
public abstract BarDTO mapBar(Bar b);
public abstract FooDTO mapFoo(Foo f) ;
}
Generated code:
@Component
public class CustomMapperImpl extends CustomMapper {
@Override
public TestlaDTO mapTestla(Testla t) {
if ( t == null ) {
return null;
}
TestlaDTO testlaDTO = new TestlaDTO();
testlaDTO.f = t.f;
return testlaDTO;
}
@Override
public BarDTO mapBar(Bar b) {
if ( b == null ) {
return null;
}
BarDTO barDTO = new BarDTO();
barDTO.t = mapTestla( b.t );
return barDTO;
}
@Override
public FooDTO mapFoo(Foo f) {
if ( f == null ) {
return null;
}
FooDTO fooDTO = new FooDTO();
fooDTO.a = f.a;
fooDTO.b = mapBar( f.b );
return fooDTO;
}
}
As you can see MapStruct generated the same mapper code without additional annotations!