MapStruct problem solution with ManyToMany



    Hello, dear readers! Those who develop Web applications in Java using the Spring framework, those who comment on these applications and are simply interested.

    In the previous article, “Spring Boot Solving a Problem with ManyToMany”,

    I gave an example of a test application executed in which there is a ManyToMany bidirectional relationship between the two classes. The article gave an example of solving a looping problem when receiving a rest response using the DTO class. Readers in the comments suggested using the MapStruct library to solve the looping problem .

    After reading the documentationI made sure that this is a really strong thing, with the help of which you can solve quite complex tasks of moving data between objects. MapStruct solves the problem of looping including.

    In this article, I will give an example of solving the same problem as a Spring Boot application using the MapStruct library. Source code available on Github

    Entity People and SongPlayers remained unchanged. Getters and setters dropped.

    @EntitypublicclassPeople{
        @Id@GeneratedValue(strategy = GenerationType.AUTO)
        privatelong id;
        private String human;
        //несколько людей играют в одной группе@ManyToOne(cascade = CascadeType.ALL)
        private RockGroups rockGroups;
        //разные люди исполняют разные композиции@ManyToMany(mappedBy = "songInstrumentalist",fetch = FetchType.EAGER)
        private List<SongPlayers> songItems;
        publicPeople(){}
        publicPeople(long id, String human){
            this.id = id;
            this.human = human;
        }
    //. . . . .
    }

    @EntitypublicclassSongPlayers{
        @Id@GeneratedValue(strategy = GenerationType.AUTO)
        privatelong id;
        private String song;
        //у композиции один композиторprivate String composer;
        // и один автор стиховprivate String poet;
        //песня содержится в альбомеprivate String album;
        //и много исполнителей//исполнители могут исполнять разные песни@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
        private List<People> songInstrumentalist;
    //. . . . .
    }

    We also create Repository interfaces for the People and SongPlayers classes.

    @RepositorypublicinterfacePeopleRepositoryextendsJpaRepository<People, Long> {
        @Query("select h from People h where h.human=?1")
        List<People> searchByHuman(String human);
        List<People> findPeopleByHuman(String human);
    }

    @RepositorypublicinterfaceSongPlayersRepositoryextendsJpaRepository<SongPlayers, Long> {
        List<SongPlayers> findSongPlayersBySong(String song);
        List<SongPlayers> findSongPlayersByComposer(String composer);
        List<SongPlayers> findSongPlayersByPoet(String poet);
    }

    We also create DTO classes for People and SongPlayers, which now look not so cumbersome. Getters and setters omitted.

    publicclassPeopleDTO{
        privatelong id;
        private String human;
        private RockGroups rockGroups;
        private List<SongPlayersDTO> songPlayersList;
    // . . . . .
    }

    publicclassSongPlayersDTO{
        privatelong id;
        private String song;
        private String composer;
        private String poet;
        private String album;
    // . . . . .
    }

    To describe the rule for transferring data from the source object to the DTO object and, if necessary, back create mapper interfaces for each class for which protection against looping is required. Here I will give PeopleMapper and SongPlayersMapper

    @Mapper(uses = SongPlayersMapper.class)
    publicinterfacePeopleMapper{
        PeopleMapper PEOPLE_MAPPER = Mappers.getMapper(PeopleMapper.class);
        @Mapping(source = "songItems", target = "songPlayersList")
        PeopleDTO fromPeople(People people);
    }

    @Mapper/*(uses = {PeopleMapper.class})*/publicinterfaceSongPlayersMapper{
        SongPlayersMapper SONG_PLAYERS_MAPPER = Mappers.getMapper(SongPlayersMapper.class);
        SongPlayersDTO fromSongPlayers(SongPlayers songPlayers);
        @InheritInverseConfigurationSongPlayers toSongPlayers(SongPlayersDTO songPlayersDTO);
    }

    In the Service folder, we will create interfaces and implementation of service classes, in which we will place data retrieval methods (I will quote for People).

    publicinterfacePeopleService{
        List<PeopleDTO> getAllPeople();
        PeopleDTO getPeopleById(long id);
        People addPeople(People people);
        voiddelPeople(long id);
        ResponseEntity<Object> updPeople(People people, long id);
        List<RockGroups> getByHuman(String human);
        List<String> getSongByHuman(String human);
    }

    @Service("peopleservice")
    publicclassPeopleServiceImplimplementsPeopleService{
        @Autowiredprivate PeopleRepository repository;
        @Overridepublic List<PeopleDTO> getAllPeople(){
            List<PeopleDTO> peopleDTOList = new ArrayList<>();
            List<People> peopleList = repository.findAll();
            for (People people : peopleList){
                peopleDTOList.add(PeopleMapper.PEOPLE_MAPPER.fromPeople(people));
            }
            return peopleDTOList;
        }
    // . . . . .
    }

    In controllers, we will use these methods accordingly (again, only for People)

    @RestController@RequestMapping("/people")
    publicclassPeopleController{
        @Autowiredprivate PeopleServiceImpl service;
        @GetMapping("/all")
        public List<PeopleDTO> getAllPeople(){
            return service.getAllPeople();
        }
    // . . . . .
    }

    From the above solution of the looping problem with respect to ManyToMany, I can say that the variant with the DTO classes from the previous article and the variant with the MapStruct library from this article are also working. Compared with the previous version, the DTO class has become much simpler, but Mapper interfaces have been added. In general, any method can be used, for simple cases I would be inclined to the first option.

    I thank all the participants in the discussion. Waiting for your comments.

    Link to the previous article .

    Link to the project on Github .

    References:

    • Felipe Gutierrez Pro Spring Boot
    • Craig Walls Spring in action 5-th edition

    Also popular now: