#java #spring-boot #many-to-many
#java #spring-boot #многие ко многим
Вопрос:
Я работаю над базой данных для добавления групп, музыкантов, инструментов и т. Д.
У меня есть таблица «группа» и таблица «музыкант». У них есть отношения ManyToMany (в одной группе может быть много музыкантов, музыкант может быть во многих группах), с дополнительной таблицей BandMusician, которая имеет встроенный идентификатор 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;
}
Когда я получаю запрос POST на «/ musician», я могу сохранить музыканта. Я использую Jackson для десериализации такого запроса:
{
"name": "John the Ripper",
"DoB": "03-12-1965",
"instruments": "voice, guitar",
"bands": "Band1, Band2"
}
С помощью Jackson я могу получить каждую группу, выполнить поиск в BandRepository и создать BandMusician.
ПРОБЛЕМА: когда я получаю запрос, чтобы создать BandMusician, я должен создать BandMusiciansId, и для этого мне нужны идентификатор группы и идентификатор музыканта. Но я создаю музыканта прямо сейчас, поэтому у меня нет musicianId. Он создается автоматически, когда я сохраняю музыканта.
Класс MusicianJsonDeserializer
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 ). Опять же, я хочу иметь возможность отправлять сообщение в «/ musician», которое будет перехвачено MusicianJsonDeserializer, и каким-то образом создавать BandMusicianId и BandMusician для каждой группы, отправленной в теле запроса.
Ответ №1:
У меня есть сущности Band и Musician и множество взаимосвязей между ними с таблицей ассоциаций BandMusician.
Я хотел создать сущность Musician и отношения (BandMusician) в одном запросе.
Насколько я могу судить, это невозможно, потому что для создания записи в таблице ассоциаций (BandMusician) мне нужно было бы, чтобы музыкант (который я создаю в этом запросе) уже был создан.
Я перепробовал все, чтобы посмотреть, возможно ли это, и не смог этого сделать. Но даже если бы это было возможно, это было бы очень плохой практикой, поскольку это сделало бы класс слишком тесно связанным.
Очевидным решением было создать только музыканта с этим запросом, а затем отправить другой запрос для создания соединения между группой и музыкантом.
Я также пытался создать много записей в таблице BandMusician одним запросом, что также было невозможно, потому что таблица JsonDeserializer, похоже, не принимает List<> в качестве возвращаемого типа. Я пытался избежать большого количества запросов на создание записей отношений (например, для музыканта, который состоит из пяти групп), но, похоже, лучше сохранить все ясным и простым.
Теперь я сохраняю одно отношение музыкант-группа для каждого запроса:
{
"musician": "Awesome musician",
"band": "Awesome band",
"role": "guitar",
"joined": 2003
}