问题
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.
回答1:
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.
来源:https://stackoverflow.com/questions/16152539/hibernate-version-causing-database-foreign-key-constraint-failure