#AfterMapping mehod is not called in generated class - mapstruct

I'm trying to customize a mapping use a string to determine an object attribute so I wrote this:
#Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
public abstract class ProductMapper {
public abstract ProductInput asProductInputFromIdentifier(String identifier);
#AfterMapping
protected void determineIdentifier(String identifier, #MappingTarget ProductInput out) {
if (StringUtils.contains(identifier, '?')) {
out.setExternalId(identifier);
} else {
out.setInernalId(identifier);
}
}
}
The generated class doesn't call the determineIdentifier method. I found another solution by using directly the Java expression on the asProductInputFromIdentifier method but I really want to write a clear code using the #AfterMapping.
#Mapping(target = "externalId", expression = "java( org.apache.commons.lang3.StringUtils.contains(identifier, '|') ? identifier : null )")
#Mapping(target = "internalId", expression = "java( !org.apache.commons.lang3.StringUtils.contains(identifier, '|') ? identifier : null )")
public abstract ProductInput asProductDetailInputFromIdentifier(String identifier);
I didn't understand why it doesn't work Is it because I don't have an Object in method parameter?

Method with #AfterMapping will be executed at the end of map method and it should have same input parameter as map method for example in below sample
#Mapper(
componentModel = "spring",
injectionStrategy = InjectionStrategy.CONSTRUCTOR,
uses = {IdentifierMapper.class})
public interface HistoryMapper {
HistoryDynamo toHistoryDynamo(History history);
#AfterMapping
default void changeReason(History history) {
System.out.println(history.getReason());
}
}
Refer github working sample for the same https://github.com/rakesh-singh-samples/map-struct-samples/tree/stack-question-60523230/src/sample/mapstruct/mapper

MapStruct doesn't know how to map from String to ProductInput in your case I would suggest that you implement the mapping method on your own.
e.g.
#Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
public abstract class ProductMapper {
public ProductInput asProductInputFromIdentifier(String identifier) {
ProductInput out = new ProductInput();
if (StringUtils.contains(identifier, '?')) {
out.setExternalId(identifier);
} else {
out.setInernalId(identifier);
}
return out;
}
}
There is no point in using lifecycle callbacks nor special #Mapping annotations to perform a simple mapping like in this example.

To solve the problem, I added the to ProductInput to the abstract method parameters as follow (which is not the requirement). As explains #Rakesh in his answer above Method with #AfterMapping will be executed at the end of map method and it should have same input parameter as map method, but it can interest others in their case:
#Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
public abstract class ProductMapper {
public abstract ProductInput asProductInputFromIdentifier(String identifier, ProductInput out);
#AfterMapping
protected void determineIdentifier(String identifier, #MappingTarget ProductInput out) {
if (StringUtils.contains(identifier, '?')) {
out.setExternalId(identifier);
} else {
out.setInernalId(identifier);
}
}
}
But In my case I used the builder = #Builder(disableBuilder = true) to turn off "builders" in MapStruct be it can help someone ;) Mapstruct Builder
Now the generated MapperImpl calls the annotated #AfterMapping method.

As #Filip mentioned, your #Aftermapping method should return the same return type as that of the mapping method.
#AfterMapping
protected ProductInput determineIdentifier(String identifier, #MappingTarget ProductInput out) {
if (StringUtils.contains(identifier, '?')) {
out.setExternalId(identifier);
} else {
out.setInernalId(identifier);
}
return out;
}
I was using Lombok #Builder annotation on my returned object. When I removed that annotation, my code started working.

Related

Mapstruct: aftermapping with parameters

Here my code:
#Mapping(target = "auditoriaMetas", qualifiedByName = "sdf")
public abstract Auditoria mapToModificacio(QdCF qdcf, QdCFPresenter qdcfPresenter, Integer idInstrument);
#Named("sdf")
public List<AuditoriaMeta> mapToMetas(QdCF current, #Context QdCFPresenter incoming) {
return null;
}
I want that after mapToModificatio is performed, mapToMetas is also executed.
Above code doesn't perform.
Any ideas?
Mapstruct will not consider putting a normal value into one that is annotated with #Context. Therefor if you mark something with #Context, then it should be marked like that through the entire chain of calls.
For example:
#Mapping(target = "auditoriaMetas", source=".", qualifiedByName = "sdf")
public abstract Auditoria mapToModificacio(QdCF qdcf, #Context QdCFPresenter qdcfPresenter, Integer idInstrument);
#Named("sdf")
public List<AuditoriaMeta> mapToMetas(QdCF current, #Context QdCFPresenter incoming) {
return null;
}

Mapping a field using existing target value (Mapstruct)

i have a custom case that some of my dto's have a field of type X, and i need to map this class to Y by using a spring service method call(i do a transactional db operation and return an instance of Y). But in this scenario i need to use existing value of Y field. Let me explain it by example.
// DTO
public class AnnualLeaveRequest {
private FileInfoDTO annualLeaveFile;
}
//ENTITY
public class AnnualLeave {
#OneToOne
private FileReference annualLeaveFile;
}
#Mapper
public abstract class FileMapper {
#Autowired
private FileReferenceService fileReferenceService;
public FileReference toFileReference(#MappingTarget FileReference fileReference, FileInfoDTO fileInfoDTO) {
return fileReferenceService.updateFile(fileInfoDTO, fileReference);
}
}
//ACTUAL ENTITY MAPPER
#Mapper(uses = {FileMapper.class})
public interface AnnualLeaveMapper {
void updateEntity(#MappingTarget AnnualLeave entity, AnnualLeaveRequest dto);
}
// WHAT IM TRYING TO ACHIEVE
#Component
public class MazeretIzinMapperImpl implements tr.gov.hmb.ikys.personel.izinbilgisi.mazeretizin.mapper.MazeretIzinMapper {
#Autowired
private FileMapper fileMapper;
#Override
public void updateEntity(AnnualLeave entity, AnnualLeaveUpdateRequest dto) {
entity.setAnnualLeaveFile(fileMapper.toFileReference(dto.getAnnualLeaveFile(), entity.getAnnualLeaveFile()));
}
}
But mapstruct ignores the result of "FileReference toFileReference(#MappingTarget FileReference fileReference, FileInfoDTO fileInfoDTO) " and does not map the result of it to the actual entity's FileReference field. Do you have any idea for resolving this problem?
Question
How do I replace the annualLeaveFile property while updating the AnnualLeave entity?
Answer
You can use expression to get this result. For example:
#Autowired
FileMapper fileMapper;
#Mapping( target = "annualLeaveFile", expression = "java(fileMapper.toFileReference(entity.getAnnualLeaveFile(), dto.getAnnualLeaveFile()))" )
abstract void updateEntity(#MappingTarget AnnualLeave entity, AnnualLeaveRequest dto);
MapStruct does not support this without expression usage. See the end of the Old analysis for why.
Alternative without expression
Instead of fixing it in the location where FileMapper is used, we fix it inside the FileMapper itself.
#Mapper
public abstract class FileMapper {
#Autowired
private FileReferenceService fileReferenceService;
public void toFileReference(#MappingTarget FileReference fileReference, FileInfoDTO fileInfoDTO) {
FileReference wanted = fileReferenceService.updateFile(fileInfoDTO, fileReference);
updateFileReference(fileReference, wanted);
}
// used to copy the content of the service one to the mapstruct one.
abstract void updateFileReference(#MappingTarget FileReference fileReferenceTarget, FileReference fileReferenceFromService);
}
Old analysis
The following is what I notice:
(Optional) your FileMapper class is not a MapStruct mapper. This can just be a normal class annotated with #Component, since it does not have any unimplemented abstract methods. (Does not affect code generation of the MazeretIzinMapper implementation)
(Optional, since you have this project wide configured) you do not have componentModel="spring" in your #Mapper definition, maybe you have this configured project wide, but that is not mentioned. (required for the #Autowired annotation, and #Component on implementations)
Without changing anything I already get a working result as you want it to be, but for non-update methods (not listed in your question, but was visible on the gitter page where you also requested help) the FileMapper as is will not be used. It requires an additional method that takes only 1 argument: public FileReference toFileReference(FileInfoDTO fileInfoDTO)
(Edit) to get rid of the else statement with null value handling you can add nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE to the #Mapper annotation.
I've run a test and with 1.5.0.Beta2 and 1.4.2.Final I get the following result with the thereafter listed FileMapper and MazeretIzinMapper classes.
Generated mapper implementation
#Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2022-03-11T18:01:30+0100",
comments = "version: 1.4.2.Final, compiler: Eclipse JDT (IDE) 1.4.50.v20210914-1429, environment: Java 17.0.1 (Azul Systems, Inc.)"
)
#Component
public class MazeretIzinMapperImpl implements MazeretIzinMapper {
#Autowired
private FileMapper fileMapper;
#Override
public AnnualLeave toEntity(AnnualLeaveRequest dto) {
if ( dto == null ) {
return null;
}
AnnualLeave annualLeave = new AnnualLeave();
annualLeave.setAnnualLeaveFile( fileMapper.toFileReference( dto.getAnnualLeaveFile() ) );
return annualLeave;
}
#Override
public void updateEntity(AnnualLeave entity, AnnualLeaveRequest dto) {
if ( dto == null ) {
return;
}
if ( dto.getAnnualLeaveFile() != null ) {
if ( entity.getAnnualLeaveFile() == null ) {
entity.setAnnualLeaveFile( new FileReference() );
}
fileMapper.toFileReference( entity.getAnnualLeaveFile(), dto.getAnnualLeaveFile() );
}
}
}
Source classes
Mapper
#Mapper( componentModel = "spring", uses = { FileMapper.class }, nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE )
public interface MazeretIzinMapper {
AnnualLeave toEntity(AnnualLeaveRequest dto);
void updateEntity(#MappingTarget AnnualLeave entity, AnnualLeaveRequest dto);
}
FileMapper component
#Mapper
public abstract class FileMapper {
#Autowired
private FileReferenceService fileReferenceService;
public FileReference toFileReference(#MappingTarget FileReference fileReference, FileInfoDTO fileInfoDTO) {
return fileReferenceService.updateFile( fileInfoDTO, fileReference );
}
public FileReference toFileReference(FileInfoDTO fileInfoDTO) {
return toFileReference( new FileReference(), fileInfoDTO );
}
// other abstract methods for MapStruct mapper generation.
}
Why the exact wanted code will not be generated
When generating the mapping code MapStruct will use the most generic way to do this.
An update mapper has the following criteria:
The #MappingTarget annotated argument will always be updated.
It is allowed to have no return type.
the generic way to update a field is then as follows:
// check if source has the value.
if (source.getProperty() != null) {
// Since it is allowed to have a void method for update mappings the following steps are needed:
// check if the property exists in the target.
if (target.getProperty() == null) {
// if it does not have the value then create it.
target.setProperty( new TypeOfProperty() );
}
// now we know that target has the property so we can call the update method.
propertyUpdateMappingMethod( target.getProperty(), source.getProperty() );
// The arguments will match the order as specified in the other update method. in this case the #MappingTarget annotated argument is the first one.
} else {
// default behavior is to set the target property to null, you can influence this with nullValuePropertyMappingStrategy.
target.setProperty( null );
}

Autofac - One interface, multiple implementations

Single interface: IDoSomething {...}
Two classes implement that interface:
ClassA : IDoSomething {...}
ClassB : IDoSomething {...}
One class uses any of those classes.
public class DummyClass(IDoSomething doSomething) {...}
code without Autofac:
{
....
IDoSomething myProperty;
if (type == "A")
myProperty = new DummyClass (new ClassA());
else
myProperty = new DummyClass (new ClassB());
myProperty.CallSomeMethod();
....
}
Is it possible to implement something like that using Autofac?
Thanks in advance,
What you are looking for is, as I remember, the Strategy Pattern. You may have N implementations of a single interface. As long you register them all, Autofac or any other DI framework should provide them all.
One of the options would be to create a declaration of the property with private setter or only getter inside Interface then implement that property in each of the class. In the class where you need to select the correct implementation, the constructor should have the parameter IEnumerable<ICommon>.
Autofac or any other DI frameworks should inject all possible implementation. After that, you could spin foreach and search for the desired property.
It may look something like this.
public interface ICommon{
string Identifier{get;}
void commonAction();
}
public class A: ICommon{
public string Identifier { get{return "ClassA";} }
public void commonAction()
{
Console.WriteLine("ClassA");
}
}
public class A: ICommon{
public string Identifier { get{return "ClassB";} }
public void commonAction()
{
Console.WriteLine("ClassA");
}
}
public class Action{
private IEnumerable<ICommon> _common;
public Action(IEnumerable<ICommon> common){
_common = common;
}
public void SelectorMethod(){
foreach(var classes in _common){
if(classes.Identifier == "ClassA"){
classes.commonAction();
}
}
}
}

Mapstruct #Mapper's uses attribute not working

I am facing an issue with one of my Mapstruct mappers not using another mapper with #Mapper(uses =
Our ValidationSupportNeedMapper maps from entities to DTOs. One ValidationSupportNeedEntity contains an ActivityEntity property and I am trying to map from this property to an Activity DTO.
The issue is therefore with a nested object i.e. ActivityEntity to Activity.
Here is the source code:
From ValidationSupportNeedMapper.java:
#Mapper(uses = {LifecycleErrorMessagesMapper.class, ActivityMapper.class})
public interface ValidationSupportNeedMapper {
ValidationSupportNeed toValidationSupportNeed(ValidationSupportNeedEntity source);
...
From ActivityMapper.java:
#Component
public class ActivityMapper {
public Activity toActivity(ActivityEntity activity) {
//Implementation
}
public ActivityEntity toActivityEntity(Activity activity) {
//Implementation
}
}
From ValidationSupportNeedEntity.java (Entity)
public class ValidationSupportNeedEntity {
private ActivityEntity activityEntity;
From ValidationSupportNeed.java (DTO)
public class ValidationSupportNeed implements AutoValidated {
private Activity validationActivity;
However Mapstruct seems to ignore the uses= attribute on the #Mapper annotation and goes ahead and generates its own mapping method as follows:
#Override
public ValidationSupportNeed toValidationSupportNeed(ValidationSupportNeedEntity source) {
if ( source == null ) {
return null;
}
ValidationSupportNeed validationSupportNeed = new ValidationSupportNeed();
validationSupportNeed.setValidationActivity( validationSupportNeedEntityToActivity( source ) );
...
}
protected Activity validationSupportNeedEntityToActivity(ValidationSupportNeedEntity validationSupportNeedEntity) {
if ( validationSupportNeedEntity == null ) {
return null;
}
Activity activity = new Activity();
activity.setCode( validationSupportNeedEntity.getValidationActivityCode() );
return activity;
}
What am I missing? Can someone please help?
edit: ActivityMapper is not autowired into the ValidationSupportNeedMapper implementation.
Adding a mapping annotation sorted the issue:
#Mapping(source = "activityEntity", target = "validationActivity")
ValidationSupportNeed toValidationSupportNeed(ValidationSupportNeedEntity source);
Notice the names of the attributes are different.

EF 5 Model First Partial Class Custom Constructor How To?

EF has generated for me some partial classes, each with a constructor, but it says not to touch them (example below), now if I make my own secondary partial class and I want to have a constructor that automatically sets some of the fields how do I do so as it would conflict?
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Breakdown.Models
{
using System;
using System.Collections.Generic;
public partial class Call
{
public Call()
{
this.Logs = new HashSet<Log>();
}
...
}
}
Partial methods can help you here, in the T4 Templates define a body-less partial method and call that inside the constructor.
public <#=code.Escape(entity)#>()
{
...
OnInit();
}
partial void OnInit();
Then in your partial class define the partial method and place inside that what you want to do in the constructor. If you don't want to do anything then you don't need to define the partial method.
partial class Entity()
{
partial void OnInit()
{
//constructor stuff
...
}
}
http://msdn.microsoft.com/en-us/library/vstudio/6b0scde8.aspx
This is not possible.
Partial classes are essentially parts of the same class.
No method can be defined twice or overridden (same rule apply for the constructor also)
But You can use below mentioned Workaround :
//From file SomeClass.cs - generated by the tool
public partial class SomeClass
{
// ...
}
// From file SomeClass.cs - created by me
public partial class SomeClass
{
// My new constructor - construct from SomeOtherType
// Call the default ctor so important initialization can be done
public SomeClass(SomeOtherType value) : this()
{
}
}
for more information check Partial Classes, Default Constructors
I hope this will help to you.
I wanted to do the same recently and ended up modifying the T4 template so I could implement my own parameterless constructor manually. To accomplish this you can remove the constructor from the generated classes and move the instantiation of collections etc to outside the constructor so this:
public Call()
{
this.Logs = new HashSet<Log>();
}
becomes this:
private ICollection<Log> logs = new HashSet<Log>();
public virtual ICollection<Log> Logs
{
get { return this.logs; }
set { this.logs = value; }
}
The drawback I suppose is that the generated classes are not as "clean". That is you can't just have auto-implemented properties for your complex/nav types.
In your model.tt file you can prevent the constructor generation by removing the below code, commenting it out or by just putting in a false into the conditional so it never gets executed:
if (propertiesWithDefaultValues.Any() || complexProperties.Any())
{
#>
public <#=code.Escape(complex)#>()
{
<#
foreach (var edmProperty in propertiesWithDefaultValues)
{
#>
this.<#=code.Escape(edmProperty)#> =
<#=typeMapper.CreateLiteral(edmProperty.DefaultValue)#>;
<#
}
foreach (var complexProperty in complexProperties)
{
#>
this.<#=code.Escape(complexProperty)#> = new
<#=typeMapper.GetTypeName(complexProperty.TypeUsage)#>();
<#
}
#>
}
Then below this you need to do some modification where properties are generated for your complex and navigation types. Add a private var with object instantiation and a property for accessing the private var for each of these eg:
if (complexProperties.Any())
{
foreach(var complexProperty in complexProperties)
{
//generate private var + any instantiation
//generate property for accessing var
}
}
Depending on the complexity of your model there may be other areas you need to modify. Hopefully this gets you started.
If I well understand the question, you need this constructor when creating a new entity, that is an entity that was not persisted before.
My case was to set a default value to all datetime, that is initalize them to "the begining of time" : 1900-01-01.
In this case I use an entity factory
public static T GetNewEntity<T> () {
T e;
try {
e = Activator.CreateInstance<T>();
} catch {
e = default(T);
}
SetDefaults(e);
return e;
}
Each time I need a new Entity I use
Entity e = GetNewEntity<Entity>();
with SetDefaults as :
public static void SetDefaults (object o) {
Type T = o.GetType();
foreach ( MemberInfo m in T.GetProperties() ) {
PropertyInfo P = T.GetProperty(m.Name);
switch ( Type.GetTypeCode(P.PropertyType) ) {
case TypeCode.String :
if ( P.GetValue(o, null) == null )
P.SetValue(o, String.Empty, null);
break;
case TypeCode.DateTime :
if ( (DateTime)P.GetValue(o, null) == DateTime.MinValue )
P.SetValue(o, EntityTools.dtDef, null);
break;
}
}
}
full code is here
It could be rewrittent to consider the entity type and so on...
Add a base class:
public class CallBase
{
protected CallBase()
{
Initialize();
}
protected abstract void Initialize();
}
Add the partial class implementation in another file
public partial class Call: CallBase
{
protected override void Initialize();
{
...
}
}
The drawback is that the Initialization method will be called before the all collection creature.