kukovsky
kukovsky

Reputation: 1

One To Many and Many to One using mapstruct problem

I'm trying to use mapstruct to map entity with buisness object. I have layer structure in project. (DTO, Buisness, Entity) In this configure of mapping I am ignoring user/reservations so they are "null". If i set it to different value it gets into infinite cycle (StackOverFlow) I need to get reservation with info about user (userId etc.)

@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface ReservationsEntityMapper {

    @Mapping(target = "user", ignore = true)
    ReservationsEntity mapToEntity(Reservations entity);

    @Mapping(target = "user", ignore = true)
    Reservations mapFromEntity(ReservationsEntity entity);

}

@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface UsersEntityMapper {
    @Mapping(target = "reservations", ignore = true)
    User mapFromEntity(FlightAppUsersEntity entity);

    @Mapping(target = "reservations", ignore = true)
    FlightAppUsersEntity mapToEntity(User user);
}

@Mapper(componentModel = "spring")
public interface ReservationsMapper {
    @Mapping(target = "user.userId", source = "user.userId")
    ReservationsDTO map(final Reservations reservations);

    @Mapping(target = "user.createdAt", ignore = true)
    @Mapping(target = "user.active", ignore = true)
    @Mapping(target = "user.roles", ignore = true)
    @Mapping(target = "createdAt", ignore = true)
    @Mapping(target = "user.userId", source = "user.userId")
    Reservations map(ReservationsDTO reservationsDTO);
}

@Mapper(componentModel = "spring", uses = ReservationsMapper.class)
public interface UsersMapper {

    @Mapping(target = "userId", source = "userId")
    UserDTO map(final User user);

    @Mapping(target = "roles", ignore = true)
    @Mapping(target = "active", ignore = true)
    @Mapping(target = "createdAt", ignore = true)
    User map(UserDTO userDTO);
}

DTO/Buisness/Entity

@Value
@With
@Builder
@EqualsAndHashCode(of = "reservationId")
@ToString(of = {"reservationId", "origin", "destination", "departureDate", "returnDate", "airline", "price", "currency", "numberOfPassengers", "createdAt", "user"})
public class Reservations {

    Integer reservationId;
    //some fields
    ReservationStatus status;
    User user;
}

@Value
@With
@Builder
@EqualsAndHashCode(of = "email")
@ToString(of = {"userId", "firstName", "lastName", "email", "roles", "createdAt", "reservations"})
public class User {

    Integer userId;
    //some fields
    Set<FlightAppRoles> roles;
    Set<Reservations> reservations;
}

@Getter
@Setter
@EqualsAndHashCode(of = "userId")
@ToString(of = {"userId", "firstName", "lastName", "email", "createdAt"})
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "flightapp_users")
public class FlightAppUsersEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "user_id", nullable = false)
    private Integer userId;

    //some fields

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
    private Set<ReservationsEntity> reservations;

    @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinTable(
            name = "flightapp_user_roles",
            joinColumns = @JoinColumn(name = "user_id"),
                inverseJoinColumns = @JoinColumn(name = "role_id")
    )
    private Set<FlightAppRoles> roles;
}

@Getter
@Setter
@EqualsAndHashCode(of = "reservationId")
@ToString(of = {"reservationId", "user", "origin", "destination", "departureDate", "returnDate", "airline", "price", "currency", "numberOfPassengers", "createdAt"})
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "reservations")
public class ReservationsEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "reservation_id", nullable = false)
    private Integer reservationId;

    //some fields

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "user_id", nullable = false)
    private FlightAppUsersEntity user;
}
Example of reservation:
reservation: Reservations(reservationId=29, origin=EWR, destination=LIS, departureDate=2025-02-27T17:40, returnDate=2025-03-01T12:50, airline=TP, price=805.37, currency=EUR, numberOfPassengers=1, createdAt=2025-02-27T04:28:35.877725, user=null)

How to implement maybe @Aftermapping or other solution to this??

Upvotes: 0

Views: 42

Answers (1)

Rob Spoor
Rob Spoor

Reputation: 9175

I actually gave a presentation about this at work this morning.

MapStruct has an example for this themselves, at https://github.com/mapstruct/mapstruct-examples/tree/main/mapstruct-jpa-child-parent/src/main/java/org/mapstruct/jpa. Basically, you create a context class that allows you to store references to the parent and pass that along using @Context. The context will then take care of setting the relations due to its own MapStruct annotations. The generated code will do the following:

  • Create an instance of the parent class
  • Store a reference to it in the context
  • Do mapping as necessary, passing the context to child mapper methods.
  • Child mapper methods will do their usual work but end with letting the context set the reference to the parent.

You can go deeper than just one relation by simply adding more content to your context class.

Upvotes: 1

Related Questions