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

时间:2013-04-22 16:55:25

标签: java mysql hibernate optimistic-locking

我有两个hibernate / JPA实体

@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上有外键约束,它引用了conference_room.code列。

使用spring @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行。

但后来我尝试在Date字段上添加对Person类的乐观锁定db更新的支持,所以新的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 timestamp列中的这个新的“更新”列,插入和更新时默认值为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)
 ....

我尝试向@Version添加ConferenceRoom字段,但这不起作用。

我无法理解为什么添加@Version搞砸了。 如果我删除外键约束或新添加的@Version字段,代码将再次开始工作,没有任何例外。

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

1 个答案:

答案 0 :(得分:0)

<强>第一

  

在我的数据库模式中,我在person.conference_room_code上有外键约束,它引用了conference_room.code列。

您的FK应该引用被引用实体的PK。在本例中,您应该person.conference_room_id引用conferenceroom.id。如果您希望code成为ConferenceRoom实体的标识字段,请不要使用代理键。如果code列不是PK候选者,那么它也不是FK候选者。

<强>第二

Merge

  

将给定对象的状态复制到具有相同标识符的持久对象上。如果当前没有与会话关联的持久性实例,则将加载该实例。返回持久化实例。如果给定实例未保存,请保存副本并将其作为新的持久实例返回。给定的实例不会与会话关联。如果使用cascade =“merge”

映射关联,则此操作会级联到关联的实例

Persist

  

使瞬态实例持久化。如果使用cascade =“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); 
}

这些事情应该解决你提出的问题。