New posts for Spring and Hibernate
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
# A record target has no setters: MapStruct calls the canonical constructor
|
||||
|
||||
CustomerDto is a record : true
|
||||
setters on CustomerDto : 0
|
||||
setters on Customer (entity) : 14
|
||||
|
||||
--- the line the generated toDto uses to build the record ---
|
||||
CustomerDto customerDto = new CustomerDto( id, fullName, email, birthDate, age, nickname, address, roles, attributes, orders, origin );
|
||||
|
||||
--- record as SOURCE: the generated toEntity reads accessors, not getters ---
|
||||
customer.setFirstName( request.firstName() );
|
||||
customer.setLastName( request.lastName() );
|
||||
customer.setEmail( request.email() );
|
||||
customer.setBirthDate( request.birthDate() );
|
||||
@@ -0,0 +1,127 @@
|
||||
# What MapStruct 1.6.3 generated, read from target/generated-sources (header and imports trimmed)
|
||||
|
||||
|
||||
--- AddressMapperImpl ---
|
||||
@Component
|
||||
public class AddressMapperImpl implements AddressMapper {
|
||||
|
||||
@Override
|
||||
public AddressDto toDto(Address address) {
|
||||
if ( address == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String zip = null;
|
||||
String country = null;
|
||||
String street = null;
|
||||
String city = null;
|
||||
|
||||
zip = address.getPostalCode();
|
||||
if ( address.getCountry() != null ) {
|
||||
country = address.getCountry();
|
||||
}
|
||||
else {
|
||||
country = "IN";
|
||||
}
|
||||
street = address.getStreet();
|
||||
city = address.getCity();
|
||||
|
||||
AddressDto addressDto = new AddressDto( street, city, zip, country );
|
||||
|
||||
return addressDto;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address toEntity(AddressDto dto) {
|
||||
if ( dto == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Address address = new Address();
|
||||
|
||||
address.setPostalCode( dto.zip() );
|
||||
address.setCountry( dto.country() );
|
||||
address.setStreet( dto.street() );
|
||||
address.setCity( dto.city() );
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(AddressDto dto, Address address) {
|
||||
if ( dto == null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( dto.zip() != null ) {
|
||||
address.setPostalCode( dto.zip() );
|
||||
}
|
||||
if ( dto.street() != null ) {
|
||||
address.setStreet( dto.street() );
|
||||
}
|
||||
if ( dto.city() != null ) {
|
||||
address.setCity( dto.city() );
|
||||
}
|
||||
if ( dto.country() != null ) {
|
||||
address.setCountry( dto.country() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
--- CustomerMapperImpl: constructor injection, then toDto ---
|
||||
@Component
|
||||
public class CustomerMapperImpl implements CustomerMapper {
|
||||
|
||||
private final AddressMapper addressMapper;
|
||||
private final OrderMapper orderMapper;
|
||||
|
||||
@Autowired
|
||||
public CustomerMapperImpl(AddressMapper addressMapper, OrderMapper orderMapper) {
|
||||
|
||||
this.addressMapper = addressMapper;
|
||||
this.orderMapper = orderMapper;
|
||||
}
|
||||
|
||||
public CustomerDto toDto(Customer customer, Clock clock) {
|
||||
if ( customer == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int age = 0;
|
||||
String nickname = null;
|
||||
Long id = null;
|
||||
String email = null;
|
||||
LocalDate birthDate = null;
|
||||
AddressDto address = null;
|
||||
Set<String> roles = null;
|
||||
Map<String, String> attributes = null;
|
||||
List<OrderDto> orders = null;
|
||||
|
||||
age = ageInYears( customer.getBirthDate(), clock );
|
||||
if ( customer.getNickname() != null ) {
|
||||
nickname = customer.getNickname();
|
||||
}
|
||||
else {
|
||||
nickname = "anonymous";
|
||||
}
|
||||
id = customer.getId();
|
||||
email = customer.getEmail();
|
||||
birthDate = customer.getBirthDate();
|
||||
address = addressMapper.toDto( customer.getAddress() );
|
||||
Set<String> set = customer.getRoles();
|
||||
if ( set != null ) {
|
||||
roles = new LinkedHashSet<String>( set );
|
||||
}
|
||||
Map<String, String> map = customer.getAttributes();
|
||||
if ( map != null ) {
|
||||
attributes = new LinkedHashMap<String, String>( map );
|
||||
}
|
||||
orders = orderMapper.toDtos( customer.getOrders() );
|
||||
|
||||
String fullName = customer.getFirstName() + " " + customer.getLastName();
|
||||
String origin = "api";
|
||||
|
||||
CustomerDto customerDto = new CustomerDto( id, fullName, email, birthDate, age, nickname, address, roles, attributes, orders, origin );
|
||||
|
||||
return customerDto;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
# Customer (mutable entity) -> CustomerDto (record), with nested Address, a List of Orders, a Set and a Map
|
||||
|
||||
fullName : Asha Mehta <- expression = java(first + " " + last)
|
||||
age : 36 <- qualifiedByName, with the clock passed as @Context
|
||||
nickname : anonymous <- source was null, defaultValue = "anonymous"
|
||||
origin : api <- constant = "api"
|
||||
address : AddressDto[street=12 Park Road, city=Pune, zip=411001, country=IN] <- postalCode renamed to zip; null country got defaultValue IN
|
||||
roles : [USER] same instance as the entity's? false
|
||||
attributes : {newsletter=yes} same instance as the entity's? false
|
||||
|
||||
--- List<Order> -> List<OrderDto>, with a computed total ---
|
||||
order id=100 status=PAID total=32.50
|
||||
OrderLineDto[sku=BOOK-1, quantity=2, unitPrice=12.50, lineTotal=25.00]
|
||||
OrderLineDto[sku=PEN-9, quantity=10, unitPrice=0.75, lineTotal=7.50]
|
||||
|
||||
--- what is NOT in the record ---
|
||||
passwordHash is a component of CustomerDto? false
|
||||
@@ -0,0 +1,13 @@
|
||||
# CustomerRequest (record) -> Customer (entity); AddressMapper.toEntity inherits the rename in reverse
|
||||
|
||||
email after @AfterMapping : '[email protected]' (request carried ' [email protected] ')
|
||||
createdAt (from @Context) : 2026-11-05T00:00:00Z
|
||||
address.postalCode : 600001 <- @InheritInverseConfiguration turned zip back into postalCode
|
||||
address.country : null <- defaultValue is NOT inverted; toDto gives IN, toEntity does not
|
||||
tier : null <- the entity said STANDARD; the request said null
|
||||
tier with nullValueCheckStrategy = ALWAYS : STANDARD
|
||||
id / passwordHash / orders : null / null / null <- ignore = true
|
||||
|
||||
--- round trip: entity -> record -> entity ---
|
||||
passwordHash survives the round trip? false
|
||||
address country survives? null -> IN
|
||||
@@ -0,0 +1,50 @@
|
||||
# Null handling: IGNORE vs the default on a patch, and RETURN_DEFAULT on whole objects and collections
|
||||
|
||||
|
||||
--- patch(): nullValuePropertyMappingStrategy = IGNORE, applied with a patch whose components are all null ---
|
||||
before: [email protected], nickname=ash, roles=[USER], address=12 Park Road/Pune/411001
|
||||
after : [email protected], nickname=ash, roles=[USER], address=12 Park Road/Pune/411001
|
||||
|
||||
--- replace(): the default strategy (SET_TO_NULL), same all-null patch ---
|
||||
before: [email protected], nickname=ash, roles=[USER], address=12 Park Road/Pune/411001
|
||||
after : email=null, nickname=null, roles=null, address=null
|
||||
|
||||
--- whole objects and collections: OrderMapper (default) vs LenientOrderMapper (RETURN_DEFAULT) ---
|
||||
strict.toDto(null) : null
|
||||
lenient.toDto(null) : OrderDto[id=null, status=null, placedAt=null, lines=null, total=null]
|
||||
strict order, lines = null : lines=null
|
||||
lenient order, lines = null : lines=[]
|
||||
strict.toDtos(null) : null
|
||||
|
||||
--- the generated patch() (IGNORE): every assignment is guarded ---
|
||||
public void patch(CustomerPatch patch, Customer customer) {
|
||||
if ( patch == null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( patch.email() != null ) {
|
||||
customer.setEmail( patch.email() );
|
||||
}
|
||||
if ( patch.nickname() != null ) {
|
||||
customer.setNickname( patch.nickname() );
|
||||
}
|
||||
if ( patch.address() != null ) {
|
||||
if ( customer.getAddress() == null ) {
|
||||
customer.setAddress( new Address() );
|
||||
}
|
||||
addressMapper.update( patch.address(), customer.getAddress() );
|
||||
}
|
||||
if ( customer.getRoles() != null ) {
|
||||
Set<String> set = patch.roles();
|
||||
if ( set != null ) {
|
||||
customer.getRoles().clear();
|
||||
customer.getRoles().addAll( set );
|
||||
}
|
||||
}
|
||||
else {
|
||||
Set<String> set = patch.roles();
|
||||
if ( set != null ) {
|
||||
customer.setRoles( new LinkedHashSet<String>( set ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
# @MappingTarget updates: nested objects and collections are changed in place
|
||||
|
||||
|
||||
--- patch only the city and the roles ---
|
||||
address: [email protected], nickname=null, roles=[USER, ADMIN], address=12 Park Road/Mumbai/411001
|
||||
same Address instance as before? true <- AddressMapper.update was called on it
|
||||
same roles Set instance as before? true <- cleared and refilled, not replaced
|
||||
another holder of the old roles Set now sees: [AUDITOR]
|
||||
|
||||
--- an immutable collection on the target ---
|
||||
patching roles on a Set.of(...) target threw: java.lang.UnsupportedOperationException
|
||||
@@ -0,0 +1,40 @@
|
||||
# @BeforeMapping / @AfterMapping on a record target versus a bean target (call order, recorded)
|
||||
|
||||
|
||||
--- target is a record: toRecord(Pair) ---
|
||||
@BeforeMapping(source)
|
||||
@BeforeMapping(source, @MappingTarget Boxed) target=Boxed[name=pen, qty=3]
|
||||
@AfterMapping(source, @MappingTarget Boxed) target=Boxed[name=pen, qty=3]
|
||||
@AfterMapping(source)
|
||||
|
||||
--- target is a mutable bean: toBean(Pair) ---
|
||||
@BeforeMapping(source)
|
||||
@BeforeMapping(source, @MappingTarget BoxedBean) name=null
|
||||
BoxedBean.setName(pen)
|
||||
BoxedBean.setQty(3)
|
||||
@AfterMapping(source, @MappingTarget BoxedBean) name=pen
|
||||
@AfterMapping(source)
|
||||
|
||||
--- the generated toRecord, which is why ---
|
||||
public Probes.Boxed toRecord(Probes.Pair pair) {
|
||||
beforeSource( pair );
|
||||
|
||||
if ( pair == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String name = null;
|
||||
int qty = 0;
|
||||
|
||||
name = pair.name();
|
||||
qty = pair.qty();
|
||||
|
||||
Probes.Boxed boxed = new Probes.Boxed( name, qty );
|
||||
|
||||
beforeRecord( pair, boxed );
|
||||
|
||||
afterRecord( pair, boxed );
|
||||
afterSource( pair );
|
||||
|
||||
return boxed;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
# @ObjectFactory returning a record: the factory's object is the result, and nothing is copied into it
|
||||
|
||||
source : Pair[name=pen, qty=3]
|
||||
result : Boxed[name=from-the-factory, qty=0]
|
||||
compiled with unmappedTargetPolicy = ERROR, and no error was reported
|
||||
|
||||
--- the generated method ---
|
||||
public Probes.Boxed toRecord(Probes.Pair pair) {
|
||||
if ( pair == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Probes.Boxed boxed = create( pair );
|
||||
|
||||
return boxed;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
# Post-processing a record: @AfterMapping on its builder, or a @DecoratedWith decorator
|
||||
|
||||
|
||||
--- ReceiptMapper: the hook receives ReceiptDto.Builder ---
|
||||
ReceiptDto[orderId=100, total=32.50, summary=order 100: 2 line(s)]
|
||||
public ReceiptDto toReceipt(Order order) {
|
||||
if ( order == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ReceiptDto.Builder receiptDto = ReceiptDto.builder();
|
||||
|
||||
receiptDto.orderId( order.getId() );
|
||||
receiptDto.total( orderMapper.sumOfLines( order.getLines() ) );
|
||||
|
||||
summarise( order, receiptDto );
|
||||
|
||||
return receiptDto.build();
|
||||
}
|
||||
|
||||
--- InvoiceMapper: @DecoratedWith, so two beans implement the interface ---
|
||||
bean invoiceMapperImpl -> InvoiceMapperImpl
|
||||
bean invoiceMapperImpl_ -> InvoiceMapperImpl_
|
||||
injected InvoiceMapper (the @Primary one) : InvoiceDto[id=100, status=PAID, total=32.50, display=#100 [PAID] 32.50]
|
||||
the generated delegate on its own : InvoiceDto[id=100, status=PAID, total=32.50, display=null]
|
||||
|
||||
--- the generated class that decorates ---
|
||||
@Component
|
||||
@Primary
|
||||
public class InvoiceMapperImpl extends InvoiceMapperDecorator {
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
# @Context cycle tracking: it works for a bean target and cannot work for a record target
|
||||
|
||||
bean target + @Context CycleContext : ann.manager.manager is ann? true
|
||||
record target + the same @Context : java.lang.StackOverflowError
|
||||
|
||||
--- why: the generated record mapping recurses BEFORE it can call the constructor ---
|
||||
public Probes.EmployeeRecord toRecord(Probes.Employee employee, CycleContext context) {
|
||||
Probes.EmployeeRecord target = context.lookup( employee, Probes.EmployeeRecord.class );
|
||||
if ( target != null ) {
|
||||
return target;
|
||||
}
|
||||
|
||||
if ( employee == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String name = null;
|
||||
Probes.EmployeeRecord manager = null;
|
||||
|
||||
name = employee.name;
|
||||
manager = toRecord( employee.manager, context );
|
||||
|
||||
Probes.EmployeeRecord employeeRecord = new Probes.EmployeeRecord( name, manager );
|
||||
|
||||
context.remember( employee, employeeRecord );
|
||||
|
||||
return employeeRecord;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
# Testing a generated mapper without Spring: Mappers.getMapper versus constructing the Impl
|
||||
|
||||
|
||||
--- 1. Mappers.getMapper(...) ---
|
||||
AddressMapper (no dependencies) : AddressMapperImpl
|
||||
CustomerMapper (uses = {...}) : RuntimeException
|
||||
root cause : java.lang.NoSuchMethodException: com.ankurm.mapstruct.mapper.CustomerMapperImpl.<init>()
|
||||
|
||||
--- 2. new CustomerMapperImpl(new AddressMapperImpl(), new OrderMapperImpl()) ---
|
||||
no Spring, no reflection : Asha Mehta
|
||||
@@ -0,0 +1,6 @@
|
||||
# @SpringJUnitConfig(classes = {the generated impls}): only the beans the mapper needs
|
||||
|
||||
bean definitions in the context : 11
|
||||
bean names : org.springframework.context.annotation.internalConfigurationAnnotationProcessor, org.springframework.context.annotation.internalAutowiredAnnotationProcessor, org.springframework.context.annotation.internalCommonAnnotationProcessor, org.springframework.context.event.internalEventListenerProcessor, org.springframework.context.event.internalEventListenerFactory, customerMapperImpl, addressMapperImpl, orderMapperImpl, contextConfigurationMapperTest.PinnedClock, org.springframework.test.context.support.internalDynamicPropertyRegistrarBeanInitializer, clock
|
||||
mapper injected as : CustomerMapperImpl
|
||||
mapped : Asha Mehta
|
||||
@@ -0,0 +1,6 @@
|
||||
# @SpringBootTest: the same mapper, inside the whole application
|
||||
|
||||
bean definitions in the context : 153
|
||||
includes the web controller : true
|
||||
clock is the application's own : Z
|
||||
mapped : Asha Mehta
|
||||
@@ -0,0 +1,15 @@
|
||||
# Property-level mapper test: change one entity property, expect exactly the right DTO component to change
|
||||
|
||||
id changes when its source changes : true
|
||||
fullName changes when its source changes : true
|
||||
email changes when its source changes : true
|
||||
birthDate changes when its source changes : true
|
||||
age changes when its source changes : true
|
||||
nickname changes when its source changes : true
|
||||
address changes when its source changes : true
|
||||
roles changes when its source changes : true
|
||||
attributes changes when its source changes : true
|
||||
orders changes when its source changes : true
|
||||
|
||||
record components : [address, age, attributes, birthDate, email, fullName, id, nickname, orders, roles]
|
||||
covered by a test : [address, age, attributes, birthDate, email, fullName, id, nickname, orders, roles]
|
||||
@@ -0,0 +1,15 @@
|
||||
# MockMvc against CustomerController: records in, records out, Jackson 3 in the middle
|
||||
|
||||
JSON library bean : tools.jackson.databind.json.JsonMapper
|
||||
|
||||
--- GET /customers/1 ---
|
||||
{"id":1,"fullName":"Asha Mehta","email":"[email protected]","birthDate":"1990-06-15","age":36,"nickname":"anonymous","address":{"street":"12 Park Road","city":"Pune","zip":"411001","country":"IN"},"roles":["USER"],"attributes":{"newsletter":"yes"},"orders":[{"id":100,"status":"PAID","placedAt":"2026-02-01T10:00:00Z","lines":[{"sku":"BOOK-1","quantity":2,"unitPrice":12.50,"lineTotal":25.00},{"sku":"PEN-9","quantity":10,"unitPrice":0.75,"lineTotal":7.50}],"total":32.50}],"origin":"api"}
|
||||
|
||||
--- POST /customers (no nickname, no tier, no country) ---
|
||||
{"id":2,"fullName":"Ravi Iyer","email":"[email protected]","birthDate":"1985-01-02","age":41,"nickname":"anonymous","address":{"street":"1 Lake View","city":"Chennai","zip":"600001","country":"IN"},"roles":["ADMIN"],"attributes":{},"orders":null,"origin":"api"}
|
||||
|
||||
--- PATCH /customers/1 {"nickname":"ash"} (every other component of the patch record is null) ---
|
||||
{"id":1,"fullName":"Asha Mehta","email":"[email protected]","birthDate":"1990-06-15","age":36,"nickname":"ash","address":{"street":"12 Park Road","city":"Pune","zip":"411001","country":"IN"},"roles":["USER"],"attributes":{"newsletter":"yes"},"orders":[{"id":100,"status":"PAID","placedAt":"2026-02-01T10:00:00Z","lines":[{"sku":"BOOK-1","quantity":2,"unitPrice":12.50,"lineTotal":25.00},{"sku":"PEN-9","quantity":10,"unitPrice":0.75,"lineTotal":7.50}],"total":32.50}],"origin":"api"}
|
||||
|
||||
--- GET /customers/99 ---
|
||||
status 404
|
||||
@@ -0,0 +1,13 @@
|
||||
# INDICATIVE timings: hand-written vs MapStruct vs Dozer (setter-based beans)
|
||||
|
||||
Java 25.0.3+9-LTS-195, one JVM, 200000 iterations per round, best of 5 rounds after a 50000-iteration warm-up
|
||||
mapping: Customer -> CustomerView (1 address, 1 order with 2 lines, 1 set, 1 map)
|
||||
|
||||
hand-written 95 ns/op
|
||||
MapStruct 1.6.3 92 ns/op (1.0x hand-written)
|
||||
Dozer 7.0.0 11498 ns/op (122x hand-written)
|
||||
|
||||
first call, cold: MapStruct 0.13 ms, Dozer 80.9 ms
|
||||
results equal: true
|
||||
|
||||
Indicative only. Timings vary between runs and machines; do not quote them as a benchmark result.
|
||||
@@ -0,0 +1,7 @@
|
||||
# A record target component nobody mapped (unmappedTargetPolicy = ERROR)
|
||||
|
||||
$ javac -d target/broken/unmapped-target -cp $MAPSTRUCT_JAR -processorpath $MAPSTRUCT_PROCESSOR_JAR src/broken/unmapped-target/*.java
|
||||
src/broken/unmapped-target/BrokenMapper.java:12: error: Unmapped target property: "note".
|
||||
Dst map(Src src);
|
||||
^
|
||||
1 error
|
||||
@@ -0,0 +1,7 @@
|
||||
# Two methods can produce the same property
|
||||
|
||||
$ javac -d target/broken/ambiguous -cp $MAPSTRUCT_JAR -processorpath $MAPSTRUCT_PROCESSOR_JAR src/broken/ambiguous/*.java
|
||||
src/broken/ambiguous/BrokenMapper.java:13: error: Ambiguous mapping methods found for mapping property "rokenMapper.Money price" to String: String plain(rokenMapper.Money m), String formatted(rokenMapper.Money m). See https://mapstruct.org/faq/#ambiguous for more info.
|
||||
Dst map(Src src);
|
||||
^
|
||||
1 error
|
||||
@@ -0,0 +1,7 @@
|
||||
# A typo in @Mapping(source = ...)
|
||||
|
||||
$ javac -d target/broken/missing-source -cp $MAPSTRUCT_JAR -processorpath $MAPSTRUCT_PROCESSOR_JAR src/broken/missing-source/*.java
|
||||
src/broken/missing-source/BrokenMapper.java:12: error: No property named "nmae" exists in source parameter(s). Did you mean "name"?
|
||||
@Mapping(source = "nmae", target = "title")
|
||||
^
|
||||
1 error
|
||||
@@ -0,0 +1,15 @@
|
||||
# @MappingTarget on a record compiles, with unmappedTargetPolicy = ERROR, and generates this
|
||||
|
||||
$ javac -d target/broken/update-record -s target/broken/update-record -cp $MAPSTRUCT_JAR -processorpath $MAPSTRUCT_PROCESSOR_JAR src/broken/update-record/*.java
|
||||
(exit status: 0)
|
||||
|
||||
$ cat target/broken/update-record/BrokenMapperImpl.java (header and imports trimmed)
|
||||
public class BrokenMapperImpl implements BrokenMapper {
|
||||
|
||||
@Override
|
||||
public void update(BrokenMapper.Patch patch, BrokenMapper.Dst target) {
|
||||
if ( patch == null ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
# No way to turn one type into another
|
||||
|
||||
$ javac -d target/broken/cannot-map -cp $MAPSTRUCT_JAR -processorpath $MAPSTRUCT_PROCESSOR_JAR src/broken/cannot-map/*.java
|
||||
src/broken/cannot-map/BrokenMapper.java:17: error: Can't map property "rokenMapper.Tag tag" to "LocalDate tag". Consider to declare/implement a mapping method: "LocalDate map(rokenMapper.Tag value)".
|
||||
Dst map(Src src);
|
||||
^
|
||||
1 error
|
||||
@@ -0,0 +1,10 @@
|
||||
# @DecoratedWith with a constructor-injected delegate (componentModel = spring)
|
||||
|
||||
$ javac -d target/broken/decorator-constructor -cp $MAPSTRUCT_JAR:$SPRING_JARS -processorpath $MAPSTRUCT_PROCESSOR_JAR src/broken/decorator-constructor/*.java
|
||||
target/broken/decorator-constructor/BrokenMapperImpl.java:13: error: constructor BrokenMapperDecorator in class BrokenMapperDecorator cannot be applied to given types;
|
||||
public class BrokenMapperImpl extends BrokenMapperDecorator {
|
||||
^
|
||||
required: BrokenMapper
|
||||
found: no arguments
|
||||
reason: actual and formal argument lists differ in length
|
||||
1 error
|
||||
@@ -0,0 +1,24 @@
|
||||
# The processor jar on the plain classpath: javac 25.0.3 compiles without generating anything
|
||||
|
||||
$ javac -d target/broken/processor-not-run -cp $MAPSTRUCT_JAR:$MAPSTRUCT_PROCESSOR_JAR src/broken/processor-not-run/UsesMapper.java
|
||||
(exit status: 0)
|
||||
|
||||
$ ls target/broken/processor-not-run
|
||||
UsesMapper$Dst.class
|
||||
UsesMapper$Src.class
|
||||
UsesMapper.class
|
||||
|
||||
$ java -cp $MAPSTRUCT_JAR:target/broken/processor-not-run UsesMapper
|
||||
Exception in thread "main" java.lang.RuntimeException: java.lang.ClassNotFoundException: Cannot find implementation for UsesMapper
|
||||
at org.mapstruct.factory.Mappers.getMapper(Mappers.java:61)
|
||||
at UsesMapper.main(UsesMapper.java:15)
|
||||
Caused by: java.lang.ClassNotFoundException: Cannot find implementation for UsesMapper
|
||||
|
||||
$ javac -proc:full -d target/broken/processor-not-run -cp $MAPSTRUCT_JAR:$MAPSTRUCT_PROCESSOR_JAR src/broken/processor-not-run/UsesMapper.java
|
||||
(exit status: 0)
|
||||
$ ls target/broken/processor-not-run
|
||||
UsesMapper$Dst.class
|
||||
UsesMapper$Src.class
|
||||
UsesMapper.class
|
||||
UsesMapperImpl.class
|
||||
UsesMapperImpl.java
|
||||
@@ -0,0 +1,25 @@
|
||||
# Lombok + MapStruct + a record target: what the processor path order and lombok-mapstruct-binding change
|
||||
|
||||
--- MapStruct first, Lombok second, no binding ---
|
||||
$ javac -d target/broken/lombok -cp $MAPSTRUCT_JAR:$LOMBOK_JAR -processorpath $MAPSTRUCT_PROCESSOR_JAR:$LOMBOK_JAR src/broken/lombok/LombokMapper.java
|
||||
src\broken\lombok\LombokMapper.java:19: error: Unmapped target properties: "name, age".
|
||||
PersonDto map(Person person);
|
||||
^
|
||||
1 error
|
||||
(exit status: 1)
|
||||
LombokMapperImpl generated: no
|
||||
|
||||
--- Lombok first, MapStruct second, no binding ---
|
||||
$ javac -d target/broken/lombok -cp $MAPSTRUCT_JAR:$LOMBOK_JAR -processorpath $LOMBOK_JAR:$MAPSTRUCT_PROCESSOR_JAR src/broken/lombok/LombokMapper.java
|
||||
(exit status: 0)
|
||||
LombokMapperImpl generated: yes
|
||||
|
||||
--- MapStruct first, then Lombok and lombok-mapstruct-binding ---
|
||||
$ javac -d target/broken/lombok -cp $MAPSTRUCT_JAR:$LOMBOK_JAR -processorpath $MAPSTRUCT_PROCESSOR_JAR:$LOMBOK_JAR:$BINDING_JAR src/broken/lombok/LombokMapper.java
|
||||
(exit status: 0)
|
||||
LombokMapperImpl generated: yes
|
||||
|
||||
--- Lombok, binding, then MapStruct ---
|
||||
$ javac -d target/broken/lombok -cp $MAPSTRUCT_JAR:$LOMBOK_JAR -processorpath $LOMBOK_JAR:$BINDING_JAR:$MAPSTRUCT_PROCESSOR_JAR src/broken/lombok/LombokMapper.java
|
||||
(exit status: 0)
|
||||
LombokMapperImpl generated: yes
|
||||
@@ -0,0 +1,20 @@
|
||||
# mapstruct-processor 1.6.3: what the jar itself declares
|
||||
|
||||
--- META-INF/services/javax.annotation.processing.Processor (comments dropped) ---
|
||||
org.mapstruct.ap.MappingProcessor
|
||||
|
||||
--- Implementation-Version in the manifest ---
|
||||
Implementation-Version: 1.6.3
|
||||
|
||||
--- options the processor accepts (-Akey=value), from @SupportedOptions via javap ---
|
||||
mapstruct.alwaysGenerateServicesFile
|
||||
mapstruct.defaultComponentModel
|
||||
mapstruct.defaultInjectionStrategy
|
||||
mapstruct.disableBuilders
|
||||
mapstruct.nullValueIterableMappingStrategy
|
||||
mapstruct.nullValueMapMappingStrategy
|
||||
mapstruct.suppressGeneratorTimestamp
|
||||
mapstruct.suppressGeneratorVersionInfoComment
|
||||
mapstruct.unmappedSourcePolicy
|
||||
mapstruct.unmappedTargetPolicy
|
||||
mapstruct.verbose
|
||||
Reference in New Issue
Block a user