Hibernate @Version导致数据库外键约束失败 [英] Hibernate @Version causing database foreign key constraint failure

查看:135
本文介绍了Hibernate @Version导致数据库外键约束失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两个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>();
//适当的getter和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会议室会议室;
//相应的getter和setters
}

在引用conference_room.code栏的person.conference_room_code上有外键约束。



在弹簧 @Transactional 方法,如果我做了下面的事情:

  public ConferenceRoom getNewConferenceRoom(Person p){
ConferenceRoom r = new ConferenceRoom();
r.setCode(MyUniqueGeneratedCode);
r.getPeople()。add(p);
// sessionFactory是spring注入的成员
sessionFactory.getCurrentSession()。merge(r);
}

一切正常保存,人员行更新正确,新会议室行

但是后来我尝试在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会议室会议室;

@版本
@Column(name =updated,length = 19)
private更新日期;
//适当的getter和setters
}

这个新的'updated'列在MYSQL timestamp列中,插入和更新的默认值为current_timestamp。

现在如果执行上述方法,我会得到

 原因:com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException:
无法添加或更新子行:外键约束失败(`schema`.` (`````````````````````````````````````````

$ >

我尝试在 ConferenceRoom 中添加 @Version 字段c $ c>,但没有奏效。



我不明白为什么添加@Version会造成混乱。
如果我删除外键约束或新添加的@Version字段,代码将再次开始工作,没有任何例外。



我不想删除外键约束。是否还有其他解决方法?

解决方案

第一个:


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


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

第二

合并


复制状态将给定对象放置到具有相同标识符的持久对象上。如果当前没有与会话关联的持久实例,则会加载它。返回持久化实例。如果给定实例未保存,则保存一个副本并将其作为新的持久实例返回。给定的实例不会与会话关联。如果关联映射为cascade =merge,则此操作级联到关联的实例。

Persist


使一个瞬态实例持久化。如果关联映射为cascade =persist,则此操作级联到关联实例。







我想你已经把 merge persist 混淆了。从我提供的代码中可以看出,您正在创建一个新的 ConferenceRoom ,而不是修改现有的。因此, merge 不会做你想做的事情。尝试将您的(提供的)方法更改为以下内容:

  public ConferenceRoom getNewConferenceRoom(Person p){
ConferenceRoom r =新的ConferenceRoom();
r.setCode(MyUniqueGeneratedCode);
r.getPeople()。add(p);
// sessionFactory是spring注入的成员
sessionFactory.getCurrentSession()。persist(r);
}

这些东西可以解决您提出的问题。


I have two hibernate/JPA entities

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

More over in my database schema I have foreign key contraint on person.conference_room_code that references conference_room.code column.

With in a spring @Transactional method if I do following

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); 
}

Everything is saved correctly, the row for person is updated properly and new conference_room row is added.

But then I tried to add support for optimistic locking db updates to Person class on Date field, so new Person class

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

This new 'updated' column in MYSQL timestamp column with default value of current_timestamp on insert and updates.

Now if the above method is executed I get

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)
 ....

I tried adding a @Version field to ConferenceRoom, but that didn't work.

I can't understand why adding @Version messes things up. If I remove foreign key constraint or the newly added @Version field, code again starts working, without any exceptions.

I don't want to drop the foreign key constraint. Is there any other way around this problem.

解决方案

First:

More over in my database schema I have foreign key contraint on person.conference_room_code that references conference_room.code column.

Your FK should reference the PK of the referenced entity. In the instant case, you should have person.conference_room_id which references conferenceroom.id. If you want your code to be the identifying field for the ConferenceRoom entity, then don't use a surrogate key. If the code column isn't a PK candidate, then it is also not an FK candidate.

Second:

Merge:

Copy the state of the given object onto the persistent object with the same identifier. If there is no persistent instance currently associated with the session, it will be loaded. Return the persistent instance. If the given instance is unsaved, save a copy of and return it as a newly persistent instance. The given instance does not become associated with the session. This operation cascades to associated instances if the association is mapped with cascade="merge"

Persist:

Make a transient instance persistent. This operation cascades to associated instances if the association is mapped with cascade="persist"


I think you have confused merge with persist. From what I can tell by the provided code, you are creating a new ConferenceRoom and not modifying an existing one. Therefore, merge is not going to do what you want it to do. Try changing your (provided) method to the following:

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); 
}

These things should fix the issues you have raised.

这篇关于Hibernate @Version导致数据库外键约束失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆