Update: Ooops
I was having trouble even coming up with a workaround when I tripped across why -
Code:
<bag name="listOfChildren" table="list_of_children" cascade="all-delete-orphan" lazy="true">
<key column="[b]id[/b]"/>
<one-to-many class="hibernatetest.Child"/>
</bag>
Oops. I used the same name for the primary id column as I was using for the foreign key column, which obviously doesn't work.
It's my mistake. It would be nice if Hibernate would let me know about this overlapping-column-name kind of thing and show some sort of error, but it does turn out to be my fault.
I'm leaving my original post below.
----------------------------------------------------I'm either really messing my mapping with some little thing, or SchemaUpdate is generating an invalid foreign key (I'm using hbm2ddl.auto=create in my hibernate mapping file).
I've distilled the problem into the code/mappings below to make it as simple as possible to look at.
In the code, I have a Parent class that holds a reference to a single Child object (oneChild), and also has a Bag of Child objects (listOfChildren). The parent creates a Child object and assigns it to the oneChild property. Then it grabs that Child object, sets oneChild to null, and adds the Child object to the listOfChildren. Things still seem to be ok here.
Then it creates a second Child object and assigns it to oneChild. When the session is flushed, I get an exception saying that a foreign key constraint fails.
I looked into the constraint, and this is the constraint that's causing the problem:
Code:
16:31:55,813 DEBUG SchemaExport:303 -
alter table children
add index FK62EA5DFFDD08DB2F (id),
add constraint FK62EA5DFFDD08DB2F
foreign key (id)
references parents (id)
Apparently, it's hooking up a foreign key between children.id and parents.id. This seems wrong to me - I don't think the id column in the children table is supposed to be mapped as being both the primary key for the children table and a foreign key that refers to the primary key of the parent table.
Is this some sort of error in my mapping, or is it a schema generation bug?
Data Classes Code:Code:
package hibernatetest;
import java.util.*;
public class Parent {
private long id;
private Child oneChild = null;
private List<Child> listOfChildren = new ArrayList();
public Parent() {
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public List<Child> getListOfChildren() {
return listOfChildren;
}
private void setListOfChildren(List<Child> listOfChildren) {
this.listOfChildren = listOfChildren;
}
public Child getOneChild() {
return oneChild;
}
public void setOneChild(Child oneChild) {
this.oneChild = oneChild;
}
}
Code:
package hibernatetest;
import java.util.*;
public class Child {
private long id;
private Parent parent;
protected Child() {}
public Child(Parent parent) {
this.setParent(parent);
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public Parent getParent() {
return parent;
}
public void setParent(Parent parent) {
this.parent = parent;
}
}
Hibernate version: 3.2.1
Mapping documents:Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="hibernatetest.Parent" table="parents">
<id name="id">
<generator class="native"/>
</id>
<many-to-one name="oneChild" cascade="all" />
<bag name="listOfChildren" table="list_of_children" cascade="all-delete-orphan" lazy="true">
<key column="id"/>
<one-to-many class="hibernatetest.Child"/>
</bag>
</class>
</hibernate-mapping>
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="hibernatetest.Child" table="children">
<id name="id">
<generator class="native"/>
</id>
<many-to-one name="parent" column="parent_id"/>
</class>
</hibernate-mapping>
Code:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- Database connection settings -->
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost/testhibernate</property>
<property name="connection.username">testhibernate</property>
<property name="connection.password"></property>
<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">1</property>
<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
<!-- Enable Hibernate's automatic session context management -->
<property name="current_session_context_class">thread</property>
<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<property name="hibernate.use_sql_comments">true</property>
<!-- Drop and re-create the database schema on startup -->
<property name="hbm2ddl.auto">create</property> <!-- create-drop -->
<!-- List of the persistant classes -->
<mapping resource="hibernatetest/Parent.hbm.xml"/>
<mapping resource="hibernatetest/Child.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Code between sessionFactory.openSession() and session.close():Code:
package hibernatetest;
import org.hibernate.*;
import org.hibernate.cfg.*;
public class Main {
public static void main(String[] args) throws Throwable {
SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
try {
// Create the parent and set it's oneChild property
Parent parent = new Parent();
session.save(parent);
parent.setOneChild(new Child(parent));
// Move the oneChild object to the collection of Child objects
Child child = parent.getOneChild();
parent.setOneChild(null);
parent.getListOfChildren().add(child);
transaction.commit();
} catch(Exception ex) {
transaction.rollback();
throw ex;
} finally {
session.close();
}
session = sessionFactory.openSession();
transaction = session.beginTransaction();
try {
// Try to set another oneChild object on the parent - error occurs
Parent parent = (Parent) session.load(Parent.class, new Long(1));
parent.setOneChild(new Child(parent));
transaction.commit();
} catch(Exception ex) {
transaction.rollback();
throw ex;
} finally {
session.close();
}
}
}
Full stack trace of any exception that occurs:Code:
16:31:56,795 WARN JDBCExceptionReporter:77 - SQL Error: 1452, SQLState: 23000
16:31:56,885 ERROR JDBCExceptionReporter:78 - Cannot add or update a child row: a foreign key constraint fails (`testhibernate/children`, CONSTRAINT `FK62EA5DFFDD08DB2F` FOREIGN KEY (`id`) REFERENCES `parents` (`id`))
Exception in thread "main" org.hibernate.exception.ConstraintViolationException: could not insert: [hibernatetest.Child]
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:71)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
at org.hibernate.id.insert.AbstractReturningDelegate.performInsert(AbstractReturningDelegate.java:40)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2108)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2588)
at org.hibernate.action.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:48)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:248)
at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:290)
at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:180)
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:108)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:186)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:175)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:98)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70)
at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:507)
at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:499)
at org.hibernate.engine.CascadingAction$1.cascade(CascadingAction.java:218)
at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:268)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:216)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:169)
at org.hibernate.engine.Cascade.cascade(Cascade.java:130)
at org.hibernate.event.def.AbstractFlushingEventListener.cascadeOnFlush(AbstractFlushingEventListener.java:131)
at org.hibernate.event.def.AbstractFlushingEventListener.prepareEntityFlushes(AbstractFlushingEventListener.java:122)
at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:65)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:26)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
at hibernatetest.Main.main(Main.java:38)
Caused by: com.mysql.jdbc.exceptions.MySQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (`testhibernate/children`, CONSTRAINT `FK62EA5DFFDD08DB2F` FOREIGN KEY (`id`) REFERENCES `parents` (`id`))
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:931)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:2870)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1573)
at com.mysql.jdbc.ServerPreparedStatement.serverExecute(ServerPreparedStatement.java:1169)
at com.mysql.jdbc.ServerPreparedStatement.executeInternal(ServerPreparedStatement.java:693)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1404)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1318)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1303)
at org.hibernate.id.IdentityGenerator$GetGeneratedKeysDelegate.executeAndExtract(IdentityGenerator.java:73)
at org.hibernate.id.insert.AbstractReturningDelegate.performInsert(AbstractReturningDelegate.java:33)
... 26 more
Java Result: 1
Name and version of the database you are using:MySQL 5.0.22-community-max-nt
The generated SQL (show_sql=true):Code:
16:31:54,161 DEBUG SchemaExport:303 -
alter table children
drop
foreign key FK62EA5DFF586F7EE4
16:31:54,521 DEBUG SchemaExport:303 -
alter table children
drop
foreign key FK62EA5DFFDD08DB2F
16:31:54,852 DEBUG SchemaExport:303 -
alter table parents
drop
foreign key FKD0B60D0954B030E8
16:31:55,192 DEBUG SchemaExport:303 -
drop table if exists children
16:31:55,242 DEBUG SchemaExport:303 -
drop table if exists parents
16:31:55,303 DEBUG SchemaExport:303 -
create table children (
id bigint not null auto_increment,
parent_id bigint,
primary key (id)
) ENGINE=InnoDB
16:31:55,423 DEBUG SchemaExport:303 -
create table parents (
id bigint not null auto_increment,
oneChild bigint,
primary key (id)
) ENGINE=InnoDB
16:31:55,523 DEBUG SchemaExport:303 -
alter table children
add index FK62EA5DFF586F7EE4 (parent_id),
add constraint FK62EA5DFF586F7EE4
foreign key (parent_id)
references parents (id)
16:31:55,813 DEBUG SchemaExport:303 -
alter table children
add index FK62EA5DFFDD08DB2F (id),
add constraint FK62EA5DFFDD08DB2F
foreign key (id)
references parents (id)
16:31:56,154 DEBUG SchemaExport:303 -
alter table parents
add index FKD0B60D0954B030E8 (oneChild),
add constraint FKD0B60D0954B030E8
foreign key (oneChild)
references children (id)
16:31:56,474 INFO SchemaExport:196 - schema export complete
Hibernate:
/* insert hibernatetest.Parent
*/ insert
into
parents
(oneChild)
values
(?)
Hibernate:
/* insert hibernatetest.Child
*/ insert
into
children
(parent_id)
values
(?)
Hibernate:
/* create one-to-many row hibernatetest.Parent.listOfChildren */ update
children
set
id=?
where
id=?
Hibernate:
/* load hibernatetest.Parent */ select
parent0_.id as id0_0_,
parent0_.oneChild as oneChild0_0_
from
parents parent0_
where
parent0_.id=?
Hibernate:
/* insert hibernatetest.Child
*/ insert
into
children
(parent_id)
values
(?)
Debug level Hibernate log excerpt:
See above stuff.