Tell me more ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

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.

share|improve this question

1 Answer

up vote 0 down vote accepted

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.

share|improve this answer
Mike, it works! But I still do not understand why merge worked earlier but addition of @Version broke it :( – Himanshu yesterday
Because Merge expects the entity to already exist. From Hibernate's perspective, the ConferenceRoom was supposed to already exist and the merge(...) was happening because you added a child into the parent's collection of children - an action which would persist(...) the child entity. Calling persist(...) on the parent tells Hibernate that the parent does not exist, and should be created with the set of children held inside of its collection. – Mike yesterday

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.