休眠@Version导致数据库外键约束失败

问题描述:

我有两个休眠/ 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字段,代码将再次开始工作,没有任何例外。

我不想删除外键约束。有没有其他解决这个问题的方法。

首先

更多在我的数据库架构我有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