Spring Boot Field Injection in MapStruct

DrPils

Bekanntes Mitglied
Hi

Ich möchte mit MapStruct meine Dtos zu Entities Mappen, da die Dtos nur die IDs von den Member enthalten, brauche ich in meinem Mapper die Repositories um die Member anhand der ID zu bekommen.
Nun werden die Repositories jedoch nicht instanziert.

Mapper:
Java:
@Mapper(componentModel = "spring", uses = {PlayerMapper.class, PlayerRepository.class, TournamentRepository.class})
public abstract class   TournamentPlayerMapper {

    @Autowired
    protected PlayerRepository playerRepository;
    @Autowired
    protected TournamentRepository tournamentRepository;

    @Mapping(source = "player.id", target = "playerId")
    public abstract TournamentPlayerDto fromTournamentPlayerToDto(TournamentPlayer tournamentPlayer);

    public abstract TournamentPlayer fromTournamentPlayerDtoToEntity(TournamentDto tournamentDto);

    protected Player fromLongToPlayer(Long playerId) {
        return playerRepository.findById(playerId).orElseThrow(PlayerNotFoundException::new);
    }

    protected Tournament fromLongToTournament(Long tournamentId) {
        return tournamentRepository.findById(tournamentId).orElseThrow(TournamentNotFoundException::new);
    }
}

Java:
public class TournamentPlayer extends BaseEntity{

    @OneToOne(cascade = MERGE)
    private Player player;

    @OneToOne(cascade = MERGE)
    private Tournament tournament;
    private LocalDateTime registeredAt;

    @PrePersist
    private void setRegisteredAt() {
        registeredAt = LocalDateTime.now();
    }
}

Java:
public record TournamentPlayerDto(Long id, long playerId, long tournamentId, LocalDateTime registeredAt) {
}

Hier bekomme ich eine NullpointerException weil playerRepository im Mapper null ist.
Test:
Java:
  @Test
    void player_should_be_mapped_by_id() {
        TournamentPlayer tournamentPlayer = tournamentPlayerRepository.findById(testTournamentPlayer.getId())
                .orElseThrow();
        Player player = tournamentPlayer.getPlayer();
        Player fromLongToPlayer = tournamentPlayerMapper.fromLongToPlayer(player.getId());
        assertNotNull(fromLongToPlayer);
    }
 
Sorry

Java:
@BeforeEach
    void setup() {
        tournamentPlayerMapper = Mappers.getMapper(TournamentPlayerMapper.class);
    }
 
Also ich muss gestehen, dass ich in dem Bereich nicht der große Experte bin, aber mit meinem bisherigen Verständnis ist das nicht der Weg, der gegangen werden sollte.

So findet sich in der Doku zu https://mapstruct.org/documentation/stable/api/org/mapstruct/factory/Mappers.html
Factory for obtaining mapper instances if no explicit component model such as CDI is configured via Mapper.componentModel().

Das Problem dürfte also sein, dass jetzt nicht Spring den Mapper erzeugt (inkl. der Dependency Injecton) sondern das MapStruct Framework die entsprechende Instanz erzeugt. (Und damit findet kein Einfügen der @Autowired Elemente statt).

Eine mögliche Lösung ist aus meiner Sicht hier die Nutzung von Qualifiers: https://mapstruct.org/documentation/stable/reference/html/#selection-based-on-qualifiers

Dabei hast Du halt eine saubere Trennung der Concerns.



Die andere Alternative könnte sein, hier mit Decorators zu arbeiten um dann den Mapper per Spring @Autowired Annotation zu bekommen. Das wäre dann in der Doku hier: https://mapstruct.org/documentation/stable/reference/html/#decorators-with-spring


Ich habe jetzt auch noch etwas geschaut ob ich etwas finde, wo man das evtl. etwas als ein Beispiel erkennen kann und bin dann über
gestolpert. Da sind beide Wege auch erwähnt in den Lösungen mit etwas Code.

Ich hoffe, dass Dir das etwas helfen konnte. Mehr kann ich leider im Augenblick nicht beitragen.
 
Hier noch die Ereklärung wieso ich nicht gleich den Mapper injected habe.

Intellij meldet da direkt den Fehler, dass entsprechender Autowired Kandidat nicht gefunden werden kann.
Das liegt darann dass die Klasse noch nicht generiert wurde, also einmal Builden und der Fehler ist beseitigt.
 

Neue Themen


Zurück
Oben