Java JPA错误地保存实体
我正在使用Hibernate和DTO保存实体,这些实体是Java JPA错误地保存实体,java,spring,hibernate,jpa,Java,Spring,Hibernate,Jpa,我正在使用Hibernate和DTO保存实体,这些实体是Test(tests),TestQuestion(Test\u questions)和TestAnswer(Test\u answer) 我使用将DTO转换为Hibernate实体。我的实体保存错误: 1) 第一个问题是在映射完成后,在测试实体中设置User对象。JPA在测试表中创建2个实体。一个有用户id,一个没有用户id。Test类中的Question列表正确保存在Test\u questions表中,并引用用户id为空的测试id 2)
Test(tests)
,TestQuestion(Test\u questions)
和TestAnswer(Test\u answer)
我使用将DTO转换为Hibernate实体。我的实体保存错误:
1) 第一个问题是在映射完成后,在测试实体中设置User
对象。JPA在测试
表中创建2个实体。一个有用户id,一个没有用户id。Test
类中的Question
列表正确保存在Test\u questions
表中,并引用用户id为空的测试id
2) 第二个问题是Question
类中的Answer
列表根本没有保存在test\u answers
表中
表:
SQL表:
CREATE TABLE tests (
id UUID DEFAULT uuid_generate_v4 () PRIMARY KEY,
therapist_id UUID REFERENCES users (id) ON DELETE CASCADE ON UPDATE CASCADE,
description TEXT DEFAULT NULL,
level INTEGER NOT NULL,
active BOOLEAN DEFAULT FALSE,
date_time_created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
date_time_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE test_questions (
id UUID DEFAULT uuid_generate_v4 () PRIMARY KEY,
test_id UUID NOT NULL REFERENCES tests (id) ON DELETE CASCADE ON UPDATE CASCADE,
type TEST_TYPE_ENUM NOT NULL,
question TEXT NOT NULL,
audio TEXT DEFAULT NULL,
description TEXT DEFAULT NULL,
img TEXT NOT NULL,
cloud_id TEXT NOT NULL,
date_time_created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
date_time_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE test_answers (
id UUID DEFAULT uuid_generate_v4 () PRIMARY KEY,
question_id UUID NOT NULL REFERENCES test_questions (id) ON DELETE CASCADE ON UPDATE CASCADE,
answer TEXT NOT NULL,
audio TEXT DEFAULT NULL,
img TEXT DEFAULT NULL,
cloud_id TEXT NOT NULL,
date_time_created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
date_time_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
测试等级:
@Entity
@Table(name = "tests")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Test implements Serializable {
private static final long serialVersionUID = -2184376232517605961L;
@Id
@GeneratedValue(generator = "uuid2", strategy = GenerationType.SEQUENCE)
@GenericGenerator(name = "uuid2", strategy = "uuid2")
@Type(type = "pg-uuid")
private UUID id;
private String description;
private Integer level = 0;
private Boolean active = false;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "date_time_created")
@JsonIgnore
private Date dateTimeCreated = new Date();
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "date_time_updated")
@JsonIgnore
private Date dateTimeUpdated = new Date();
@ManyToOne(fetch = FetchType.LAZY)
@JsonBackReference
@JoinColumn(name = "therapist_id")
private User user;
@OneToMany(mappedBy = "test", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private Set<TestQuestion> questions = new HashSet<>();
// getters-setters
@Override
public String toString() {
return "Test{" +
"id=" + id +
", description='" + description + '\'' +
", level=" + level +
", active=" + active +
", dateTimeCreated=" + dateTimeCreated +
", dateTimeUpdated=" + dateTimeUpdated +
", user=" + user.getId() +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Test test = (Test) o;
return Objects.equal(id, test.id) &&
Objects.equal(description, test.description) &&
Objects.equal(level, test.level) &&
Objects.equal(active, test.active) &&
Objects.equal(dateTimeCreated, test.dateTimeCreated) &&
Objects.equal(dateTimeUpdated, test.dateTimeUpdated) &&
Objects.equal(user, test.user) &&
Objects.equal(questions, test.questions);
}
@Override
public int hashCode() {
return Objects.hashCode(id, description, level, active, dateTimeCreated, dateTimeUpdated, user, questions);
}
}
服务类别:
@Component("testService")
@Transactional
public class TestService extends Helper {
private static final Logger log = LoggerFactory.getLogger(TestService.class);
private final TestRepository testRepository;
private final UserService userService;
private final ModelMapper modelMapper;
public TestService(TestRepository testRepository, UserService userService, ModelMapper modelMapper) {
this.testRepository = testRepository;
this.userService = userService;
this.modelMapper = modelMapper;
}
public Test createTest(TestDTO testDTO) {
User teacher = userService.findById(getLoggedUserId());
Test test = toTest(testDTO, modelMapper);
test.setUser(teacher);
test = testRepository.saveAndFlush(test);
return test;
}
private Test toTest(TestDTO testDTO, ModelMapper modelMapper) {
Test test = new Test();
Set<TestQuestion> testQuestions = new LinkedHashSet<>();
TestValidity.validate(testDTO);
testDTO.getQuestions().forEach(q -> {
TestQuestion question = toQuestion(q, modelMapper);
Set<TestAnswer> answers = toAnswerSet(q.getAnswers(), modelMapper);
question.setAnswers(answers);
testQuestions.add(question);
});
test.setQuestions(testQuestions);
return test;
}
private TestQuestion toQuestion(TestQuestionDTO questionDTO, ModelMapper modelMapper) {
return modelMapper.map(questionDTO, TestQuestion.class);
}
private Set<TestAnswer> toAnswerSet(Set<TestAnswerDTO> answerDTOSet, ModelMapper modelMapper) {
Set<TestAnswer> answers = new HashSet<>();
answerDTOSet.forEach(a -> {
TestAnswer answer = modelMapper.map(a, TestAnswer.class);
answers.add(answer);
});
return answers;
}
@组件(“测试服务”)
@交易的
公共类TestService扩展了Helper{
私有静态最终记录器log=LoggerFactory.getLogger(TestService.class);
私有最终测试库测试库;
专用最终用户服务用户服务;
私人最终模型映射器模型映射器;
公共TestService(TestRepository TestRepository、UserService UserService、ModelMapper ModelMapper){
this.testRepository=testRepository;
this.userService=userService;
this.modelMapper=modelMapper;
}
公共测试createTest(TestDTO TestDTO){
User teacher=userService.findById(getLoggedUserId());
Test=toTest(testDTO,modelMapper);
test.setUser(教师);
test=testRepository.saveAndFlush(测试);
回归试验;
}
私有测试toTest(TestDTO TestDTO,ModelMapper ModelMapper){
测试=新测试();
Set testQuestions=newlinkedhashset();
TestValidity.validate(testDTO);
testDTO.getQuestions().forEach(q->{
TestQuestion=toQuestion(q,modelMapper);
Set answers=toAnswerSet(q.getAnswers(),modelMapper);
问题.答案(答案);;
添加(问题);
});
测试。设置问题(测试问题);
回归试验;
}
私有TestQuestion to Question(TestQuestiondToQuestiondTo,ModelMapper ModelMapper){
返回modelMapper.map(questionDTO,TestQuestion.class);
}
专用集到应答集(Set answerDTOSet,ModelMapper ModelMapper){
Set answers=new HashSet();
forEach回答问题(a->{
TestAnswer answer=modelMapper.map(a,TestAnswer.class);
答案。添加(答案);
});
返回答案;
}
有什么我遗漏了吗?我不确定这些问题是否是因为“ModelMapper”,因为这是我第一次使用它。我如何才能正确保存我的实体?看起来您在关联的错误一侧声明了
级联。从Hibernate文档:
根据定义,@OneToMany关联是父关联,即使它是单向或双向关联。只有关联的父端才有意义将其实体状态转换级联到子级
我相信这就是您的第二个问题的原因,因为您在子实体TestAnswer
上声明了级联,而不是在父实体TestQuestion
上声明级联。当您创建TestAnswer
时,父实体TestQuestion
不知道它需要持久化其子实体
第一个问题也可能是由于Test
和TestQuestion
两侧都声明了级联(都在指向Test
的@manytone
上,以及指向TestQuestion
的OneToMany
上),因为这可能会导致在调用saveAndFlush()
时创建一次tests
,并且在创建TestQuestion
时再次创建tests
,并看到它需要级联持久化其父实体Test
另一个问题是在保存时没有同步关联的双方。根据Hibernate开发人员之一:
但是,我们仍然需要让双方同步,否则,我们会破坏域模型关系的一致性,除非双方都正确同步,否则实体状态转换不能保证工作
换句话说,您需要按照以下思路做一些事情:
test.setUser(teacher);
teacher.getTests().add(test);
同样地,对于测试问题
:
testQuestions.add(question);
question.setTest(test);
对于TestAnswer
,也有类似的情况
链接的Vlad Mihalcea博客通过添加一个addTestAnswer()展示了一种更好的方法
方法直接指向TestQuestion
实体。谢谢您的回答。如果我理解正确,我需要将cascade设置为parent-side,这意味着我应该将cascade设置为@OneToMany
,而不是@ManyToOne
。这导致了TransientPropertyValueException
:对象引用未保存的瞬态实例-在刷新之前保存瞬态实例:com.project.model.TestQuestion.test->com.project.model.test;嵌套异常为java.lang.IllegalStateException
。或者如果我设置了cascade(CascadeType.ALL
)若要@ManyToOne
s并从@OneToMany
中删除,测试将记录到测试
表中,但测试问题
和测试答案
表为空。您理解正确,cascade应该位于@OneToMany
上。当您将其移动到@ManyToOne
时,您会遇到同样的情况在你的第二期文章中提到。我现在还不清楚你为什么会出现这个错误;父端层叠是应该防止的(hibernate应该先保留父级,然后层叠子级)。我假设移动cascade=…
是唯一被改变的事情?是的,
test.setUser(teacher);
teacher.getTests().add(test);
testQuestions.add(question);
question.setTest(test);