1

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

实体CourseTeacherCoursePk。_

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

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

每个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

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

4

2 回答 2

2

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

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

5.2.1。保存实体

可以使用该CrudRepository.save(…)方法保存实体。它使用底层 JPA 持久化或合并给定的实体EntityManager。如果实体还没有被持久化,Spring Data JPA 通过调用entityManager.persist(…)方法来保存实体。否则,它调用该entityManager.merge(…)方法。

实体状态检测策略

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

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

<…>

Spring Data JPA - 参考文档

回到问题

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

其他参考资料

此外,请参阅相关问题:java - JPA CascadeType Persist doesn't work with spring data

于 2019-08-16T03:04:41.717 回答
0

我曾经遇到过这种情况。我什至注意到更改为 CascadeType.MERGE 也有效。我对此进行了搜索,发现 PERSIST 假设 Teacher 对象已经创建,并且它只是将 teacherID 分配给 Course 对象。PERSIST 意味着只需将教师参考放在课程对象中。您还可以看到它提供了 Course 对象不应更新 Teacher 对象的功能。教师对象应单独更新。

更改为 MERGE 更新参考以及教师对象。CascadeType.ALL 因为 MERGE 而起作用。

于 2019-08-15T14:48:46.010 回答