无法为双向Spring Data JPA 和Spring REST 正确设置@ManyToMany 和@OneToOne 关系
Unable to setup @ManyToMany and @OneToOne relationship correctly for bi directional Spring Data JPA and Spring REST
我正在使用 Spring Boot with Spring Data JPA 和 Spring Data REST。
我有一个名为 TestExection 的 class,它是一个具有多个 TestResults 的 class 设置。创建 TestExecution 时,没有创建任何结果来为其设置。
稍后当 TestExecution 为 运行 时,将创建 TestResult 对象。
这些 TestResult 对象与 TestExecution 具有@OneToOne 关系,并且在保存 TestResult 之前,在 TestResult 上设置 TestExecution 对象并调用 save(TestResult)。
// Create RESULT object
TestResult testResult = new TestResult(someTestExection......);
save(testResult)
如果我通过 REST 调用以查看 testResults/1/testExecution,我可以看到与 testResult 关联的 testExecution。如果我对 testExecutions/1/testResults 进行相同的调用,它 returns 一个空的 [].
2 个问题:
1) 我是否需要明确地将新创建的 TestResult 对象设置到 TestExecutions 的 TestResult Set 中?
testExecution.getTestResults().add(testResult);
save(testExecution)
当我尝试这个时,它会导致附加的堆栈跟踪。
Hibernate: update test_execution set description=?, owner=?, version=? where id=? and version=?
[WARNING]
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.boot.maven.AbstractRunMojo$LaunchRunner.run(AbstractRunMojo.java:478)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.WhosebugError
at com.miw.mcb.server.model.TestExecution.hashCode(TestExecution.java:32)
at com.miw.mcb.server.model.TestResult.hashCode(TestResult.java:30)
at java.util.AbstractSet.hashCode(AbstractSet.java:126)
at org.hibernate.collection.internal.PersistentSet.hashCode(PersistentSet.java:448)
at com.miw.mcb.server.model.TestExecution.hashCode(TestExecution.java:32)
at com.miw.mcb.server.model.TestResult.hashCode(TestResult.java:30)
at java.util.AbstractSet.hashCode(AbstractSet.java:126)
at org.hibernate.collection.internal.PersistentSet.hashCode(PersistentSet.java:448)
或
2) 是否有某种方法可以设置它与关系,以便当 TestResult 设置 TestExecution 时可以将其链接并添加到其列表中?
测试执行
@Data
@Entity
@Table(name = "test_execution")
//@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class TestExecution {
private @Id @GeneratedValue Long id;
// TODO set back to lazy
@ManyToMany(fetch = FetchType.EAGER)
//@JsonBackReference
private Set<TestResult> testResults;
// TODO set back to lazy
@ManyToMany(fetch = FetchType.EAGER)
private Set<TestSuite> testSuites;
测试结果
@Data
@Entity
@Table(name = "test_result")
//@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class TestResult {
private @Id @GeneratedValue Long id;
// TODO switch back to lazy
@OneToOne(fetch = FetchType.EAGER)
//@JsonManagedReference
private TestExecution testExecution;
// TODO switch back to lazy
@OneToOne(fetch = FetchType.EAGER)
private TestSuite testSuite;
两个存储库设置相同
测试执行库
@Repository
@Transactional
public interface TestExecutionRepository extends PagingAndSortingRepository<TestExecution, Long> {
测试结果库
@Repository
@Transactional
public interface TestResultRepository extends PagingAndSortingRepository<TestResult, Long> {
AppConfig
@Configuration
@ComponentScan({ "com.miw.mcb.server.dao.repository", "com.miw.mcb.server.model" })
public class AppConfig {
}
SpringBootApp(ReactAndSpringDataRestApplication)
@SpringBootApplication
public class ReactAndSpringDataRestApplication {
public static void main(String[] args) {
SpringApplication.run(ReactAndSpringDataRestApplication.class, args);
}
}
更新:我对@ManyToOne 和@OneToMany 进行了更新,但仍然存在 TestResults
如何传播到 TestExecution
[ 的问题=27=]
感谢您对 ManyToOne 和 OneToMany 的解释。我以为我的映射不正确。
在那之后,当 运行ning:
时,我得到了以下堆栈跟踪
testExecution.getTestResults().add(testResult);
save(testExecution)
** result.setTestExecution(执行)的更新; execution.getResults().add(结果); repo.save(执行);不先保存 result
**
java.lang.WhosebugError: null
at com.miw.mcb.server.model.TestExecution.hashCode(TestExecution.java:26)
at com.miw.mcb.server.model.TestResult.hashCode(TestResult.java:35)
at java.util.AbstractSet.hashCode(AbstractSet.java:126)
at org.hibernate.collection.internal.PersistentSet.hashCode(PersistentSet.java:448)
at com.miw.mcb.server.model.TestExecution.hashCode(TestExecution.java:26)
at com.miw.mcb.server.model.TestResult.hashCode(TestResult.java:35)
at java.util.AbstractSet.hashCode(AbstractSet.java:126)
at org.hibernate.collection.internal.PersistentSet.hashCode(PersistentSet.java:448)
at com.miw.mcb.server.model.TestExecution.hashCode(TestExecution.java:26)
at com.miw.mcb.server.model.TestResult.hashCode(TestResult.java:35)
at java.util.AbstractSet.hashCode(AbstractSet.java:126)...
** 为了解决这个问题,我必须使用 Lombok 实现测试结果的 hasCode() 函数 **
@EqualsAndHashCode(exclude={"id", "testExecution", "device", "testCase"})
我认为您对 JPA 注释的选择是错误的。而且,我什至不明白这是如何工作的。
事实上,@ManyToMany
关系代表了一种关系,在这种关系中,一个实体的每个元素都可以与另一个实体的任何人相关联。 that 你需要一个中间 table。这意味着单个 TestResult
可以与许多处决相关联。
另一方面,您提供了一个 @OneToOne
映射,这只是另一种方式,仅从另一端为此对象提供一个实体实例。
你管理它的方式,看起来你想要的是 @OneToMany
,这将允许 TestExecution
控制 testResults
集合,这意味着测试执行可以有不止一个结果,但每个结果只属于一次执行。
测试执行:
@OneToMany(fetch=FetchType.EAGER, cascade = CascadeType.ALL, mappedBy="testExecution")
private Set<TestResult> testResults;
测试结果:
@ManyToOne(fetch=FetchType.EAGER)
@JoinColumn(name="id_test_result")
private TestExecution testExecution;
这样您就可以让 TextExecution 管理其 TestResult 集合(使用 CascadeType.ALL
在保存 TestExecution 对象时将更改传播到集合中)。
另请参阅:
- JPA OneToMany and ManyToOne Relationships
我正在使用 Spring Boot with Spring Data JPA 和 Spring Data REST。
我有一个名为 TestExection 的 class,它是一个具有多个 TestResults 的 class 设置。创建 TestExecution 时,没有创建任何结果来为其设置。
稍后当 TestExecution 为 运行 时,将创建 TestResult 对象。
这些 TestResult 对象与 TestExecution 具有@OneToOne 关系,并且在保存 TestResult 之前,在 TestResult 上设置 TestExecution 对象并调用 save(TestResult)。
// Create RESULT object
TestResult testResult = new TestResult(someTestExection......);
save(testResult)
如果我通过 REST 调用以查看 testResults/1/testExecution,我可以看到与 testResult 关联的 testExecution。如果我对 testExecutions/1/testResults 进行相同的调用,它 returns 一个空的 [].
2 个问题:
1) 我是否需要明确地将新创建的 TestResult 对象设置到 TestExecutions 的 TestResult Set 中?
testExecution.getTestResults().add(testResult);
save(testExecution)
当我尝试这个时,它会导致附加的堆栈跟踪。
Hibernate: update test_execution set description=?, owner=?, version=? where id=? and version=? [WARNING] java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at org.springframework.boot.maven.AbstractRunMojo$LaunchRunner.run(AbstractRunMojo.java:478) at java.lang.Thread.run(Thread.java:745) Caused by: java.lang.WhosebugError at com.miw.mcb.server.model.TestExecution.hashCode(TestExecution.java:32) at com.miw.mcb.server.model.TestResult.hashCode(TestResult.java:30) at java.util.AbstractSet.hashCode(AbstractSet.java:126) at org.hibernate.collection.internal.PersistentSet.hashCode(PersistentSet.java:448) at com.miw.mcb.server.model.TestExecution.hashCode(TestExecution.java:32) at com.miw.mcb.server.model.TestResult.hashCode(TestResult.java:30) at java.util.AbstractSet.hashCode(AbstractSet.java:126) at org.hibernate.collection.internal.PersistentSet.hashCode(PersistentSet.java:448)
或
2) 是否有某种方法可以设置它与关系,以便当 TestResult 设置 TestExecution 时可以将其链接并添加到其列表中?
测试执行
@Data
@Entity
@Table(name = "test_execution")
//@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class TestExecution {
private @Id @GeneratedValue Long id;
// TODO set back to lazy
@ManyToMany(fetch = FetchType.EAGER)
//@JsonBackReference
private Set<TestResult> testResults;
// TODO set back to lazy
@ManyToMany(fetch = FetchType.EAGER)
private Set<TestSuite> testSuites;
测试结果
@Data
@Entity
@Table(name = "test_result")
//@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class TestResult {
private @Id @GeneratedValue Long id;
// TODO switch back to lazy
@OneToOne(fetch = FetchType.EAGER)
//@JsonManagedReference
private TestExecution testExecution;
// TODO switch back to lazy
@OneToOne(fetch = FetchType.EAGER)
private TestSuite testSuite;
两个存储库设置相同
测试执行库
@Repository
@Transactional
public interface TestExecutionRepository extends PagingAndSortingRepository<TestExecution, Long> {
测试结果库
@Repository
@Transactional
public interface TestResultRepository extends PagingAndSortingRepository<TestResult, Long> {
AppConfig
@Configuration
@ComponentScan({ "com.miw.mcb.server.dao.repository", "com.miw.mcb.server.model" })
public class AppConfig {
}
SpringBootApp(ReactAndSpringDataRestApplication)
@SpringBootApplication
public class ReactAndSpringDataRestApplication {
public static void main(String[] args) {
SpringApplication.run(ReactAndSpringDataRestApplication.class, args);
}
}
更新:我对@ManyToOne 和@OneToMany 进行了更新,但仍然存在 TestResults
如何传播到 TestExecution
[ 的问题=27=]
感谢您对 ManyToOne 和 OneToMany 的解释。我以为我的映射不正确。
在那之后,当 运行ning:
时,我得到了以下堆栈跟踪 testExecution.getTestResults().add(testResult);
save(testExecution)
** result.setTestExecution(执行)的更新; execution.getResults().add(结果); repo.save(执行);不先保存 result
**
java.lang.WhosebugError: null
at com.miw.mcb.server.model.TestExecution.hashCode(TestExecution.java:26)
at com.miw.mcb.server.model.TestResult.hashCode(TestResult.java:35)
at java.util.AbstractSet.hashCode(AbstractSet.java:126)
at org.hibernate.collection.internal.PersistentSet.hashCode(PersistentSet.java:448)
at com.miw.mcb.server.model.TestExecution.hashCode(TestExecution.java:26)
at com.miw.mcb.server.model.TestResult.hashCode(TestResult.java:35)
at java.util.AbstractSet.hashCode(AbstractSet.java:126)
at org.hibernate.collection.internal.PersistentSet.hashCode(PersistentSet.java:448)
at com.miw.mcb.server.model.TestExecution.hashCode(TestExecution.java:26)
at com.miw.mcb.server.model.TestResult.hashCode(TestResult.java:35)
at java.util.AbstractSet.hashCode(AbstractSet.java:126)...
** 为了解决这个问题,我必须使用 Lombok 实现测试结果的 hasCode() 函数 **
@EqualsAndHashCode(exclude={"id", "testExecution", "device", "testCase"})
我认为您对 JPA 注释的选择是错误的。而且,我什至不明白这是如何工作的。
事实上,@ManyToMany
关系代表了一种关系,在这种关系中,一个实体的每个元素都可以与另一个实体的任何人相关联。 that 你需要一个中间 table。这意味着单个 TestResult
可以与许多处决相关联。
另一方面,您提供了一个 @OneToOne
映射,这只是另一种方式,仅从另一端为此对象提供一个实体实例。
你管理它的方式,看起来你想要的是 @OneToMany
,这将允许 TestExecution
控制 testResults
集合,这意味着测试执行可以有不止一个结果,但每个结果只属于一次执行。
测试执行:
@OneToMany(fetch=FetchType.EAGER, cascade = CascadeType.ALL, mappedBy="testExecution")
private Set<TestResult> testResults;
测试结果:
@ManyToOne(fetch=FetchType.EAGER)
@JoinColumn(name="id_test_result")
private TestExecution testExecution;
这样您就可以让 TextExecution 管理其 TestResult 集合(使用 CascadeType.ALL
在保存 TestExecution 对象时将更改传播到集合中)。
另请参阅:
- JPA OneToMany and ManyToOne Relationships