Ian Dallas
Ian Dallas

Reputation: 12741

How to Stop Hibernate From Trying to Update one-to-many Over Join Table

Ok so I'm having bit of a problem with my Hibernate mappings and getting the desired behavior.

Basically what I have is the following Hibernate mapping:

<hibernate-mapping>
    <class name="com.package.Person" table="PERSON" schema="MYSCHEMA" lazy="false">
        <id name="personId" column="PERSON_ID" type="java.lang.Long">
            <generator class="sequence">
                <param name="sequence">PERSON_ID_SEQ</param>
            </generator>
        </id>

        <property name="firstName" type="string" column="FIRST_NAME">
        <property name="lastName" type="string" column="LAST_NAME">
        <property name="age" type="int" column="AGE">

        <set name="skills" table="PERSON_SKILL" cascade="all-delete-orphan">
            <key>
                <column name="PERSON_ID" precision="12" scale="0" not-null="true"/>
            </key>
            <many-to-many column="SKILL_ID" unique="true" class="com.package.Skill"/>
        </set>
    </class>
</hibernate-mapping>


<hibernate-mapping>
    <class name="com.package.Skill" table="SKILL" schema="MYSCHEMA">
        <id name="skillId" column="SKILL_ID" type="java.lang.Long">
            <generator class="sequence">
                <param name="sequence">SKILL_ID_SEQ</param>
            </generator>
        </id>
        <property name="description" type="string" column="DESCRIPTION">
    </class>
</hibernate-mapping>

So lets assume that I have already populated the Skill table with some skills in it. Now when I create a new Person I want to associate them with a set of skills that already exist in the skill table by just setting the ID of the skill. For example:

Person p = new Person();
p.setFirstName("John");
p.setLastName("Doe");
p.setAge(55);

//Skill with id=2 is already in the skill table
Skill s = new Skill()
s.setSkillId(2L);

p.setSkills(new HashSet<Skill>(Arrays.asList(s)));
PersonDao.saveOrUpdate(p);

If I try to do that however I get an error saying:

WARN (org.slf4j.impl.JCLLoggerAdapter:357) - SQL Error: 1407, SQLState: 72000
ERROR (org.slf4j.impl.JCLLoggerAdapter:454) - ORA-01407: cannot update ("MYSCHEMA"."SKILL"."DESCRIPTION") to NULL

ERROR (org.slf4j.impl.JCLLoggerAdapter:532) - Could not synchronize database state with session
org.hibernate.exception.GenericJDBCException: Could not execute JDBC batch update

The reason I am getting this error I think is because Hibernate sees that the Skill with Id 2 has 'updated' its description to null (since I never set it) and tries to update it. But I don't want Hibernate to update this. What I want it to do is insert the new Person p and insert a record into the join table, PERSON_SKILL, that matches p with the skill in the SKILL table with id=2 without touching the SKILL table.

Is there anyway to achieve this behavior?

Upvotes: 1

Views: 6074

Answers (2)

Pascal Thivent
Pascal Thivent

Reputation: 570645

This may be possible if you don't cascade all-delete-orphan which is explicitely telling hibernate to cascade the changes.

But the right way would be IMO to load load the desired Skill entity from the database and to add it to the set of skills of the Person.

Upvotes: 1

matt b
matt b

Reputation: 140061

Instead of creating the Skill object yourself:

//Skill with id=2 is already in the skill table
Skill s = new Skill()
s.setSkillId(2L);
p.setSkills(new HashSet<Skill>(Arrays.asList(s)));

You should be retrieving it from the Hibernate Session:

Skill s = (Skill) session.get(Skill.class, 2L);
p.setSkills(new HashSet<Skill>(Arrays.asList(s)));

This way the Session thinks that the skill contained in p.skills is persistent, and not transient.

Upvotes: 1

Related Questions