Hi,
I have a problem with a one-to-one mapping. I have 3 classes: Customer, Person and Address. I also have 3 tables for those classes in the database, also 3 separate mapping files. Person is a subclass of Customer. When I save an instance of Person, I get the error that the column Address_id is null. This column has a foreign key to the Address table. The instance of Address is created in the constructor of Person. Therefore, the instance of Address should first be saved, then the instance of Person can be saved.
Is my mapping wrong? I don't want a relation from Address to Person, only from Person to Address, because the Address-class is also used in other classes, Employee for example.
The application is built in ASP.NET 2.0. I have a separate class library Domain for the domain classes and the hbm.xml files.
Hibernate version: NHibernate 1.1
Mapping documents:
Code:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0" assembly="MyApp.Domain">
<class name="Customer" table="Customer">
<cache usage="read-write" />
<id name="Id" type="Int32" column="id" unsaved-value="0">
<generator class="identity"/>
</id>
<property name="CustNr" column="CustNr" type="String" length="10" />
</class>
</hibernate-mapping>
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0" assembly="MyApp.Domain">
<joined-subclass name="Person" table="Person" extends="Customer">
<key column="id" />
<one-to-one name="Address" class="Address" constrained="true" cascade="all"/>
<property name="FirstName" column="FirstName" type="String" length="50"/>
<property name="LastName" column="LastName" type="String" length="50"/>
</joined-subclass>
</hibernate-mapping>
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0" assembly="MyApp.Domain">
<class name="Address" table="Address">
<cache usage="read-write" />
<id name="Id" type="Int32" column="id" unsaved-value="0">
<generator class="identity"/>
</id>
<property name="Street" column="Street" type="String" length="50" />
<property name="Number" column="Number" type="String" length="50" />
</class>
</hibernate-mapping>
Code between sessionFactory.openSession() and session.close():Code:
person = new Person();
person.FirstName = 'John';
person.LastName = 'Doe';
person.Address.Street = 'Bakerstreet'; //The Address-instance is created in the constructor of Person
person.Address.Number = 10;
Db.Save(person);
// This is the Save-method from Simon Green's Db-class (comes with NHibernateHelper).
public static void Save(object item)
{
ITransaction transaction = Db.Session.BeginTransaction();
try
{
Db.Session.Save(item);
transaction.Commit();
}
catch (Exception)
{
transaction.Rollback();
throw;
}
}
Full stack trace of any exception that occurs:Code:
[SqlException (0x80131904): Cannot insert the value NULL into column 'address_id', table 'MyDatabase.dbo.Person'; column does not allow nulls. INSERT fails.
The statement has been terminated.]
System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection) +95
System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection) +82
System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj) +346
System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj) +3244
System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString) +186
System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async) +1121
System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result) +334
System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(DbAsyncResult result, String methodName, Boolean sendToPipe) +407
System.Data.SqlClient.SqlCommand.ExecuteNonQuery() +149
NHibernate.Impl.BatcherImpl.ExecuteNonQuery(IDbCommand cmd) +58
NHibernate.Persister.NormalizedEntityPersister.Insert(Object[] fields, Boolean[] notNull, SqlString[] sql, Object obj, ISessionImplementor session) +892
[ADOException: could not insert: [Person]]
NHibernate.Persister.NormalizedEntityPersister.Insert(Object[] fields, Boolean[] notNull, SqlString[] sql, Object obj, ISessionImplementor session) +1056
NHibernate.Persister.NormalizedEntityPersister.Insert(Object[] fields, Object obj, ISessionImplementor session) +192
NHibernate.Impl.ScheduledIdentityInsertion.Execute() +97
NHibernate.Impl.SessionImpl.Execute(IExecutable executable) +128
NHibernate.Impl.SessionImpl.DoSave(Object theObj, Key key, IClassPersister persister, Boolean replicate, Boolean useIdentityColumn, CascadingAction cascadeAction, Object anything) +787
NHibernate.Impl.SessionImpl.DoSave(Object obj, Object id, IClassPersister persister, Boolean useIdentityColumn, CascadingAction cascadeAction, Object anything) +443
NHibernate.Impl.SessionImpl.SaveWithGeneratedIdentifier(Object obj, CascadingAction action, Object anything) +257
[ADOException: Could not save object]
NHibernate.Impl.SessionImpl.SaveWithGeneratedIdentifier(Object obj, CascadingAction action, Object anything) +394
NHibernate.Impl.SessionImpl.Save(Object obj) +237
Db.Save(Object item) in c:\NET\MyApp\App_Code\NHibernate\Db.cs:157
EditPerson.butSave_Click(Object sender, EventArgs e) in c:\NET\MyApp\User Controls\EditPerson.ascx.cs:52
System.Web.UI.WebControls.Button.OnClick(EventArgs e) +96
System.Web.UI.WebControls.Button.RaisePostBackEvent(String eventArgument) +117
System.Web.UI.WebControls.Button.System.Web.UI.IPostBackEventHandler.RaisePostBackEvent(String eventArgument) +31
System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument) +32
System.Web.UI.Page.RaisePostBackEvent(NameValueCollection postData) +72
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +3840
Name and version of the database you are using:
SQL Server 2005