1

我有以下两个实体:

人.java:

@Entity
@Table(name="PERSON")
public class Person {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name="id")
    private int id;

    @Column(name="first_name")
    private String firstName;

    @Column(name="last_name")
    private String lastName;

    @OneToOne(cascade = CascadeType.ALL, mappedBy = "person", optional = false)
    private Address address;

    public Person(final String firstName, final String lastName, final Address address)
    {
        this.firstName = firstName;
        this.lastName = lastName;
        this.address = address;
    }

    //Required by hibernate
    public Person()
    {
    }

    public int getId()
    {
        return id;
    }

    public String getFirstName()
    {
        return firstName;
    }

    public String getLastName()
    {
        return lastName;
    }

    public Address getAddress() {
        return address;
    }

    public void setId(final int id)
    {
        this.id = id;
    }

    public void setFirstName(final String firstName)
    {
        this.firstName = firstName;
    }

    public void setLastName(final String lastName)
    {
        this.lastName = lastName;
    }

    public void setAddress(final Address address)
    {
        this.address = address;
    }

}

地址.java:

@Entity
@Table(name = "ADDRESS")
public class Address
{
    @Id
    private int personId;

    @MapsId
    @OneToOne()
    private Person person;

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

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

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

    public Address()
    {
    }

    public Address(final String street, final String town, final String postcode)
    {
        this.street = street;
        this.town = town;
        this.postcode = postcode;
    }

    public Person getPerson()
    {
        return person;
    }

    public void setPerson(final Person person)
    {
        this.person = person;
        this.personId = person.getId();
    }

    public String getStreet()
    {
        return street;
    }

    public void setStreet(final String street)
    {
        this.street = street;
    }

    public String getTown()
    {
        return town;
    }

    public void setTown(final String town)
    {
        this.town = town;
    }

    public String getPostcode()
    {
        return postcode;
    }

    public void setPostcode(final String postcode)
    {
        this.postcode = postcode;
    }

}

我想将这两个实体映射到以下架构:

CREATE TABLE person (
    id INT NOT NULL AUTO_INCREMENT,
    first_name VARCHAR(50) NULL DEFAULT NULL,
    last_name VARCHAR(50) NULL DEFAULT NULL,
    PRIMARY KEY (id)
);
CREATE TABLE address (
    person_id INT(10) NOT NULL,
    street VARCHAR(50) NULL DEFAULT NULL,
    town VARCHAR(50) NULL DEFAULT NULL,
    postcode VARCHAR(50) NULL DEFAULT NULL,
    FOREIGN KEY (person_id) REFERENCES person(id) ON DELETE CASCADE
);

请注意,地址表没有主键,只有人员表的外键。这意味着地址不能在没有与之关联的人的情况下存在,并且映射必须是一对一的。

不幸的是,Hibernate 注释不能正常工作。这是我用来测试上述内容的代码:

public static Person save(Person person) {
    SessionFactory sf = new AnnotationConfiguration().configure().buildSessionFactory();
    Session session = sf.openSession();
    session.beginTransaction();
    Integer id = (Integer)session.save(person);
    person.setId(id);
    session.getTransaction().commit();
    session.close();
    return person;
}

@Test
public void testWritePerson() {
    Person person = new Person("Jack", "Bauer", new Address("123 Fake Street", "Springfield", "SP1F 123"));
    person.getAddress().setPerson(person);

    Person savedPerson = PersonTestUtil.save(person);
    assertFalse(
}

但我得到的错误是:

org.hibernate.id.IdentifierGenerationException: null id generated for:class Address

我尝试了各种不同注释的许多排列,但没有成功。有什么想法可以修复上述代码中的注释,以便正确保留 Person 和 Address 实体吗?如果您想自己测试它的完整代码以及如何设置数据库的说明,请参见此处:

https://github.com/alexspurling/hibernate-test

4

3 回答 3

2

我发现解决方案是从地址字段的 Person 的 OneToOne 注释中删除“可选 = false”属性:

@OneToOne(cascade = CascadeType.ALL, mappedBy = "person")
private Address address;

我相信这是可行的,因为它可以防止 Hibernate 在生成 Person 的 id 之前尝试保留地址。

请查看更新的 github 存储库以获取完整的工作示例:https ://github.com/alexspurling/hibernate-test

于 2012-11-19T13:32:01.900 回答
0

我认为 Address 对象中的 personID 字段应该反映 person 对象属性的状态。尝试对地址实体进行此更改:

public void setPerson(final Person person)
{
    this.person = person;
    this.personId = person.getId();
}

您需要确保在设置一个时另一个保持同步。

编辑
好的,我现在可以看到我的建议不起作用。基本问题是您需要使用人员 ID 设置地址 ID,但人员 ID 是在持久化时自动生成的,因此您还没有该值。Address 上的 Id 没有注释告诉 JPA 它实际上是 Person 的外键,因此它不知道使用生成的 person Id 设置它。
您需要做的是向 JPA 提供足够的信息,以便它知道保存 Person,然后使用该 Id 保存地址。我会尝试这样的事情。

@Entity
@Table(name = "ADDRESS")
public class Address
{
    //*** Use the Person object to get and set this value
    //*** @Id
    //*** private int personId;

    @Id
    @OneToOne()
    private Person person;

现在 JPA 应该有足够的注释信息来确定保存对象的顺序以及如何在 Address 上设置 personId。
最后,您在测试用例中在地址上设置 Person 的行应该移到 Person 构造函数,因为这是必不可少的步骤。如果没有这条线,JPA 将没有能够链接两者的对象引用。

public Person(final String firstName, final String lastName, final Address address)
{
     this.firstName = firstName;
     this.lastName = lastName;
     this.address = address;
     this.address.setPerson(this);
} 

我认为应该这样做。

于 2012-11-16T15:06:43.427 回答
0

尝试更改映射(其他都可以)。读这个

http://viralpatel.net/blogs/hibernate-one-to-one-mapping-tutorial-using-annotation/

人还行

地址应该这样改

public class Address {

  @Id
  @Column(name="personId", unique=true, nullable=false)
  @GeneratedValue(generator="gen")
  @GenericGenerator(name="gen", strategy="foreign"
           , parameters=@Parameter(name="property", value="person"))
  private int personId;

  @OneToOne
  @PrimaryKeyJoinColumn
  private Person person
  ...

插入新值时,我们可以显式设置两端:

于 2012-11-17T06:37:27.743 回答