Hibernate version:
3.0.5
Mapping documents:
Company.hbm.xml:
Code:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class table="companies" name="test.Company">
<id unsaved-value="null" name="id">
<generator class="native"/>
</id>
<property name="name"/>
<set inverse="true" name="employees">
<key column="employerId"/>
<one-to-many class="test.Person"/>
</set>
<set inverse="true" table="formerEmployments" name="formerEmployees">
<key column="companyId"/>
<many-to-many class="test.Employment" column="employmentId"/>
</set>
</class>
</hibernate-mapping>
Person.hbm.xml:
Code:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class table="companies" name="test.Company">
<id unsaved-value="null" name="id">
<generator class="native"/>
</id>
<property name="name"/>
<set inverse="true" name="employees">
<key column="employerId"/>
<one-to-many class="test.Person"/>
</set>
<set inverse="true" table="formerEmployments" name="formerEmployees">
<key column="companyId"/>
<many-to-many class="test.Employment" column="employmentId"/>
</set>
</class>
</hibernate-mapping>
Employment.hbm.xml:
Code:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class table="employments" name="test.Employment">
<id unsaved-value="null" name="id">
<generator class="native"/>
</id>
<property name="startDate"/>
<property name="endDate"/>
</class>
</hibernate-mapping>
schema.ddl:
Code:
alter table formerEmployments drop foreign key FK423588D65DE790EF;
alter table formerEmployments drop foreign key FK423588D67C86A98C;
alter table formerEmployments drop foreign key FK423588D618D99559;
alter table people drop foreign key FKC4E2328F7713A817;
drop table if exists companies;
drop table if exists employments;
drop table if exists formerEmployments;
drop table if exists people;
create table companies (
id bigint not null auto_increment,
name varchar(255),
primary key (id)
);
create table employments (
id bigint not null auto_increment,
startDate datetime,
endDate datetime,
primary key (id)
);
create table formerEmployments (
companyId bigint not null,
employmentId bigint not null,
contactId bigint not null,
primary key (contactId, employmentId)
);
create table people (
id bigint not null auto_increment,
name varchar(255),
employerId bigint,
startDate datetime,
primary key (id)
);
alter table formerEmployments
add index FK423588D65DE790EF (employmentId),
add constraint FK423588D65DE790EF
foreign key (employmentId)
references employments (id);
alter table formerEmployments
add index FK423588D67C86A98C (contactId),
add constraint FK423588D67C86A98C
foreign key (contactId)
references people (id);
alter table formerEmployments
add index FK423588D618D99559 (companyId),
add constraint FK423588D618D99559
foreign key (companyId)
references companies (id);
alter table people
add index FKC4E2328F7713A817 (employerId),
add constraint FKC4E2328F7713A817
foreign key (employerId)
references companies (id);
Code between sessionFactory.openSession() and session.close():Code example can be found at
http://www.vtype.com/bbchops/test.zip which also includes the complete debug output of the test program execution
Full stack trace of any exception that occurs:Code:
[java] 6547 [main] WARN org.hibernate.util.JDBCExceptionReporter - SQL Error: 1216, SQLState: 23000
[java] 6547 [main] ERROR org.hibernate.util.JDBCExceptionReporter - Cannot add or update a child row: a foreign key constraint fails
[java] 6547 [main] ERROR org.hibernate.event.def.AbstractFlushingEventListener - Could not synchronize database state with session
[java] org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
[java] org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
[java] at org.hibernate.exception.ErrorCodeConverter.convert(ErrorCodeConverter.java:74)
[java] at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
[java] at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:181)
[java] at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:226)
[java] at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:140)
[java] at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:274)
[java] at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
[java] at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:730)
[java] at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:324)
[java] at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:86)
[java] at test.Tester.editData(Tester.java:50)
[java] at test.Tester.test(Tester.java:31)
[java] at test.Tester.main(Tester.java:24)
[java] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[java] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
[java] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
[java] at java.lang.reflect.Method.invoke(Method.java:324)
[java] at org.apache.tools.ant.taskdefs.ExecuteJava.run(ExecuteJava.java:193)
[java] at org.apache.tools.ant.taskdefs.ExecuteJava.execute(ExecuteJava.java:130)
[java] at org.apache.tools.ant.taskdefs.Java.run(Java.java:705)
[java] at org.apache.tools.ant.taskdefs.Java.executeJava(Java.java:177)
[java] at org.apache.tools.ant.taskdefs.Java.execute(Java.java:83)
[java] at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:275)
[java] at org.apache.tools.ant.Task.perform(Task.java:364)
[java] at org.apache.tools.ant.Target.execute(Target.java:341)
[java] at org.apache.tools.ant.Target.performTasks(Target.java:369)
[java] at org.apache.tools.ant.Project.executeTarget(Project.java:1214)
[java] at org.apache.tools.ant.Project.executeTargets(Project.java:1062)
[java] at org.apache.tools.ant.Main.runBuild(Main.java:673)
[java] at org.apache.tools.ant.Main.startAnt(Main.java:188)
[java] at org.apache.tools.ant.launch.Launcher.run(Launcher.java:196)
[java] at org.apache.tools.ant.launch.Launcher.main(Launcher.java:55)
[java] Caused by: java.sql.BatchUpdateException: Cannot add or update a child row: a foreign key constraint fails
[java] at com.mysql.jdbc.ServerPreparedStatement.executeBatch(ServerPreparedStatement.java:822)
[java] at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:57)
[java] at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:174)
[java] at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:226)
[java] at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:140)
[java] at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:274)
[java] at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
[java] at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:730)
[java] at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:324)
[java] at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:86)
[java] at test.Tester.editData(Tester.java:50)
[java] at test.Tester.test(Tester.java:31)
[java] at test.Tester.main(Tester.java:24)
[java] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[java] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
[java] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
[java] at java.lang.reflect.Method.invoke(Method.java:324)
[java] at org.apache.tools.ant.taskdefs.ExecuteJava.run(ExecuteJava.java:193)
[java] at org.apache.tools.ant.taskdefs.ExecuteJava.execute(ExecuteJava.java:130)
[java] at org.apache.tools.ant.taskdefs.Java.run(Java.java:705)
[java] at org.apache.tools.ant.taskdefs.Java.executeJava(Java.java:177)
[java] at org.apache.tools.ant.taskdefs.Java.execute(Java.java:83)
[java] at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:275)
[java] at org.apache.tools.ant.Task.perform(Task.java:364)
[java] at org.apache.tools.ant.Target.execute(Target.java:341)
[java] at org.apache.tools.ant.Target.performTasks(Target.java:369)
[java] at org.apache.tools.ant.Project.executeTarget(Project.java:1214)
[java] at org.apache.tools.ant.Project.executeTargets(Project.java:1062)
[java] at org.apache.tools.ant.Main.runBuild(Main.java:673)
[java] at org.apache.tools.ant.Main.startAnt(Main.java:188)
[java] at org.apache.tools.ant.launch.Launcher.run(Launcher.java:196)
[java] at org.apache.tools.ant.launch.Launcher.main(Launcher.java:55)
Name and version of the database you are using:MySQL 4.1.12-standard
The generated SQL (show_sql=true):Code:
[java] Hibernate: insert into formerEmployments (contactId, employmentId) values (?, ?)
Debug level Hibernate log excerpt:Code:
[java] 6406 [main] DEBUG org.hibernate.impl.SessionImpl - automatically flushing session
[java] 6406 [main] DEBUG org.hibernate.event.def.AbstractFlushingEventListener - flushing session
[java] 6406 [main] DEBUG org.hibernate.event.def.AbstractFlushingEventListener - processing flush-time cascades
[java] 6406 [main] DEBUG org.hibernate.engine.Cascades - processing cascade ACTION_SAVE_UPDATE for: test.Person
[java] 6406 [main] DEBUG org.hibernate.engine.Cascades - cascade ACTION_SAVE_UPDATE for collection: test.Person.formerEmployers
[java] 6406 [main] DEBUG org.hibernate.engine.Cascades - cascading to saveOrUpdate: test.Employment
[java] 6406 [main] DEBUG org.hibernate.event.def.AbstractSaveEventListener - transient instance of: test.Employment
[java] 6406 [main] DEBUG org.hibernate.event.def.DefaultSaveOrUpdateEventListener - saving transient instance
[java] 6406 [main] DEBUG org.hibernate.event.def.AbstractSaveEventListener - saving [test.Employment#<null>]
[java] 6406 [main] DEBUG org.hibernate.event.def.AbstractSaveEventListener - executing insertions
[java] 6406 [main] DEBUG org.hibernate.persister.entity.BasicEntityPersister - Inserting entity: test.Employment (native id)
[java] 6406 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
[java] 6406 [main] DEBUG org.hibernate.SQL - insert into employments (startDate, endDate) values (?, ?)
[java] Hibernate: insert into employments (startDate, endDate) values (?, ?)
[java] 6406 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - preparing statement
[java] 6406 [main] DEBUG org.hibernate.persister.entity.BasicEntityPersister - Dehydrating entity: [test.Employment#<null>]
[java] 6406 [main] DEBUG org.hibernate.type.TimestampType - binding '2005-09-27 13:26:58' to parameter: 1
[java] 6406 [main] DEBUG org.hibernate.type.TimestampType - binding '2005-09-27 13:26:59' to parameter: 2
[java] 6406 [main] DEBUG org.hibernate.id.IdentifierGeneratorFactory - Natively generated identity: 1
[java] 6406 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
[java] 6422 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - closing statement
[java] 6422 [main] DEBUG org.hibernate.engine.Cascades - done cascade ACTION_SAVE_UPDATE for collection: test.Person.formerEmployers
[java] 6422 [main] DEBUG org.hibernate.engine.Cascades - done processing cascade ACTION_SAVE_UPDATE for: test.Person
[java] 6422 [main] DEBUG org.hibernate.event.def.AbstractFlushingEventListener - dirty checking collections
[java] 6422 [main] DEBUG org.hibernate.event.def.AbstractFlushingEventListener - Flushing entities and processing referenced collections
[java] 6438 [main] DEBUG org.hibernate.event.def.WrapVisitor - Wrapped collection in role: test.Person.formerEmployers
[java] 6438 [main] DEBUG org.hibernate.persister.entity.BasicEntityPersister - test.Person.employer is dirty
[java] 6438 [main] DEBUG org.hibernate.persister.entity.BasicEntityPersister - test.Person.startDate is dirty
[java] 6438 [main] DEBUG org.hibernate.event.def.DefaultFlushEntityEventListener - Updating entity: [test.Person#1]
[java] 6438 [main] DEBUG org.hibernate.engine.Collections - Collection found: [test.Person.formerEmployers#1], was: [<unreferenced>] (initialized)
[java] 6438 [main] DEBUG org.hibernate.event.def.AbstractFlushingEventListener - Processing unreferenced collections
[java] 6438 [main] DEBUG org.hibernate.event.def.AbstractFlushingEventListener - Scheduling collection removes/(re)creates/updates
[java] 6453 [main] DEBUG org.hibernate.event.def.AbstractFlushingEventListener - Flushed: 0 insertions, 1 updates, 0 deletions to 4 objects
[java] 6453 [main] DEBUG org.hibernate.event.def.AbstractFlushingEventListener - Flushed: 1 (re)creations, 0 updates, 0 removals to 1 collections
[java] 6453 [main] DEBUG org.hibernate.pretty.Printer - listing entities:
[java] 6453 [main] DEBUG org.hibernate.pretty.Printer - test.Company{formerEmployees=null, employees=null, name=My Company, id=1}
[java] 6453 [main] DEBUG org.hibernate.pretty.Printer - test.Company{formerEmployees=null, employees=null, name=Rival Company, id=2}
[java] 6453 [main] DEBUG org.hibernate.pretty.Printer - test.Employment{startDate=2005-09-27 13:26:58, endDate=2005-09-27 13:26:59, id=1}
[java] 6453 [main] DEBUG org.hibernate.pretty.Printer - test.Person{startDate=2005-09-27 13:26:59, employer=test.Company#1, formerEmployers=[test.Employment#1], name=Paul Example, id=1}
[java] 6453 [main] DEBUG org.hibernate.event.def.AbstractFlushingEventListener - executing flush
[java] 6453 [main] DEBUG org.hibernate.persister.entity.BasicEntityPersister - Updating entity: [test.Person#1]
[java] 6453 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
[java] 6453 [main] DEBUG org.hibernate.SQL - update people set name=?, employerId=?, startDate=? where id=?
[java] Hibernate: update people set name=?, employerId=?, startDate=? where id=?
[java] 6453 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - preparing statement
[java] 6469 [main] DEBUG org.hibernate.persister.entity.BasicEntityPersister - Dehydrating entity: [test.Person#1]
[java] 6469 [main] DEBUG org.hibernate.type.StringType - binding 'Paul Example' to parameter: 1
[java] 6469 [main] DEBUG org.hibernate.type.LongType - binding '1' to parameter: 2
[java] 6469 [main] DEBUG org.hibernate.type.TimestampType - binding '2005-09-27 13:26:59' to parameter: 3
[java] 6469 [main] DEBUG org.hibernate.type.LongType - binding '1' to parameter: 4
[java] 6469 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - Adding to batch
[java] 6469 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - Executing batch size: 1
[java] 6469 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
[java] 6469 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - closing statement
[java] 6469 [main] DEBUG org.hibernate.persister.collection.AbstractCollectionPersister - Inserting collection: [test.Person.formerEmployers#1]
[java] 6469 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
[java] 6469 [main] DEBUG org.hibernate.SQL - insert into formerEmployments (contactId, employmentId) values (?, ?)
[java] Hibernate: insert into formerEmployments (contactId, employmentId) values (?, ?)
[java] 6469 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - preparing statement
[java] 6469 [main] DEBUG org.hibernate.type.LongType - binding '1' to parameter: 1
[java] 6469 [main] DEBUG org.hibernate.type.LongType - binding '1' to parameter: 2
[java] 6469 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - Adding to batch
[java] 6484 [main] DEBUG org.hibernate.persister.collection.AbstractCollectionPersister - done inserting collection: 1 rows inserted
[java] 6484 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - Executing batch size: 1
[java] 6516 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
[java] 6516 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - closing statement
[java] 6516 [main] DEBUG org.hibernate.util.JDBCExceptionReporter - Could not execute JDBC batch update [insert into formerEmployments (contactId, employmentId) values (?, ?)]
[java] java.sql.BatchUpdateException: Cannot add or update a child row: a foreign key constraint fails
This is basically a cleaned-up version of code I am writing for a crm app. There are 3 entities: Company, Person and Employment. Company and Person have 2 relationships: a one-to-many for current employers/employees and a many-to-many relationship involving the Employment entity for former employments. This additional class includes extra information such as start/end dates, reason for move, etc. What I would like to do is have Sets in both Company and Person for these old employment records but when I add an Employment to the Set in Person (the non-inverse end of the relationship), only the employment and person id are inserted into the link table, causing a foreign key constraint violation.
How can I ensure that the company id is also recorded, or am I approaching this problem from the wrong angle?