Search code examples
javaspringmapstructbidirectional-relation

One to Many Relation mapping using org.mapstruct framework


How can I map the one to many relationship using org.mapstruct framework?

DTO classes:

@Data
public class ScheduledJobDTO {

    private String jobName;
    private String jobGroup;
    private String jobClass;
    private String cronExpression;
    private Boolean cronJob;
    private Long repeatTime;
    private Integer repeatCount;
    private Set<ScheduledJobParamsDTO> paramtersDTOs;

}

@Data
@EqualsAndHashCode
public class ScheduledJobParamsDTO {

    String name;
    String value;
}

Domain Classes -

@Data
@Entity
@Table(name = "scheduled_job")
public class ScheduledJob {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(name = "job_name")
    private String jobName;
    @Column(name = "job_group")
    private String jobGroup;
    @Column(name = "job_class")
    private String jobClass;
    @Column(name = "cron_expression")
    private String cronExpression;
    @Column(name = "is_cron_job")
    private Boolean cronJob;
    @Column(name = "repeat_time")
    private Long repeatTime;
    @Column(name = "repeat_count")
    private Integer repeatCount;
    @Column(name = "trigger_start_date")
    private LocalDate triggerStartDate;
    @Column(name = "trigger_end_date")
    private LocalDate triggerEndDate;
    @Column(name = "created_at")
    private LocalDate createdAt;
    @Column(name = "modified_at")
    private LocalDate modifiedAt;
    @Column(name = "is_active")
    private Boolean active;
    @OneToMany(mappedBy = "scheduledJob")
    private Set<ScheduledJobParams> parameters;

}

@Data
@Entity
@Table(name = "scheduled_job_params")
@EqualsAndHashCode
public class ScheduledJobParams {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "scheduled_job_id", nullable = false)
    ScheduledJob scheduledJob;
    String name;
    String value;
}

Mapper Class -

@Mapping(source = ".", target = ".")
@Mapping(source = "paramtersDTOs", target = "parameters")
ScheduledJob mapToDomain(ScheduledJobDTO scheduledJobDTO);

Now, the above mapper is mapping the ScheduledJob & ScheduledJobParams, but the ScheduledJobParams has reference of ScheduledJob.

How can I map the reference ScheduledJob to ScheduledJobParams?


Solution

  • You can achieve that through @AfterMapping with @MappedTarget. This is described in the reference documentation: 12.2. Mapping customization with before-mapping and after-mapping methods.

    // Java 8+ otherwise you need to use an abstract class and a for-loop instead
    @Mapper(componentModel = "spring")
    public interface ScheduledJobMapper {
    
        @Mapping(target = "parameters", source = "paramtersDTOs")
        ScheduledJob mapToDomain(ScheduledJobDTO dto);
    
        @AfterMapping
        default void after(@MappingTarget ScheduledJob domain, ScheduledJobDTO dto) {
            domain.getParameters().forEach(scheduledJobParams -> {
                scheduledJobParams.setScheduledJob(domain);
            });
        }
    }
    

    However, I am sure you don't need to fill the bidirectional relationship when you map back from the DTO into the entity (this is what I understand as you refer to "domain"). Note printing out or serializing such object i.e. into JSON or XML throws java.lang.StackOverflowError if not properly handled.