0
votes

I have an issue with Mapstruct mapping my entities in a Spring-Boot rest api with One-To-Many and Many-To-One relationship.

I use
mapstruct 1.4.2.Final, Lombok 1.18.20

I have the classes below

Policy.class

@Data
@Entity
@Table(name = "Policy")
public class Policy implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @Column(name = "Policy_UID", nullable = false)
    private Integer policyUid;

    //other fields without relationships

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "userID", referencedColumnName = "Id")
    private User user;
}


User.class

@Data
@Entity
@Table(name = "User")
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @Column(name = "Id")
    private Integer id;

    //other fields without relationships

    @OneToMany(mappedBy = "user")
    @JsonIgnore
    public List<Policy> policyList;
}



PolicyDTO.class

@Data
@AllArgsConstructor
@NoArgsConstructor
public class PolicyDTO {

    private Integer policyUid;

    //other fields without relationships

    private UserDTO user;
}

UserDTO.class

@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserDTO {

    private Integer id;

    //other fields without relationships

    public List<PolicyDTO> policyList;
}


PolicyMapper.class

@Mapper (componentModel = "spring")
public interface PolicyMapper {
    PolicyDTO policytoPolicyDTO(Policy policy);
    List<PolicyDTO> toPolicyListDTO(List<Policy> policies);
}

UserMapper.class

@Mapper(componentModel = "spring")
public interface UserMapper {
    UserDTO toUserDTO(User user);
    List<UserDTO> toUserListDTO(List<User> UsersList);
}

and the calling for results

List<PolicyDTO> list = policyMapper.toPolicyListDTO(paging.getContent());
return new PageImpl<>(list, page, paging.getTotalElements());

So when I put in comment the private UserDTO user; into PolicyDTO and public List policyList; into UserDTO

The result Policies list it's ok with values in all fields except the two commented of course. If I uncomment the two fields into DTO's with the bidirectional relationship the result is null not only in relationship fields but in all fields. all fields are null like no results at all behavior.

So the problem is with bidirectional one-to-many and many-to-one relationship mapping.

In addition to this, I have to say for your info that most Policies have null UserID if it matters.

Does anyone have any suggestions on this issue?

Thank you in advance

2
Try adding the annotations like in the examples.. github.com/mapstruct/mapstruct-examples/blob/master/… - Noixes
mapping between the fields takes place when the source and target beans field names are same. Else if they are different you need to add the @Mapping annotation by specifying source and target fields. And also when you compile your application, there will be UserMapperImpl and PolicyMapperImpl class files generated you can always set the debug points there or you can see the generated mapping to verify. - Lucia
@Lucia Whats about @Mapper(uses = { OrderItemMapper.class }) - Noixes

2 Answers

0
votes

first of all try to use the MapStruct version

<mapstruct.version>1.3.1.Final</mapstruct.version>

To implement MapStruct with spring you should create a interface EntityMapper with two generic params <D,E> and write this methods below :

public interface EntityMapper <D, E> {

    public E toEntity(D dto);

    public D toDto(E entity);

    public List<E> toEntity(List<D> dtoList);

    public List <D> toDto(List<E> entityList);
}

And create your own entity interface

@Mapper(componentModel = "spring", uses = {})
public interface PolicyMapper extends EntityMapper<PolicyDTO, Policy> {
    PolicyDTO toDto(Policy policy);

    Policy toEntity(PolicyDTO policyDTO);

    default Policy fromId(Long id) {
        if (id == null) {
            return null;
        }
        Policy policy = new Policy();
        policy.setId(id);
        return policy;
    }
}

@Mapper(componentModel = "spring", uses = {})
public interface UserMapper extends EntityMapper<UserDTO, User> {
    UserDTO toDto(User user);

    User toEntity(UserDTO userDTO);

    default User fromId(Long id) {
        if (id == null) {
            return null;
        }
        User user = new User();
        user.setId(id);
        return user;
    }
}

You dont need to define a method to render a List, this is a functionnality of MapStruct to map all fields in the entity, you need just toDto, toEntity, and it will be work.

I hope that's help you ;)

0
votes

@BENMOHAMED i need mostly the policy entity the many-to-one side from biderectional. My annotationProccessorPath is

                        <path>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>${org.mapstruct.version}</version>
                        </path>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>1.18.20</version>
                        </path>
                        <!-- other annotation processors -->
                    </annotationProcessorPaths>

After a lot of research it was about the priority in annotationProcessorPaths settings in pom.xml I have set the lombok path first and then the mapstruct path and the issue was solved. The correct settings :

                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>1.18.20</version>
                        </path>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok-mapstruct-binding</artifactId>
                            <version>0.2.0</version>
                        </path>
                        <path>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>${org.mapstruct.version}</version>
                        </path>
                        <!-- other annotation processors -->
                    </annotationProcessorPaths>