@PrimaryKeyJoinColumn未选择共享密钥

时间:2018-11-30 20:49:16

标签: java spring hibernate shared-primary-key

我之前使用过@PrimaryKeyJoinColumn,现在我正在尝试使用Spring Boot,但我无法弄清丢失的内容,这很奇怪,因为我似乎已正确完成了所有事情:

人员班级:

@MockBean

部门课程:

    @Table(name = "PERSON")
@Entity

@Getter
@Setter
public class Person {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID")
    private Long id;


    @Column(name = "NAME")
    private String name;

    @OneToOne(cascade = CascadeType.PERSIST)
    @PrimaryKeyJoinColumn
    private Department department;
}

服务类别:

    @Table(name = "DEPARTMENT")
@Entity
@Getter
@Setter
public class Department {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID")
    private Long id;

    @Column(name = "NAME")
    private String name;


    @OneToOne(mappedBy = "department")
    private Person person;
}

这就是我在数据库中得到的: 人员表:

   @Transactional
    public void addPerson() {
        Person person = new Person();
        person.setName("person 1");
        Department department = new Department();
        department.setName("test");
        department.setPerson(person);
        person.setDepartment(department);
        personRepository.save(person);
    }

部门表:

ID   Name
13  person 1

所需的输出:两个ID应该相同(Person ID应该是18而不是13) 有什么主意吗?


更新: 休眠总是尝试插入不带ID的人员,因此,如果我从人员ID中删除自动递增,并尝试插入具有现有部门的人员,我会得到:

ID  Name
18  test

更新2: 似乎@PrimaryKeyJoinColumn不会像部门类那样处理ID生成,因此我需要使用generator。我想知道,因为相同的注释在继承中起作用,而对子类的ID不做任何事情。因此,我期望得到一个答案,解释为什么ID生成在继承中起作用,而join却需要OneToOne生成器

1 个答案:

答案 0 :(得分:1)

尽管将整个部门仅任命为一个人(部门通常将许多人组成)至少是不好的命名,但我认为您确实在寻找具有共享主键的OneToOne关系。

最好的解决方案(最佳内存,速度和维护)是使用@MapsId

@Getter
@Setter
@Entity
public class Person {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToOne(mappedBy = "person", fetch = FetchType.LAZY)
    private Department department;
}

@Getter
@Setter
@Entity
public class Department {
    @Id // Note no generation
    private Long id;

    private String name;

    @OneToOne(fetch = FetchType.LAZY)
    @MapsId
    @JoinColumn(name = "id", foreignKey = @ForeignKey(name = "department_belongs_to_person"))
    private Person person;
}

部门在这里拥有关系(拥有通常意味着它将在数据库中包含带有FK的列,但在这里它们只是共享主键),并且拥有将其PK绑定到Person生成的PK的FK。

请注意,关系是双向的OneToOne,共享PK为FK。这被认为是最佳实践,但有一个缺点,就是必须编写一个getter。 :)

来源:https://vladmihalcea.com/the-best-way-to-map-a-onetoone-relationship-with-jpa-and-hibernate/

也-我强烈建议您花几天时间阅读本网站上的帖子,甚至在设计比几张桌子更大的东西之前,先不要实施它们。 :)

EDIT

我在这里可能是错的(我现在确定-请看注释),但是据我所知,保持ID相等并不是JPA指定的。它可以指定的基本上是:

“嘿,你们(人,部门)是兄弟(OneToOne),并且都知道(与Person实体中的mappingBy =“ person”双向)。您将是年长的兄弟,它将生成ID(人的@GeneratedValue) ,那么您应该拥有相同的名称。您将使用这些字段(ID,一个生成,另一个不生成)来连接(@PrimaryKeyJoinColumn)。“

我想说的是,仅仅因为您说“这连接了您”,并不意味着它们是同步的-您必须确保它。

现在如何确保它-@MapsId被认为是最好的。

如果您正在寻找不同的方法,则还可以通过#setDepartment(Department)手动将ID设置为与其他ID相同,在该方法中,您将Department的ID设置为与呼叫Person相同(但这仅在该Person已具有生成了ID,这基本上打破了主意。

我所知道的另一种方法是使用外国生成器策略。

人员:

@Id
@GeneratedValue
private long id;

@OneToOne(mappedBy="person")
private Department department;

部门:

@Id
@GeneratedValue(generator="gen")
@GenericGenerator(name="gen", strategy="foreign", parameters=@Parameter(name="property", value="person"))
private long id;

@OneToOne
@PrimaryKeyJoinColumn
private Person person;

现在-此使用特定于休眠的注释,而不是纯JPA。