📜 ⬆️ ⬇️

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 bidirectional relation between ManyToMany 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 reviewing the documentation, I became convinced 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 in the form of a Spring Boot application using the MapStruct library. Source code available on Github

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

@Entity public class People { @Id @GeneratedValue(strategy = GenerationType.AUTO) private long id; private String human; //      @ManyToOne(cascade = CascadeType.ALL) private RockGroups rockGroups; //     @ManyToMany(mappedBy = "songInstrumentalist",fetch = FetchType.EAGER) private List<SongPlayers> songItems; public People(){} public People(long id, String human){ this.id = id; this.human = human; } //. . . . . } 

 @Entity public class SongPlayers { @Id @GeneratedValue(strategy = GenerationType.AUTO) private long 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.

 @Repository public interface PeopleRepository extends JpaRepository<People, Long> { @Query("select h from People h where h.human=?1") List<People> searchByHuman(String human); List<People> findPeopleByHuman(String human); } 

 @Repository public interface SongPlayersRepository extends JpaRepository<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.

 public class PeopleDTO { private long id; private String human; private RockGroups rockGroups; private List<SongPlayersDTO> songPlayersList; // . . . . . } 

 public class SongPlayersDTO { private long 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) public interface PeopleMapper { PeopleMapper PEOPLE_MAPPER = Mappers.getMapper(PeopleMapper.class); @Mapping(source = "songItems", target = "songPlayersList") PeopleDTO fromPeople(People people); } 

 @Mapper/*(uses = {PeopleMapper.class})*/ public interface SongPlayersMapper { SongPlayersMapper SONG_PLAYERS_MAPPER = Mappers.getMapper(SongPlayersMapper.class); SongPlayersDTO fromSongPlayers(SongPlayers songPlayers); @InheritInverseConfiguration SongPlayers 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 give it for People).

 public interface PeopleService { List<PeopleDTO> getAllPeople(); PeopleDTO getPeopleById(long id); People addPeople(People people); void delPeople(long id); ResponseEntity<Object> updPeople(People people, long id); List<RockGroups> getByHuman(String human); List<String> getSongByHuman(String human); } 

 @Service("peopleservice") public class PeopleServiceImpl implements PeopleService { @Autowired private PeopleRepository repository; @Override public 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 apply these methods accordingly (again only for People)

 @RestController @RequestMapping("/people") public class PeopleController { @Autowired private 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 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 participants in the discussion. Waiting for your comments.

Link to the previous article .

Link to the project on Github .

References:

Source: https://habr.com/ru/post/433270/


All Articles