Spring Boot solving a problem with ManyToMany



The article is addressed to you, wandering in the darkness of the night. You, whose path is illuminated only lonely fireflies. In general, a programmer who studies Spring Boot and ManyToMany's relationship to Hibernate.

He did a test task on an abstract musical theme: write a service for storing data about musicians, songs, albums using Java, Spring, Hibernate. Part of the task was to create the classes “Compositions” and “Artists”. The composition can be performed by many performers, and the performer can perform many compositions. Typical bidirectional relationship ManyToMany.

The Khasang courses told you how to avoid looping rest requests to Spring using the DTO class, but Spring Boot is another story. On Russian-language websites I saw answers like “Yes, this is all elementary,” but without specific explanations. I will give an example of solving this problem. The full code is on the githab, link below.

First create the Entity: People and SongPlayers. Getters and setters omitted for short.

@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;
//. . . . . . . . .
}

Then we create Repository interfaces for the People class.

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

And for the SongPlayers class

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

The Repository annotation extends the Component annotation , which allows the implemented class to make bean and autowiring, respectively.

Extending the JpaRepository interface allows you to perform the necessary CRUD operations without additional description and other useful things.

Now you need to create DTO classes for Entity People and SongPlayers. Here I will give only PeopleDTO so as not to clutter the article. Getters and setters dropped again.

publicclassPeopleDTO{
    privatelong id;
    private String human;
    private RockGroups rockGroups;
    private List<SongPlayersDTO> songPlayersList;
    public List<PeopleDTO> getPeopleDTOList(List<People> peopleList){
        List<PeopleDTO> peopleDTOList = new ArrayList<>();
        for (People people : peopleList){
            songPlayersList = new ArrayList<>();
            PeopleDTO peopleDTO = new PeopleDTO();
            peopleDTO.setId(people.getId());
            peopleDTO.setHuman(people.getHuman());
            peopleDTO.setRockGroups(people.getRockGroups());
            for (SongPlayers songPlayers : people.getSongItems()){
                SongPlayersDTO songPlayersDTO = new SongPlayersDTO();
                songPlayersDTO.setId(songPlayers.getId());
                songPlayersDTO.setSong(songPlayers.getSong());
                songPlayersDTO.setPoet(songPlayers.getPoet());
                songPlayersDTO.setComposer(songPlayers.getComposer());
                songPlayersDTO.setAlbum(songPlayers.getAlbum());
                songPlayersList.add(songPlayersDTO);
            }
            peopleDTO.setSongPlayersList(songPlayersList);
            peopleDTOList.add(peopleDTO);
        }
        return peopleDTOList;
    }
//. . . . . . . . .
}

By analogy, the SongPlayersDTO class is also created. Select the required fields for display in the rest - answer.

Create a controller for the people. Attention, watch your hands!

@RestController@RequestMapping("/people")
publicclassPeopleController{
    @Autowiredprivate PeopleRepository repository;
    @GetMapping("/all")
    public List<PeopleDTO> getAllPeople(){
        PeopleDTO peopleDTO = new PeopleDTO();
        return peopleDTO.getPeopleDTOList(repository.findAll());
    }
//. . . . . . .
}

Yeah, you say. This will not work. And where is the implementation of the PeopleRepository interface?
It will work, the PeopleRepository class is created on the fly! Just fantastic.

Now compare the number of generated classes and interfaces for an identical project on Spring and Spring Boot
classes in the Spring projectclasses in spring boot

I wish you all successful programming. Waiting for your comments.


Used Books:

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

Also popular now: