Java 使用复合标识符@EmbeddedId和@OneToOne

Java 使用复合标识符@EmbeddedId和@OneToOne,java,hibernate,spring-data-jpa,hibernate-cascade,Java,Hibernate,Spring Data Jpa,Hibernate Cascade,我下面的例子是为了学习。目标是使用创建一些示例。不幸的是,我得到了一些奇怪的结果 实体:课程,教师,课程知识 每门课程只能由一名教师讲授 我在班级老师和班级课程之间使用双向关系@OneToOne() 每个课程都包含一个复合主键。在我的示例中,我正在简化并使用只使用一个属性的复合主键 我的示例的目标是在课程对象被持久化时持久化关联的教师 id | first_name |last_name 1 | Jeff | Boss 老师班级: @Entity(name = "Teach

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

实体
课程
教师
课程知识

每门课程只能由一名
教师讲授

我在班级
老师
和班级
课程
之间使用双向关系
@OneToOne()

每个
课程都包含一个复合主键。在我的示例中,我正在简化并使用只使用一个属性的复合主键

我的示例的目标是在课程对象被持久化时持久化关联的教师

id  | first_name |last_name
 1  |   Jeff     |  Boss
老师
班级:

@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
}
课程
课程:

@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
教师表

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

有人能解释一下为什么它不能只使用级联类型。PERSIST

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

更改为合并更新引用和教师对象。 CascadeType.ALL由于合并而起作用。

Spring数据JPA:
crudepository.save(…)
方法行为 考虑到使用了Spring数据JPA,有一些关于
crudepository.save(…)
方法如何检测要执行的方法调用的细节:要么
EntityManager.persist(…)
要么
EntityManager.merge(…)

5.2.1. 拯救实体 可以使用
crudepository.save(…)
方法保存实体。它使用底层JPA
EntityManager
持久化或合并给定实体。如果实体尚未持久化,Spring Data JPA将通过调用
entityManager.persist(…)
方法保存实体。否则,它将调用
entityManager.merge(…)
方法

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

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

-

回到问题上来 回到问题中提到的代码块。
由于
课程
实例的标识符属性(主键)不为null,Spring Data JPA将其视为一个现有(非新)实体,并调用
EntityManager.merge(…)
方法,而不是
EntityManager.persist(…)
方法

其他参考资料
此外,请参考相关问题:。

谢谢您的明确回答。关于将Spring数据Jpa与hibernate结合使用,我面临一些困惑。是否必须显式使用Entitmanager?或者spring数据存储库就足够了?如果您有一些关于两者使用的良好实践的参考资料,这将非常有帮助。>是否必须明确使用Entitmanager?或者spring数据存储库就足够了?大致上,Spring数据JPA存储库比JPA实体管理器更高级抽象。同时,如果可能的话,请考虑使用更高级的抽象——Spring数据JPA存储库。请参考以下问题:。@zakzak,顺便问一下,您是否确定
教师
可能只有一门
课程
@OneToOne(mappedBy=“officialTeacher”)私人课程?在我看来,一个
老师可能有很多
课程(
0..*
)。我只是在尝试这两个实体之间可能存在的不同联系。在另一个例子中,我有一个老师可以教授很多课程。。。。。。