Spring 引导:POST 请求具有多对多关系的实体

Spring Boot: POST request to entity with ManyToMany relationship

我正在开发用于添加乐队、音乐家、乐器等的数据库

我有一个table'band'和一个table'musician'。他们有一个 ManyToMany 关系(一个乐队可以有很多音乐家,一个音乐家可以在很多乐队中),还有一个额外的 table BandMusician,它有一个 embeddedId BandMusicianId。我这样做是因为我希望乐队和音乐家之间的关系也有其他信息,比如音乐家加入乐队的年份。

@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Band {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String genre;
    private int year;

    @OneToOne(mappedBy = "band")
    private Website website;

    @OneToMany(mappedBy = "band")
    private List<Album> albuns;

    @OneToMany(mappedBy = "band")
    private List<BandMusician> musicians;

}


@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonDeserialize(using = MusicianJsonDeserializer.class)
public class Musician {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    @JsonFormat(pattern = "dd-MM-yyyy")
    @JsonProperty("DoB")
    @Column(name = "date_of_birth")
    private LocalDate DoB;

    @ManyToMany
    @JoinTable(
            name = "musician_instruments",
            joinColumns = @JoinColumn(name = "musician_id"),
            inverseJoinColumns = @JoinColumn(name = "instrument_id")
    )   
    private List<Instrument> instruments = new ArrayList<>();

    @OneToMany(mappedBy = "musician")
    private List<BandMusician> bands;

    public void addInstrument(Instrument instrument) {
        this.instruments.add(instrument);
    }

}


@Embeddable
@Data
@AllArgsConstructor
@NoArgsConstructor
public class BandMusiciansId implements Serializable{
    
    private static final long serialVersionUID = 1L;

    @Column(name = "band_id")
    private Long bandId;

    @Column(name = "musician_id")
    private Long musicianId;
}


@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class BandMusician {

    @EmbeddedId
    private BandMusiciansId id = new BandMusiciansId();

    @ManyToOne
    @MapsId("bandId")
    @JoinColumn(name = "band_id")
    private Band band;

    @ManyToOne
    @MapsId("musicianId")
    @JoinColumn(name = "musician_id")
    private Musician musician;

    private String role;
    private int joined;
}

当我收到“/musician”的 POST 请求时,我可以保存一位音乐家。我正在使用 Jackson 反序列化这样的请求:

{
    "name": "John the Ripper",
    "DoB": "03-12-1965",
    "instruments": "voice, guitar",
    "bands": "Band1, Band2"
}

有了 Jackson,我可以获得每个乐队,用 BandRepository 搜索并创建一个 BandMusician。

问题:当我收到请求时,为了创建 BandMusician,我必须创建 BandMusiciansId,为此我需要 bandId 和 MusicianId。但我现在正在创建音乐家,所以我没有 musicianId。它是在我保存音乐家时自动创建的。

MusicianJsonDeserializer class

public class MusicianJsonDeserializer extends JsonDeserializer<Musician>{

private final InstrumentRepository instrumentRepository;
private final BandRepository bandRepository;

@Autowired
public MusicianJsonDeserializer(
        InstrumentRepository instrumentRepository,
        BandRepository bandRepository
) {
    this.instrumentRepository = instrumentRepository;
    this.bandRepository = bandRepository;
}

@Override
public Musician deserialize(JsonParser p, DeserializationContext ctxt) 
        throws IOException, JacksonException {
    
    ObjectCodec codec = p.getCodec();
    JsonNode root = codec.readTree(p);
    
    Musician musician = new Musician();
    musician.setName(root.get("name").asText());
    
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy");
    musician.setDoB(LocalDate.parse(root.get("DoB").asText(), formatter));
    
    if (root.get("instruments") != null) {
        String instrumentList = root.get("instruments").asText();
        String[] instrumentArray = instrumentList.split(", ");
        List<Instrument> musicianInstrumentList = new ArrayList<>();
        
        for (String instrument : instrumentArray) {
            Instrument instrumentFound = 
                    instrumentRepository.findByName(instrument)
                    .orElseThrow(RuntimeException::new);
            // TODO custom exception
            musicianInstrumentList.add(instrumentFound);
        }
        
        musician.setInstruments(musicianInstrumentList);
    }
    
    if (root.get("bands") != null) {
        // TODO Stuck here!

我想到的是:在我的MusicianService中,保存音乐家后,我可以创建BandMusician和关系。我认为在服务层这样做是一个糟糕的选择。

编辑:为了更容易理解,我创建了一个项目,只包含这个项目的相关部分并推送到github(https://github.com/ricardorosa-dev/gettinghelp ). 同样,我想要的是能够将 POST 发送到“/musician”,这将被 MusicianJsonDeserializer 捕获,并以某种方式为请求正文中发送的每个乐队创建 BandMusicianId 和 BandMusician。

我有实体 Band 和 Musician 以及它们之间的多对多关系与关联 table BandMusician。

我想要的是在同一请求中创建实体 Musician 和关系 (BandMusician)。

据我所知,这是不可能的,因为要在协会 table(BandMusician)中创建唱片,我必须有音乐家(我在这个请求中创建) 已经创建。

我尝试了一切只是为了看看它是否可能并且无法做到。但即使有可能,这也是一种非常糟糕的做法,因为它会使 class 耦合得太紧。

明确的解决方案是使用此请求仅创建 Musician,然后发送另一个请求以创建 Band 和 Musician 之间的连接。

我还尝试通过一个请求在 BandMusician table 中创建许多条目,这也是不可能的,因为 JsonDeserializer table 似乎不接受 List<> 作为 return 类型。我试图避免提出很多请求来创建关系条目(例如,对于一个在五个乐队中的音乐家),但似乎最好让事情保持清晰和简单。

我现在根据请求保存一个音乐家-乐队关系:

{
    "musician": "Awesome musician",
    "band": "Awesome band",
    "role": "guitar",
    "joined": 2003
}