mapstruct / mapstruct/mapstruct
using @MappingTarget with @Default constructor annotated member class
@sjaakd is already working on this.
Since Aug 21, 2020.
- Dominant language
- Java
- Stars
- 7.7k
- Forks
- 1.1k
- PR merge metrics
- No merged PRs in 30d
Description
Mapping method with @MappingTarget generates code that tries to alter immutable member instead of setting a new instance.
Example:
If I have a class with a constructor injected member:
```
public class DemonstratorTarget {
private DemonstratorTargetEmbedded embedded;
@Default
public DemonstratorTarget(DemonstratorTargetEmbedded embedded) { this.embedded = embedded; }
public DemonstratorTargetEmbedded getEmbedded() { return embedded; }
public void setEmbedded(DemonstratorTargetEmbedded embedded) { this.embedded = embedded; }
}
```
with a supposedly immutable member class:
```
public class DemonstratorTargetEmbedded {
private String version;
private Map metadata = new HashMap<>();
public DemonstratorTargetEmbedded() { }
@Default
public DemonstratorTargetEmbedded(String version, Map metadata) {
this.version = version;
this.metadata = metadata;
}
public String getVersion() { return version; }
public Map getMetadata() { return metadata; }
}
```
and use it in a mapper as `@MappingTarget`:
```
@Mapper
public interface DemonstratorMapperBroken {
void updateTarget(@MappingTarget DemonstratorTarget target, DemonstratorSource source);
}
```
The resulting mapper will not call `target.setEmbedded([newly created instance])` but tries to alter the member:
```
mappingTarget.getMetadata().clear();
Map map = demonstratorSourceEmbedded.getMetadata();
if ( map != null ) {
mappingTarget.getMetadata().putAll( map );
}
```
This of course fails in the example above, because there is no setter for `version`.
Now if I change the mapper to look like this (added a mapping method, did not change the original one):
```
@Mapper
public interface DemonstratorMapperWorking {
void updateTarget(@MappingTarget DemonstratorTarget target, DemonstratorSource source);
DemonstratorTargetEmbedded toTargetEmbedded(DemonstratorSourceEmbedded gender);
}
```
...the mapper works as expected:
```
target.setEmbedded( toTargetEmbedded( source.getEmbedded() ) );
[...]
DemonstratorTargetEmbedded demonstratorTargetEmbedded = new DemonstratorTargetEmbedded( version, metadata );
```
I am unsure if this is expected behaviour. Is there a way to circumvent this without adding a method like `toTargetEmbedded`?
I created a demonstrator:
1. `git clone -b mapstruct-constructor-mapping https://github.com/mickroll/demonstrator.git`
2. `cd demonstrator`
3. `mvn clean install`
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.