2013-04-22 74 views
0

我有兩個休眠/ JPA實體休眠@Version導致數據庫外鍵約束失敗

@Entity 
@Table(name = "conference_room", uniqueConstraints = @UniqueConstraint(columnNames = "code")) 
class ConferenceRoom { 
    @Id 
    @GeneratedValue(strategy = IDENTITY) 
    @Column(name = "id", unique = true, nullable = false) 
    private Integer  id; 
    @Column(name = "code", unique = true, length = 20) 
    private String  code; 

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "conferenceRoom") 
    @Cascade({CascadeType.ALL}) 
    private Set<Person> people  = new HashSet<Person>(); 
    // Appropriate getters and setters 
} 

@Entity 
@Table(name = "person") 
class Person { 
    @Id 
    @GeneratedValue(strategy = IDENTITY) 
    @Column(name = "id", unique = true, nullable = false) 
    private Integer  id; 

    @ManyToOne(fetch = FetchType.LAZY) 
    @JoinColumn(name = "conference_room_code", referencedColumnName = "code") 
    private ConferenceRoom conferenceRoom ; 
    // Appropriate getters and setters 
} 

更多在我的數據庫架構我有person.conference_room_code外鍵contraint引用conference_room.code列。

隨着彈簧@Transactional方法,如果我下面

public ConferenceRoom getNewConferenceRoom(Person p) { 
    ConferenceRoom r = new ConferenceRoom(); 
    r.setCode("MyUniqueGeneratedCode"); 
    r.getPeople().add(p); 
    // sessionFactory is spring injected member 
    sessionFactory.getCurrentSession().merge(r); 
} 

一切都是正確保存,供人行正確地更新和添加新的conference_room行。

但後來我嘗試進行樂觀鎖定數據庫更新支持添加到Person類上的日期字段,所以新的Person類

@Entity 
@Table(name = "person") 
class Person { 
    @Id 
    @GeneratedValue(strategy = IDENTITY) 
    @Column(name = "id", unique = true, nullable = false) 
    private Integer   id; 

    @ManyToOne(fetch = FetchType.LAZY) 
    @JoinColumn(name = "conference_room_code", referencedColumnName = "code") 
    private ConferenceRoom conferenceRoom ; 

    @Version 
    @Column(name = "updated", length = 19) 
    private Date   updated; 
    // Appropriate getters and setters 
} 

這新的「更新」的MYSQL時間戳列與CURRENT_TIMESTAMP的默認值列插入和更新。

現在,如果在執行上面的方法,我得到

Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: 
Cannot add or update a child row: a foreign key constraint fails (`schema`.`person`, CONSTRAINT `fk_room_code` FOREIGN KEY (`conference_room_code`) REFERENCES `conference_room` (`code`) ON DELETE NO ACTION ON UPDATE NO ACTION) 
.... 

我嘗試添加一個@VersionConferenceRoom,但沒有奏效。

我不明白爲什麼添加@Version會弄亂一切。 如果我刪除外鍵約束或新添加的@Version字段,代碼將再次開始工作,沒有任何例外。

我不想刪除外鍵約束。有沒有其他解決這個問題的方法。

回答

0

首先

更多在我的數據庫架構我有person.conference_room_code外鍵contraint引用conference_room.code列。

你的FK應該引用被引用實體的PK。在本例中,您應該有person.conference_room_id,其中引用conferenceroom.id。如果您希望code成爲ConferenceRoom實體的標識字段,則不要使用代理鍵。如果code列不是PK候選人,那麼它也不是FK候選人。

Merge

複製給定對象的狀態到具有相同標識符的持久對象。如果當前沒有與會話關聯的持久實例,則會加載它。返回持久化實例。如果給定實例未保存,則保存一個副本並將其作爲新的持久實例返回。給定的實例不會與會話關聯。此操作級聯到關聯的實例,如果該協會與級聯映射=「合併」

Persist

使臨時實例持久。此操作級聯到關聯的實例,如果該協會與級聯映射=「堅持」


我想你混淆mergepersist。從我可以通過所提供的代碼告訴,你正在創建一個新ConferenceRoom,而不是修改現有的一個。因此,merge是不會做你想要它做的事情。嘗試將您的(提供的)方法更改爲以下內容:

public ConferenceRoom getNewConferenceRoom(Person p) { 
    ConferenceRoom r = new ConferenceRoom(); 
    r.setCode("MyUniqueGeneratedCode"); 
    r.getPeople().add(p); 
    // sessionFactory is spring injected member 
    sessionFactory.getCurrentSession().persist(r); 
} 

這些問題應該解決您提出的問題。

+0

邁克,它的作品!但我還是不明白,爲什麼'merge'以前能用,但除了@版本的打破了它:( – Himanshu 2013-04-23 05:02:28

+0

因爲合併預計實體已經存在。從Hibernate的角度來看,'ConferenceRoom'本來已經存在,並且'合併(。 ..)'發生了什麼,因爲你增加了一個孩子到兒童的父母的集合 - 這將堅持'(...)'子實體調用'堅持(...)'父告訴Hibernate是一個動作父母不*不*存在,並應與一組其收藏的內舉行兒童創建。 – Mike 2013-04-23 11:47:31