简体   繁体   中英

ModelMapper & JPA: How to map DTO with id to Entity with oneToMany field

ModelMapper has been working great for converting my entities to DTOs, but i'm having trouble going the other way around since most of my entities have at least one field that's another entity itself.

Group:

@Entity
@Table(name = "GROUPS_")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Group {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID")
    private Long id;

    @Column(name = "NAME", nullable = false)
    private String name;

    @ManyToOne
    @JoinColumn(name = "AREA_ID", nullable = false)
    private Area area;
}

Area:

@Entity
@Table(name = "AREAS")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Area {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID")
    private Long id;

    @Column(name = "NAME", nullable = false)
    private String name;
}

GroupDTO:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class GroupRequestDTO {
    private String name;
    private Long areaId;
}

What i want to do:

private Group mapToEntity (GroupRequestDTO groupRequestDTO){
    return modelMapper.map(Group.class,groupRequestDTO);
}

the behaviour I'm expecting (also my current implementation)

private Group mapToEntity (GroupREquestDTO groupRequestDTO){
    return new Group(
        null,
        groupRequestDTO.getName(),
        entityManager.getReference(Area.class, groupRequestDTO.getAreaId())
    )
}

Might be useful to know that my RequestDTOs will always have referenced entities ids with the name [entitiy]Id and all entities' ids are called "id"

Bonus question, is there a way to generalize this for my other entities? i'm fine setting custom mappings for each entity (there's not that many) but it would be great for modelMapper to just know to convert "fooId" to a Foo entity/reference

I have also come across the same issue and this is how I map my DTOs with either list of Id's to a List of Entities(Which are @ManyToMany) or an Id to an Entity with @ManyToOne.

First, add a constructor to your Area.java or child entity as follow:

@Entity
@Table(name = "AREAS")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Area {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID")
    private Long id;

    @Column(name = "NAME", nullable = false)
    private String name;

    public Area(Long id) {
        this.id = id;
    }
}

Then in your controller layer or your test class, you have to make Converters as follows:

    private final ModelMapper mapper = new ModelMapper();
    static {
        Converter<Long, Area> idToArea = ctx -> new Area(ctx.getSource());
        TypeMap<GroupRequestDTO, Group> propertyMapper = 
        this.mapper.createTypeMap(GroupRequestDTO.class, Group.class);
        propertyMapper.addMappings(mapper -> {
            mapper.using(idToArea).map(GroupRequestDTO::getAreaId, Group::setArea);
        });
    }

Next in your method you can do this:

@Test
public void testMapDTOToEntity() {
    GroupRequestDTO groupRequestDTO = new GroupRequestDTO("Europe",12L);
    Group group = mapper.map(groupRequestDTO,Group.class);
    Assertions.assertEquals(group.getName(), groupRequestDTO.getName());
    Assertions.assertEquals(group.getArea().getId(), 
    groupRequestDTO.getAreaId());
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM