开发者

JPA composite key issue: Column 'Person_ID' cannot be null

this is hurdle number 43 tonight in trying to get JPA/Hibernate working in a new project.

When trying to create and then persist my Staffer class, I get:

SEVERE: Column 'Person_ID' cannot be null
SEVERE: Could not synchronize database state with session

Staffer contains a Person, an Office, and a Location, with the Person being the primary key. I'm manually creating each of theses entities, persisting them, attaching them, verifying that they are not null, that their ids are not null, and then persisting my Staffer.

I can't figure out why JPA/Hibernate is complaining about an (apparently) null person field when I try to persist:

    EntityManager em = EMF.get().createEntityManager();
    em.getTransaction().begin();
    Location location = new Location();
    location.setCity("my city");
    location = em.merge(location);
    assertNotNull(location.getId());

    Office office = new Office();
    office.setName("my office");
    office.setLocation(location);
    office = em.merge(office);
    assertNotNull(office.getId());

    Person person = new Person();
    person.setFirstName("first");
    person.setLastName("last");
    person = em.merge(person);
    assertNotNull(person.getId());

    Staffer staffer = new Staffer();
    staffe开发者_运维百科r.setPerson(person);
    staffer.setCellPhone("555-555-5555");
    staffer.setOffice(office);
    staffer.setHomeLocation(location);
    staffer.setPersonalEmail("me@home.com");

    assertNotNull(staffer.getPerson());
    assertNotNull(staffer.getPerson().getId());
    staffer = em.merge(staffer);  // CODE FAILS HERE
    em.getTransaction().commit();

Here is my model code:

@Entity
@Table(name = "Staffer")
public class Staffer implements Serializable
{
    @Id
    @OneToOne(cascade = CascadeType.PERSIST)
    @JoinColumn(name = "Person_ID")
    private Person person;

    @ManyToOne(cascade = CascadeType.PERSIST)
    @JoinColumn(name = "Office_ID")
    private Office office;

    @ManyToOne(cascade = CascadeType.PERSIST)
    @JoinColumn(name = "Home_Address_Location_ID")
    private Location homeLocation;   

    @Column(name = "Home_Phone")
    private String homePhone;

    @Column(name = "Cell_Phone")
    private String cellPhone;

    @Column(name = "Personal_Email")
    private String personalEmail;

    @Override
    public boolean equals(Object o)
    {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Staffer staffer = (Staffer) o;

        if (homeLocation != null ? !homeLocation.equals(staffer.homeLocation) : staffer.homeLocation != null)
            return false;
        if (office != null ? !office.equals(staffer.office) : staffer.office != null) return false;
        if (person != null ? !person.equals(staffer.person) : staffer.person != null) return false;

        return true;
    }

    @Override
    public int hashCode()
    {
        int result = person != null ? person.hashCode() : 0;
        result = 31 * result + (office != null ? office.hashCode() : 0);
        result = 31 * result + (homeLocation != null ? homeLocation.hashCode() : 0);
        return result;
    }

    // getters & setters, etc.
}

@Entity
@Table(name = "Person")
public class Person
{
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "Person_ID")
    private Long id;

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

    @Column(name = "Last_Name")
    private String lastName;
    // getters & setters, etc.
}

@Entity
@Table(name = "Office")
public class Office
{
    @Id
    @GeneratedValue(strategy= GenerationType.AUTO)
    @Column(name = "Office_ID")
    private Long id;

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

    @ManyToOne(cascade = CascadeType.PERSIST)
    @JoinColumn(name = "Location_ID")
    private Location location;

    // getters & setters, etc.
}

@Entity
@Table(name = "Location")
public class Location
{
    @Id
    @GeneratedValue(strategy= GenerationType.AUTO)
    @Column(name = "Location_ID")
    private Long id;

    @Column(name ="City")
    private String city;

    // getters & setters, etc.
}

Also, I'm not sure why when the table is created by Hibernate, it creates the Home_Address_Location_ID and Office fields as part of the key for Staffer; my intention is to only use the Person as primary key.

+--------------------------+--------------+------+-----+---------+-------+
| Field                    | Type         | Null | Key | Default | Extra |
+--------------------------+--------------+------+-----+---------+-------+
| Cell_Phone               | varchar(255) | YES  |     | NULL    |       | 
| Home_Phone               | varchar(255) | YES  |     | NULL    |       | 
| Personal_Email           | varchar(255) | YES  |     | NULL    |       | 
| Person_ID                | bigint(20)   | NO   | PRI | NULL    |       | 
| Home_Address_Location_ID | bigint(20)   | YES  | MUL | NULL    |       | 
| Office_ID                | bigint(20)   | YES  | MUL | NULL    |       | 
+--------------------------+--------------+------+-----+---------+-------+


It seems to me Staffer is really a subclass of Person. I would model it this way (Hibernate will be happier). Probably using a table-per-subclass setup.

Other options include a surrogate id on Staffer (ugh) or some sort of embedded ID voodoo (haven't thought that last one through).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜