2011-02-11 3 views
2

これは、新しいプロジェクトでJPA/Hibernateを使用しようとすると、今夜43番のハードルです。作成し、私のStafferクラスを永続化しようとするとJPAの複合キーの問題:列 'Person_ID'をnullにすることはできません

、私が取得:

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

StafferPersonが主キーであることと、PersonOffice、およびLocationが含まれています。私はこれらのエンティティを手動で作成し、永続化し、それらを添付し、ヌルでないこと、IDがヌルでないことを確認してから、Stafferを永続させています。

私が持続しようとすると、JPA/Hibernateは(明らかに)ヌル人物フィールド文句を言っている理由私は理解することはできません。ここで

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(); 
    staffer.setPerson(person); 
    staffer.setCellPhone("555-555-5555"); 
    staffer.setOffice(office); 
    staffer.setHomeLocation(location); 
    staffer.setPersonalEmail("[email protected]"); 

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

は私のモデルコードです:

@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. 
} 

また、テーブルがHibernateによって作成されたときに、なぜStaffのキーの一部としてHome_Address_Location_IDフィールドとOfficeフィールドを作成するのかわかりません。私の意図は主キーとしてだけ人を使用することです。

+--------------------------+--------------+------+-----+---------+-------+ 
| 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 |  | 
+--------------------------+--------------+------+-----+---------+-------+ 
+0

おそらく、Hibernateのバグです。http://stackoverflow.com/questions/4027623/how-do-i-properly-cascade-save-a-one-to-one-bidirectional-relationship-on-primarを参照してください。 – axtavt

+0

axtavt-おそらく、私の場合、関係は双方向ではありません。 –

答えて

0

本当にPersonのサブクラスであるStaffer私には思えます。私はそれをこのようにモデル化します(Hibernateはもっと幸せになるでしょう)。おそらくテーブルごとにサブクラスの設定を使用しています。

その他のオプションには、代理IDがStaffer(ugh)または埋め込みIDのブードー(最後のものを考えていない)が含まれています。

+0

ええ、Stafferは一種のPersonだと思われます。これは、私がクライアントからSQLを与えられ、クライアントが望むSQLを生成するためにできるだけ近づくようなモデルクラスを作成しようとしていることから、一種の悩ましいプロセスです。サロゲートID?私はそれを見なければならないでしょう。ありがとう。 –

+0

@CaffeineComaサロゲートIDは、何らかの理由で自然キーを主キーとして使用したくないため、エンティティに割り当てる "偽の" IDです。この場合、Stafferテーブルに追加のID列が必要になります。 Person - Stafferは常に1対1の関係(すべてのスタッフが人であるため、なぜ新しい識別子が必要なのでしょうか?)であるので、完全に無駄なIDを導入するので、最適な解決策ではないと思います。しかし、データベースを変更することができれば回避策になります。 – wds

関連する問題