Hello I cannot figure this out.
I have 2 base classes each with 2 subclasses like this
Parent
Parent1 extends Parent
Parent2 extends Parent
and
Child
Child1 extends Child
Child2 extends Child
Child1 has a many to one relationship with Parent1
Child2 has a many to one relationship with Parent2
After reading through the docs I tried to make it work using discriminator
Here is the code
package org.marcus;
import java.io.Serializable;
import java.util.Date;
import java.util.Set;
import org.apache.commons.lang.builder.ToStringBuilder;
/**
* @author Marcus
* @hibernate.class table="child"
* discriminator-value="M"
* @hibernate.discriminator column="type" type="character"
* Represents a contact.
*/
public class Child {
long child_id = -1;
/** Creates a new instance of Child */
public Child() {
}
/**
* @hibernate.id unsaved-value="-1" generator-class="native"
*/
public long getChild_id() {
return child_id;
}
/**
* Setter for property child_id.
* @param child_id New value of property child_id.
*/
public void setChild_id(long child_id) {
this.child_id = child_id;
}
public String toString() {
return new ToStringBuilder(this)
.append("id", getChild_id())
.toString();
}
public boolean equals(Object other) {
if ( (this == other ) ) return true;
if ( !(this.getClass().isInstance(other))) return false;
Child castOther = (Child) other;
if(this.child_id == castOther.child_id)return true;
return false;
}
public int hashCode() {
return (int)child_id;
}
}
package org.marcus;
/**
*
* @author marcus
*@hibernate.subclass table="child" discriminator-value="P"
*/
public class Child1 extends Child{
Parent1 parent;
/** Creates a new instance of Child1 */
public Child1() {
}
/**
* @hibernate.many-to-one name="parent" column="parent_id" class="org.marcus.Parent1" not-null="true"
*
*/
public Parent1 getParent() {
return parent;
}
/**
* Setter for property parent.
* @param parent New value of property parent.
*/
public void setParent(Parent1 parent) {
this.parent = parent;
}
}
package org.marcus;
/**
*
* @author marcus
*@hibernate.subclass table="child" discriminator-value="Z"
*/
public class Child2 extends Child{
Parent2 parent;
/** Creates a new instance of Child2 */
public Child2() {
}
/**
* @hibernate.many-to-one name="parent" column="parent_id" class="org.marcus.Parent2" not-null="true"
*
*/
public Parent2 getParent() {
return parent;
}
/**
* Setter for property parent.
* @param parent New value of property parent.
*/
public void setParent(Parent2 parent) {
this.parent = parent;
}
}
Here are the parents
package org.marcus;
import java.io.Serializable;
import java.util.Date;
import java.util.*;
import org.apache.commons.lang.builder.ToStringBuilder;
public class Parent {
private long parent_id = -1;
private String name;
/** Creates a new instance of Parent */
public Parent() {
}
/**
* @hibernate.id unsaved-value="-1" generator-class="native"
*/
public long getParent_id() {
return parent_id;
}
/**
* Setter for property parent_id.
* @param parent_id New value of property parent_id.
*/
public void setParent_id(long parent_id) {
this.parent_id = parent_id;
}
/**
* @hibernate.property
*/
public java.lang.String getName() {
return name;
}
/**
* Setter for property name.
* @param name New value of property name.
*/
public void setName(java.lang.String name) {
this.name = name;
}
public String toString() {
return new ToStringBuilder(this)
.append("id", getParent_id())
.toString();
}
public boolean equals(Object other) {
if ( (this == other ) ) return true;
if ( !(this.getClass().isInstance(other))) return false;
Parent castOther = (Parent) other;
if(this.parent_id == castOther.parent_id)return true;
return false;
}
public int hashCode() {
return (int)parent_id;
}
}
package org.marcus;
import java.io.Serializable;
import java.util.Date;
import java.util.*;
import org.apache.commons.lang.builder.ToStringBuilder;
/**
* @author Marcus
* @hibernate.class table="parent1"
* Represents a contact.
*/
public class Parent1 extends Parent{
private Set children = new HashSet();
/** Creates a new instance of Parent1 */
public Parent1() {
}
public void addChild(Child1 c) {
c.setParent(this);
getChildren().add(c);
}
/**
* @hibernate.set name="children" inverse="true" cascade="all-delete-orphan"
*
* @hibernate.collection-key column="parent_id"
* @hibernate.collection-one-to-many class="org.marcus.Child1"
* @return
*/
public java.util.Set getChildren() {
return children;
}
/**
* Setter for property children.
* @param children New value of property children.
*/
public void setChildren(java.util.Set children) {
this.children = children;
}
}
package org.marcus;
import java.io.Serializable;
import java.util.Date;
import java.util.*;
import org.apache.commons.lang.builder.ToStringBuilder;
/**
* @author Marcus
* @hibernate.class table="parent2"
* Represents a contact.
*/
public class Parent2 extends Parent{
private Set children = new HashSet();
/** Creates a new instance of Parent2 */
public Parent2() {
}
public void addChild(Child2 c) {
c.setParent(this);
getChildren().add(c);
}
/**
* @hibernate.set name="children" inverse="true" cascade="all-delete-orphan"
*
* @hibernate.collection-key column="parent_id"
* @hibernate.collection-one-to-many class="org.marcus.Child2"
* @return
*/
public java.util.Set getChildren() {
return children;
}
/**
* Setter for property children.
* @param children New value of property children.
*/
public void setChildren(java.util.Set children) {
this.children = children;
}
}
Here are the generated mappings from XDoclet
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
<class
name="org.marcus.Child"
table="child"
dynamic-update="false"
dynamic-insert="false"
discriminator-value="M"
>
<id
name="child_id"
column="child_id"
type="long"
unsaved-value="-1"
>
<generator class="native">
</generator>
</id>
<discriminator
column="type"
type="character"
/>
<!--
To add non XDoclet property mappings, create a file named
hibernate-properties-Child.xml
containing the additional properties and place it in your merge dir.
-->
<subclass
name="org.marcus.Child1"
dynamic-update="false"
dynamic-insert="false"
discriminator-value="P"
>
<many-to-one
name="parent"
class="org.marcus.Parent1"
cascade="none"
outer-join="auto"
update="true"
insert="true"
access="property"
column="parent_id"
not-null="true"
/>
<!--
To add non XDoclet property mappings, create a file named
hibernate-properties-Child1.xml
containing the additional properties and place it in your merge dir.
-->
</subclass>
<subclass
name="org.marcus.Child2"
dynamic-update="false"
dynamic-insert="false"
discriminator-value="Z"
>
<many-to-one
name="parent"
class="org.marcus.Parent2"
cascade="none"
outer-join="auto"
update="true"
insert="true"
access="property"
column="parent_id"
not-null="true"
/>
<!--
To add non XDoclet property mappings, create a file named
hibernate-properties-Child2.xml
containing the additional properties and place it in your merge dir.
-->
</subclass>
</class>
</hibernate-mapping>
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
<class
name="org.marcus.Parent1"
table="parent1"
dynamic-update="false"
dynamic-insert="false"
>
<id
name="parent_id"
column="parent_id"
type="long"
unsaved-value="-1"
>
<generator class="native">
</generator>
</id>
<set
name="children"
lazy="false"
inverse="true"
cascade="all-delete-orphan"
sort="unsorted"
>
<key
column="parent_id"
>
</key>
<one-to-many
class="org.marcus.Child1"
/>
</set>
<property
name="name"
type="java.lang.String"
update="true"
insert="true"
access="property"
column="name"
/>
<!--
To add non XDoclet property mappings, create a file named
hibernate-properties-Parent1.xml
containing the additional properties and place it in your merge dir.
-->
</class>
</hibernate-mapping>
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
<class
name="org.marcus.Parent2"
table="parent2"
dynamic-update="false"
dynamic-insert="false"
>
<id
name="parent_id"
column="parent_id"
type="long"
unsaved-value="-1"
>
<generator class="native">
</generator>
</id>
<set
name="children"
lazy="false"
inverse="true"
cascade="all-delete-orphan"
sort="unsorted"
>
<key
column="parent_id"
>
</key>
<one-to-many
class="org.marcus.Child2"
/>
</set>
<property
name="name"
type="java.lang.String"
update="true"
insert="true"
access="property"
column="name"
/>
<!--
To add non XDoclet property mappings, create a file named
hibernate-properties-Parent2.xml
containing the additional properties and place it in your merge dir.
-->
</class>
</hibernate-mapping>
If I run this snippet of code
Parent1 p = new Parent1();
Child1 c = new Child1();
p.addChild(c);
session.save(p);
session.flush();
I get this error
19:29:16,305 WARN JDBCExceptionReporter:38 - SQL Error: 0, SQLState: null
19:29:16,306 ERROR JDBCExceptionReporter:46 - Batch entry 0 insert into child (parent_id, type, child_id) values ( was aborted. Call getNextException() to see the cause.
19:29:16,309 WARN JDBCExceptionReporter:38 - SQL Error: 0, SQLState: 23503
19:29:16,309 ERROR JDBCExceptionReporter:46 - ERROR: insert or update on table "child" violates foreign key constraint "fk5a3f51c7b66b0d0b9579e27"
19:29:16,313 WARN JDBCExceptionReporter:38 - SQL Error: 0, SQLState: null
19:29:16,314 ERROR JDBCExceptionReporter:46 - Batch entry 0 insert into child (parent_id, type, child_id) values ( was aborted. Call getNextException() to see the cause.
19:29:16,314 WARN JDBCExceptionReporter:38 - SQL Error: 0, SQLState: 23503
19:29:16,315 ERROR JDBCExceptionReporter:46 - ERROR: insert or update on table "child" violates foreign key constraint "fk5a3f51c7b66b0d0b9579e27"
19:29:16,317 ERROR JDBCExceptionReporter:38 - Could not execute JDBC batch update
Batch entry 0 insert into child (parent_id, type, child_id) values ( was aborted. Call getNextException() to see the cause.
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeBatch(AbstractJdbc2Statement.java:107)
at com.mchange.v2.sql.filter.FilterPreparedStatement.executeBatch(FilterPreparedStatement.java:260)
at net.sf.hibernate.impl.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:54)
at net.sf.hibernate.impl.BatcherImpl.executeBatch(BatcherImpl.java:122)
at net.sf.hibernate.impl.SessionImpl.executeAll(SessionImpl.java:2410)
at net.sf.hibernate.impl.SessionImpl.execute(SessionImpl.java:2360)
at net.sf.hibernate.impl.SessionImpl.flush(SessionImpl.java:2229)
at Jubilee.Jubilee.main(Jubilee.java:76)
19:29:16,319 ERROR SessionImpl:2368 - Could not synchronize database state with session
net.sf.hibernate.JDBCException: Could not execute JDBC batch update
at net.sf.hibernate.impl.BatcherImpl.executeBatch(BatcherImpl.java:129)
at net.sf.hibernate.impl.SessionImpl.executeAll(SessionImpl.java:2410)
at net.sf.hibernate.impl.SessionImpl.execute(SessionImpl.java:2360)
at net.sf.hibernate.impl.SessionImpl.flush(SessionImpl.java:2229)
at Jubilee.Jubilee.main(Jubilee.java:76)
ad infinitum.
Obviously hibernate is not providing a value to link the child1 object with its parent1 object
If I remove the Child2 Parent2 references completely it works fine.
If I run this snippet with Parent1 Child1 out of the picture
Parent2 p = new Parent2();
Child2 c = new Child2();
p.addChild(c);
session.save(p);
session.flush();
It also runs fine
Perhaps my concept of discriminator is wrong. What I really want to achieve is the following
Child Object can have a many to one relationship with either an instance of Parent 1 or Parent2 or ParentX but cant exist without either a Parent1 or a Parent2 or ParentX
Looking at the generated sql. I can see the reason its failing. For some reason its using foreign keys which can never be satisfied. What is the point of having a discriminator value if its also going to use foreign keys
alter table child drop constraint FK5A3F51C7B66B0D0;
alter table child drop constraint FK5A3F51C7B66B0D0B9579E27;
drop table child;
drop table parent1;
drop table parent2;
drop sequence hibernate_sequence;
create table child (
child_id INT8 not null,
type CHAR(1) not null,
parent_id INT8 not null,
primary key (child_id)
);
create table parent1 (
parent_id INT8 not null,
name VARCHAR(255),
primary key (parent_id)
);
create table parent2 (
parent_id INT8 not null,
name VARCHAR(255),
primary key (parent_id)
);
alter table child add constraint FK5A3F51C7B66B0D0 foreign key (parent_id) references parent1;
alter table child add constraint FK5A3F51C7B66B0D0B9579E27 foreign key (parent_id) references parent2;
create sequence hibernate_sequence;
Any help would be appreciated
Marcus
|