使用复合标识符@EmbeddedId@OneToOne



我下面的例子是为了学习目的。目标是使用复合标识符创建一些示例。不幸的是,我得到了一些奇怪的结果。

实体CourseTeacherCoursePk

每门课程只能由一个Teacher授课。

我在类Teacher和类Course之间使用双向关系@OneToOne()

每个Course都包含一个复合主键。在我的示例中,我正在简化,并且我使用的是仅使用一个属性的复合主键。

我的示例的目标是在持久保存课程对象时保留关联的教师。

Teacher类:

@Entity(name = "Teacher")
public class Teacher {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "firstName")
private String firstName;
@Column(name = "lastName")
private String lastName;
@OneToOne(mappedBy = "officialTeacher")
private Course course;
//setter getter constructors are omitted for brevity
}

Course类:

@Entity(name = "Course")
public class Course {
@EmbeddedId
private CoursePk pkCourse;
@OneToOne(cascade = CascadeType.PERSIST)
private Teacher officialTeacher;
//setter getter constructors are omitted for brevity
}

应表示复合主键的CoursePk

@Embeddable
public class CoursePk implements Serializable {
@Column(name = "courseName")
private Integer courseId;
}

我的运行示例:

private void composedPrimaryKey() {
Teacher teacher = new Teacher("Jeff", "Boss");
CoursePk composedPK = new CoursePk(1);
Course course = new Course(composedPK, teacher);
Course savedCourse = courseRepository.save(course);
}

有了这个,我的表格看起来像:

课程表

course_name | official_teacher_id
1      |      1

教师表

id  | first_name |last_name
1  |   null     |  null

如您所见,教师的信息不会持久化,而只会保留 id 字段。

神奇的是,当我将级联类型更改为CascadeType.ALL时,一切都会持久化。

id  | first_name |last_name
1  |   Jeff     |  Boss

有人可以解释为什么它不适用于仅CascadeType.PERSIST.

Spring Data JPA:CrudRepository.save(…)方法行为

鉴于使用了 Spring Data JPA,关于CrudRepository.save(…)方法如何检测要执行的方法调用有一些细节:EntityManager.persist(…)EntityManager.merge(…)

5.2.1. 保存实体

可以使用CrudRepository.save(…)方法执行保存实体。它通过使用基础 JPAEntityManager来保留或合并给定实体。如果实体尚未持久化,Spring Data JPA 会通过调用entityManager.persist(…)方法保存实体。否则,它将调用entityManager.merge(…)方法。

实体状态检测策略

Spring Data JPA提供了以下策略来检测实体是否是新的:

  • Id-Property 检查(默认):默认情况下,Spring Data JPA 检查给定实体的标识符属性。如果标识符属性为 null,则假定该实体是新的。否则,假定它不是新的。

<...>

— Spring Data JPA - 参考文档。

回到问题

回到问题中提到的代码段。
由于Course实例的标识符属性(主键)不为空,因此 Spring Data JPA 将其视为现有(不是新)实体,并调用EntityManager.merge(…)方法而不是EntityManager.persist(…)方法。

其他参考资料

另外,请参考相关问题:java - JPA CascadeType Persist 不适用于 spring 数据。

我曾经遇到过这种情况。我甚至注意到更改为CascadeType.MERGE也可以工作。我搜索了一下,知道 PERSIST 假设已经创建了教师对象,并且它只是将教师 ID 分配给课程对象。PERSIST 表示只需将教师引用放在课程对象中。您还可以看到它提供了课程对象不应更新教师对象的功能。教师对象应单独更新。

更改为合并更新引用以及教师对象。 CascadeType.ALL 因为 MERGE 而工作。

相关内容

  • 没有找到相关文章

最新更新