Моя проблема в том, что Hibernate не сохраняет вложенные сущности, указанные в сущности.
Рассмотрим следующие сущности:
PollEntity
@Table(name = "\"poll\"")
@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@EqualsAndHashCode(of = "id")
@Builder
public class PollEntity {
@Transient
public OptionEntity addOption(OptionEntity pollOptionEntity) {
if(options == null)
options = new HashSet<>();
options.add(pollOptionEntity);
pollOptionEntity.setPoll(this);
return pollOptionEntity;
}
@Transient
public OptionEntity dropOption(OptionEntity pollOptionEntity) {
options.remove(pollOptionEntity);
pollOptionEntity.setPoll(null);
return pollOptionEntity;
}
@Id
@Column(name = "id")
private UUID id;
@Column(name = "author")
@UUIDv4
private UUID author;
@Column(name = "poll_question")
@Size(max = 1000)
private String pollQuestion;
@OneToMany(fetch = FetchType.EAGER, mappedBy = "poll", cascade = CascadeType.MERGE)
@Builder.Default
@Valid
private Set<OptionEntity> options;
}
OptionEntity
@Table(name = "\"option\"")
@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@EqualsAndHashCode(of = "id")
@Builder
public class OptionEntity {
@Id
@GeneratedValue(generator = "UUID")
@Column(name = "id")
@GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
private UUID id;
@JoinColumn(name = "poll_id")
@ManyToOne
private PollEntity poll;
@Column(name = "option")
@Size(max = 1000)
@NotNull
private String option;
}
А вот метод обслуживания:
@Transactional(rollbackFor = Throwable.class)
public void createPoll(@Valid PollEntity pollEntity) throws ValidationException {
validationService.validateOrThrow(pollEntity);
if (pollRepository.findById(pollEntity.getId()).isPresent())
throw new ValidationException("Invalid id", Map.of("id", "Poll with id (" + pollEntity.getId() + ") already exists"));
pollEntity = validationService.validateAndSave(pollRepository, pollEntity);
И соответствующий тест:
@Test
public void createPollTest() throws ValidationException {
var uuid = UUID.randomUUID();
var pollOption1 = OptionEntity.builder()
.option("Test option 1")
.build();
var pollOption2 = OptionEntity.builder()
.option("Test option 2")
.build();
var pollOption3 = OptionEntity.builder()
.option("Test option 3")
.build();
var poll = PollEntity.builder()
.id(uuid)
.pollQuestion("Test question")
.author(UUID.randomUUID())
.build();
poll.addOption(pollOption1);
poll.addOption(pollOption2);
poll.addOption(pollOption3);
pollService.createPoll(poll);
}
, который дает следующий вывод в базу данных
опрос
2e565f50-7cd4-4fc9-98cd-49e0f0964487 feae5781-ff07-4a21-9292-c11c4f1a047d Test question
опция
c786fe5d-632d-4e94-95ef-26ab2af633e7 fc712242-8e87-41d8-93f2-ff0931020a4a Test option 1
и остальные варианты остались без ответа.
I Также использовались для создания опций в отдельном методе
@Transactional(propagation= Propagation.REQUIRES_NEW, rollbackFor = Throwable.class)
public Set<OptionEntity> createOptions(@Valid Set<OptionEntity> pollOptionsEntities) throws ValidationException {
for (var pollOption : pollOptionsEntities) {
validationService.validateAndSave(pollOptionsRepository, pollOption);
}
return pollOptionsEntities;
}
, и сущности опционов создавались, но пришлось переключиться на сохранение из встроенных методов сущностей из-за ошибок с сохраняющейся сущностью опроса.
Схема базы данных выглядит следующим образом:
CREATE TABLE "poll"
(
"id" UUID PRIMARY KEY,
"author" UUID NOT NULL,
"poll_question" VARCHAR(1000) NOT NULL
)
CREATE TABLE "option"
(
"id" UUID PRIMARY KEY,
"poll_id" UUID NOT NULL REFERENCES "poll" ("id") ON DELETE CASCADE
"option" VARCHAR(1000) NOT NULL
)
Какие возможные подходы попробовать?
UPD 1
Рассмотрев различные варианты вопросы ( 1 , 2 , 3 , 4 , 5 )
I ' ты пришел p с этим добавлением к сущности, которые предполагают сохранение сущности сущностей независимо от фактического значения и все еще имеют только одну опцию в выводе. Что было сделано неправильно?
@Override
public boolean equals(Object object) {
if ( object == this ) {
return false;
}
if ( object == null || object.getClass() != getClass() ) {
return false;
}
final OptionEntity other = OptionEntity.class.cast( object );
if ( getId() == null && other.getId() == null ) {
return false;
}
else {
return false;
}
}
@Override
public int hashCode() {
final HashCodeBuilder hcb = new HashCodeBuilder( 17, 37 );
if ( id == null ) {
while (getOptions().iterator().hasNext())
hcb.append( getOptions().iterator().next() );
}
else {
hcb.append( id );
}
hcb.append( options );
return hcb.toHashCode();
}