I have 3 tables, Parent, Child and ParentChild which joins the first two together. When doing an insert (Parent.getChilds().add(new Child());) I get an error because, apparently, the primary key hasn’t been created and I get a constraint violation:
java.sql.BatchUpdateException: ORA-02291: integrity constraint (TU.SYS_C0072908) violated - parent key not found
If I let my test case rollback, this works fine, I don’t get a constraint violation, and all of my assertions pass. If I set my test case to not roll back I get the above error. Also, if the Parent and Child were created outside of the current transaction then everything works peachy-keen. I.e.:
Parent parent = parentDao.get(parentId);
Child child = childDao.get(childId);
parent.getChilds().add(child);
child.setParent(parent);
parentDao.save(parent);
Mappings look like this:
Parent
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.whatev">
<class name="Parent">
<id name="parentId">
<generator class="native"/>
</id>
<set name="childs" table="ParentChild" cascade="all">
<key column="parentId"/>
<many-to-many class="Child" column="childId" unique="true"/>
</set>
</class>
</hibernate-mapping>
Child
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.whatev">
<class name="Child">
<id name="childId">
<generator class="native"/>
</id>
<join table="ParentChild" inverse="true">
<key column="childId"/>
<many-to-one name="Parent" column="parentId" not-null="true"/>
</join>
</class>
</hibernate-mapping>
A Parent can have more than one Child and a Child can have only one Parent. (It has been pointed out that there should be no join table and, instead, be a PARENTID column in the CHILD table but this design decision is not mine to make.)
Tables are pretty straight forward as well:
CREATE TABLE PARENT (
PARENTID NUMBER(38) NOT NULL,
PRIMARY KEY(PARENTID)
);
CREATE TABLE CHILD (
CHILDID NUMBER(38) NOT NULL,
PRIMARY KEY(CHILDID)
);
CREATE TABLE PARENTCHILD (
PARENTID NUMBER(38) NOT NULL,
CHILDID NUMBER(38) NOT NULL,
FOREIGN KEY(PARENTID) REFERENCES PARENT(PARENTID),
FOREIGN KEY(CHILDID) REFERENCES CHILD(CHILDID),
UNIQUE (PARENTID, CHILDID)
);
The primary keys are populated by sequences through a trigger when the record is created.
What’s wrong with my setup?
The issue is with the trigger applying the sequence nextval.
By specifying
nativebut not telling hibernate about the sequence, hibernate thinks it has to come up with the id itself since Oracle doesn’t have anything to natively create unique ids.Child.childIdwill have the nextval from the sequence (because of the trigger overwriting what hibernate comes up with) andParentChild.childIdgets the value that hibernate assigns, creating a constraint issue.To handle this in a cross-database-friendly way, either drop the trigger, keep the sequence and let Oracle handle the ids:
Or drop the trigger and sequence and let hibernate assign the ids: