Hi folks,
I'm having trouble with a bi-directional one-to-many parent/child relationship, as discussed in Section 9 of the reference manual. I'm using hibernate-2.1rc1, and when I try to add a new child, I get this:
Code:
net.sf.hibernate.PropertyValueException: not-null property references a null or transient value: net.sf.hibernate.persister.EntityPersister.domain
at net.sf.hibernate.impl.SessionImpl.checkNullability(SessionImpl.java:1206)
at net.sf.hibernate.impl.SessionImpl.doSave(SessionImpl.java:871)
at net.sf.hibernate.impl.SessionImpl.doSave(SessionImpl.java:815)
at net.sf.hibernate.impl.SessionImpl.saveWithGeneratedIdentifier(SessionImpl.java:738)
at net.sf.hibernate.impl.SessionImpl.save(SessionImpl.java:715)
at net.sf.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:1317)
at net.sf.hibernate.engine.Cascades$4.cascade(Cascades.java:114)
at net.sf.hibernate.engine.Cascades.cascade(Cascades.java:436)
at net.sf.hibernate.engine.Cascades.cascadeCollection(Cascades.java:526)
at net.sf.hibernate.engine.Cascades.cascade(Cascades.java:452)
at net.sf.hibernate.engine.Cascades.cascade(Cascades.java:503)
at net.sf.hibernate.engine.Cascades.cascade(Cascades.java:482)
at net.sf.hibernate.impl.SessionImpl.preFlushEntities(SessionImpl.java:2552)
at net.sf.hibernate.impl.SessionImpl.flushEverything(SessionImpl.java:2192)
at net.sf.hibernate.impl.SessionImpl.flush(SessionImpl.java:2181)
at net.sf.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:61)
at bz.clue.cluex4.daofactory.DataSource.commit(DataSource.java:42)
... 35 more
Here's my PostgreSQL 7.4 DDL:
Code:
CREATE TABLE Domain (
Id SERIAL PRIMARY KEY,
Name VARCHAR NOT NULL,
CONSTRAINT Domain_Name_ukey UNIQUE (Name)
);
CREATE TABLE Alias (
Id SERIAL PRIMARY KEY,
LocalPart VARCHAR NOT NULL,
DomainId INTEGER NOT NULL
CONSTRAINT Alias_DomainId REFERENCES Domain
ON DELETE CASCADE,
CONSTRAINT Alias_LocalPart_DomainId_ukey
UNIQUE(LocalPart, DomainId)
);
Here's the important stuff out of my Domain.hbm.xml and Alias.hbm.xml files:
Code:
<class name="bz.clue.cluex4.dao.Domain" table="Domain"
dynamic-update="false"
dynamic-insert="false">
<id name="id" column="Id" type="java.lang.Long">
<generator class="sequence">
<param name="sequence">Domain_Id_seq</param>
</generator>
</id>
...
<set name="aliases" lazy="true" inverse="true" cascade="all-delete-orphan">
<key column="DomainId"/>
<one-to-many class="bz.clue.cluex4.dao.Alias"/>
</set>
</class>
<class name="bz.clue.cluex4.dao.Alias" table="Alias"
dynamic-update="false"
dynamic-insert="false">
<id name="id" column="Id" type="java.lang.Long">
<generator class="sequence">
<param name="sequence">Alias_Id_seq</param>
</generator>
</id>
...
<many-to-one name="domain" column="DomainId" not-null="true"/>
</class>
So now, as per Section 9, I have the following interesting classes and methods:
Code:
public class Alias implements DataAccessObject
{
private Long id;
private Domain domain;
...
public void create()
{
domain.addAlias(this);
}
....
}
public class Domain implements DataAccessObject
{
private Long id;
private Set aliases = new HashSet();
....
public void addAlias(Alias alias)
{
alias.setDomain(this); // Not necessary in this use case, but the manual says do it
getAliases().add(alias);
}
}
I don't think the alias.setDomain(this) isn't necessary, because the caller is setting the domain property itself. But I stuck that in to conform with the manual.
So now in my calling code, I do this, where the DataSource class just encapsulates Session and Transaction functionality and the DomainFactory class's create() method encapsulates Session.save():
Code:
DataSource.beginTransaction();
Domain clue = new Domain();
domain.setName("clue.bz");
DomainFactory.create(clue);
Alias root = new Alias();
root.setLocalPart("root");
root.setDomain(clue);
root.create();
DataSource.commit();
The commit() fails with the exception given above, and it's the root.create() request that triggers it. Obviously, if I don't try to establish the parent/child relationship and persist the child, the commit() works.
I should point out that I had the parent/child relationship working perfectly when I settled for establishing the relationship like this in the calling code:
Code:
Alias root = new Alias();
root.setLocalPart("root");
clue.addAlias(root);
But then the caller uses FooFactory.create(foo) for some entities and parent.addChild(child) for others. I'm trying to remove this inconsistency from the caller's view.
I've read the Parent/Child section (9) very carefully, and I think I've followed it to the letter. I've checked the Users FAQ, the Latest FAQ from the Forums, and searched the old and current forum archives.
Any advice that could help me save what little hair I have left at this point would be greatly appreciated.
Thanks,
Sheldon.