Search code examples
javaspringmapstruct

MapStruct: Mapping a source class with an object (with field UUID) to target with field UUID result in null


I have this Entity here:

@Data
@Entity
@Table(name="transactions")
public class Transaction {

    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    @Column(name = "transaction_uuid")
    public UUID transactionUUID;

    public BigDecimal amount;

    @ManyToOne
    public User senderUser;

    @ManyToOne
    public User receiverUser;
}

with the User class:

@Entity
@Table(name = "users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    @Column(name = "user_uuid")
    public UUID userUUID;
    public String username;
    @Column(unique = true)
    public String email;
    @Column(unique = true)
    public String cpf;
    public String password;
    public BigDecimal balance;
    public UserType userType;
}

I'm trying to map to this DTO:

public record TransactionResponse(BigDecimal amount, UUID sender, UUID receiver) {
}

This is my mapper:

@Mapper(componentModel = "spring", uses = UserMapper.class)
public interface TransactionMapper {

    Transaction mapRequestToTransaction(TransactionRequest transactionRequest);

    // TODO: sender and receiver in target class are null
    @Mappings({
        @Mapping(target = "sender", source = "senderUser.userUUID"),
        @Mapping(target = "receiver", source = "receiverUser.userUUID")
    })
//    @Mapping(target = "sender", source = "senderUser", qualifiedByName = "sender")
//    @Mapping(target = "receiver", source = "receiverUser", qualifiedByName = "receiver")
    List<TransactionResponse> mapEntityListToTransactionResponseList(List<Transaction> transaction);

    @Named("sender")
    default UUID sender(Transaction item) {
        return item.getSenderUser().userUUID;
    }

    @Named("receiver")
    default UUID receiver(Transaction item) {
        return item.getReceiverUser().userUUID;
    }
}

P.s.: the comment part is what I already have tried but no success either

The target class is returning the sender and receiver field null, here is the generated class:

public class TransactionMapperImpl implements TransactionMapper {

    @Override
    public Transaction mapRequestToTransaction(TransactionRequest transactionRequest) {
        if ( transactionRequest == null ) {
            return null;
        }

        Transaction transaction = new Transaction();

        transaction.setAmount( transactionRequest.amount() );

        return transaction;
    }

    @Override
    public List<TransactionResponse> mapEntityListToTransactionResponseList(List<Transaction> transaction) {
        if ( transaction == null ) {
            return null;
        }

        List<TransactionResponse> list = new ArrayList<TransactionResponse>( transaction.size() );
        for ( Transaction transaction1 : transaction ) {
            list.add( transactionToTransactionResponse( transaction1 ) );
        }

        return list;
    }

    protected TransactionResponse transactionToTransactionResponse(Transaction transaction) {
        if ( transaction == null ) {
            return null;
        }

        BigDecimal amount = null;

        amount = transaction.getAmount();

        UUID sender = null;
        UUID receiver = null;

        TransactionResponse transactionResponse = new TransactionResponse( amount, sender, receiver );

        return transactionResponse;
    }
}

`I already have tried:

  1. @Mapping(target = "sender", source = "senderUser.userUUID"),
    @Mapping(target = "receiver", source = "receiverUser.userUUID")
    
  2. @Mapping(target = "sender", source = "transaction.senderUser.userUUID"), @Mapping(target = "receiver", source = "transaction.receiverUser.userUUID")

  3. @Mapping(target = "sender", source = "senderUser", qualifiedByName = "sender") @Mapping(target = "receiver", source = "receiverUser", qualifiedByName = "receiver")

@Named("sender")
    default UUID sender(Transaction item) {
        return item.getSenderUser().userUUID;
    }

@Named("receiver")
default UUID receiver(Transaction item) {
    return item.getReceiverUser().userUUID;
}`

Solution

  • Just create a mapper from Trasaction to TransactionResponse

    class Mapper {
      @Mapping(target="sender", source="senderUser.userUUID")
      @Mapping(target="receiver", source="receiverUser.userUUID")
      TransactionResponse map(Transaction source);
    
      List<TransactionResponse> mapEntityListToTransactionResponseList(List<Transaction> transaction);
    }
    

    when I have a 'list-of' to map - as a rule of thumb - I map contained type and not the list itself; iterable mapping will be autogenerated.