I have 2 tables
Account
-Id
-AccountName
Contractor
-Id
-AccountId referneces account table
-Code
When I Insert record in Contractor table, it should insert the name of the Contractor in the Account table as Account Name and store the AccountId in the Contractor table.
Can somebody help me in generating the mapping file for this? I tried the following below
Account.hbm
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="NHibernateService" assembly="NHibernateService">
<class name="Account" table="tbl_Account" entity-name="Account">
<id name="id" column="id">
<generator class="native" />
</id>
<property name="AcName" column="AcName" type="string" length="50" />
</class>
Contractor.hbm
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="NHibernateService" assembly="NHibernateService">
<class name="Contractor" table="tbl_Contractor" entity-name="Contractor">
<id name="id" column="id">
<generator class="native" />
</id>
<property name="Code" column="Code" type="string" length="50"/>
<bag name="Account" cascade="none" lazy="false">
<key column="Id"/>
<one-to-many entity-name="Account" />
</bag>
</class>
</hibernate-mapping>
The Contractor mapping should be quiet different. The table/schema structure says, that each Contract can have exactly one Account...not more. So there cannot be <bag> but we need <many-to-one>: (see 5.1.10. many-to-one)
<class name="Contractor" table="tbl_Contractor" entity-name="Contractor">
<id name="id" column="id">
<generator class="native" />
</id>
<property name="Code" column="Code" type="string" length="50"/>
<!--<bag name="Account" cascade="none" lazy="false">
<key column="Id"/>
<one-to-many entity-name="Account" />
</bag>-->
<many-to-one name="Account" column="AccountId" cascade="all" >
</class>
The Contractor C# definition should be like
public class Contractor
{
public virtual Account Account { get; set; }
...
And that all together should work for us, because we instructed the NHibernate to cascade changes into second end:
var contract = new Contract {... };
var account = new Account { .. };
contract.Account = account;
session.Save(contract); // both are persisted
It's been a while since I used NHibernate, but I believe you need to configure the cascade for the child collection on parent object which in your case is the Contractor class.
That way, when you do this:
var contractor = new Contractor { Code = "XX" };
contractor.Account.Add(new Account { Name = "Account Name" });
session.Insert(contractor);
NHibernate will generate:
INSERT INTO Account ...
INSERT INTO Contractor ...
You currently have it set to none, you probably want either all or save-update depending on whether you want deletes to cascade too. Check out this page for further info.
Related
I have this relationship between Car, Bike, Skateboard object with a Person object.
The relationship is stored in Ownership table.
How do I create this mapping in NHibernate ? I'm trying to follow this tutorial: http://nhibernate.info/doc/nh/en/index.html#inheritance. Any help is appreciated.
Here is the way I might do it.
<class lazy="false" name="Domain.OwnerShip, Domain" table="OwnerShip_Table">
<id name="Id" column="OwnerShipId" type="System.Int32" access="property">
<generator class="identity"></generator>
</id>
<joined-subclass name="Car" table="Cars" lazy="false">
<key column="Id" />
<property name="Owner" />
</joined-subclass>
<joined-subclass name="Bike" table="Bikes" lazy="false">
<key column="Id" />
<property name="Owner" />
</joined-subclass>
<joined-subclass name="Skateboard" table="Skateboards" lazy="false">
<key column="Id" />
<property name="Owner" />
</joined-subclass>
</class>
You would probably want a Persons table as well as a Person Entity with a mapping where Person has a Bag of OwnedVehicles.
Here is an option, with a table per hierarchy.
I assume :
Ownership holds its own Id (OwnerShipId, auto-generated)
you have a column per referenced class/table (BikeId, CarId), to handle different key types and foreign keys
The mapping would then look something like :
<class lazy="false" name="Domain.OwnerShip, Domain" table="OwnerShip_Table" discriminator-value="0">
<id name="Id" column="OwnerShipId" type="System.Int32" access="property">
<generator class="identity"></generator>
</id>
<discriminator column="VehicleTypeCode" type="int"/>
<many-to-one name="Owner" column="PersonId" not-null="true" fetch="..."/>
<many-to-one name="CodeType" column="VehicleTypeCode" not-null="true" fetch="..." />
<subclass name="Domain.BikeOwnerShip, Domain" discriminator-value="1">
<many-to-one name="Bike" column="BikeId" not-null="true" fetch="..."/>
</subclass>
<subclass name="Domain.CarOwnerShip, Domain" discriminator-value="2">
<many-to-one name="Car" column="CarId" not-null="true" fetch="..."/>
</subclass>
</class>
I guess it would also be nice to have a virtual, not mapped, OwnedObject property in the base class, something like
public virtual Object OwnedObject{
get{return null;}
}
in the CarOwnership class the OwnedObject implementation would be
public override Object OwnedObject{
get{return this.Car;}
}
Hope this will help
I'm upgrading an old NHibernate 1.2 solution I've taken over to NHib 3.1. We're having problems with persisting a parent child relationship. Which gives us this error:
NHibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)
This code was working in NHib 1.2 but does not work in 3.1
We're saving much like this code below:
Film f = NewFilm();
Recipe r = new Recipe("2", TimeSpan.FromMinutes(15), TimeSpan.FromMinutes(15));
f.Recipe = r;
SaveAndFlush(f, r); //custom code that saves f then saves r then flushes through the session.
However if we save r then f and flush it works.
I'd like to know why this happens, why the change between NHib versions. Is it the way the sesison thinks entities are transient now? Does it handle the foreign key id generator differently?
On a side note, the ID of the recipe doesn't equal the ID of the film, which I would expect it to do.
HMB files. - UPDATED to include full files
Film:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" schema="dbo">
<subclass name="Application.Core.Domain.Film, Application.Core" extends="Application.Core.Domain.VideoContent, Application.Core" discriminator-value="film" lazy="true">
<list inverse="false" lazy="true" name="Resources" access="field.camelcase-underscore" cascade="all-delete-orphan">
<key column="FilmId" />
<index column="PositionInFilm"/>
<one-to-many class="Application.Core.Domain.ContentResource, Application.Core" />
</list>
<list inverse="false" lazy="true" name="Steps" access="field.camelcase-underscore" cascade="all-delete-orphan">
<key column="FilmId" />
<index column="PositionInWebText"/>
<one-to-many class="Application.Core.Domain.WebText, Application.Core" />
</list>
<property name="FilmType" column="FilmType" />
<property name="PosterFrameTimeCode" column="PosterFrameTimeCode" />
<one-to-one name="Recipe" class="Application.Core.Domain.Recipe, Application.Core" cascade="save-update" access="field.camelcase-underscore"/>
<bag lazy="true" name="Shapes" access="field.camelcase-underscore" cascade="save-update" where="Archived=0">
<key column="ContentId"/>
<one-to-many class="Application.Core.Domain.FilmShape, Application.Core"/>
</bag>
<bag lazy="true" name="ArchivedShapes" access="field.camelcase-underscore" cascade="save-update" where="Archived=1">
<key column="ContentId"/>
<one-to-many class="Application.Core.Domain.FilmShape, Application.Core" />
</bag>
<many-to-one name="FilmToReplace" column="ReplacesFilmId" class="Application.Core.Domain.Film, Application.Core" access="field.camelcase-underscore" />
</subclass>
Recipe:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" schema="dbo">
<class name="Application.Core.Domain.Recipe,Application.Core" table="tbl_Recipe" lazy="false">
<id name="Id" column="HeaderId" type="System.Guid" access="field.camelcase-underscore">
<generator class="foreign">
<param name="property">Content</param>
</generator>
</id>
<list inverse="false" lazy="true" name="RecipeIngredients" access="field.camelcase-underscore" cascade="all-delete-orphan">
<key column="RecipeId" />
<index column="PositionInRecipe"/>
<one-to-many class="Application.Core.Domain.RecipeIngredient, Application.Core" />
</list>
<property name="Serves" column="Serves" type="System.String"/>
<property name="PreparationTime" column="PreparationTime" type="TimeSpan"/>
<property name="CookingTime" column="CookingTime" type="TimeSpan"/>
<property name="OvenTemperature" column="OvenTemperature" type="Application.Data.UserTypes.TemperatureType, Application.Data"/>
<one-to-one name="Content" class="Application.Core.Domain.Content, Application.Core" constrained="true" access="field.camelcase-underscore"/>
</class>
</hibernate-mapping>
If you have a cascading save on a "child" entity, you just save the parent. You don't need to save the child.
So here you should try saving only "f".
From the documentation:
http://nhibernate.info/doc/nh/en/index.html#mapping-declaration-onetoone
Primary key associations don't need an
extra table column; if two rows are
related by the association then the
two table rows share the same primary
key value. So if you want two objects
to be related by a primary key
association, you must make sure that
they are assigned the same identifier
value!
For a primary key association, add the
following mappings to Employee and
Person, respectively.
<one-to-one name="Person" class="Person"/>
<one-to-one name="Employee" class="Employee" constrained="true"/>
Now we must ensure that the primary
keys of related rows in the PERSON and
EMPLOYEE tables are equal. We use a
special NHibernate identifier
generation strategy called foreign:
<class name="Person" table="PERSON">
<id name="Id" column="PERSON_ID">
<generator class="foreign">
<param name="property">Employee</param>
</generator>
</id>
...
<one-to-one name="Employee"
class="Employee"
constrained="true"/>
</class>
A newly saved instance of Person is
then assigned the same primar key
value as the Employee instance refered
with the Employee property of that
Person.
While I am saving by calling SaveOrUpdate(), I got this warning and the data is not saving in the database after calling Transaction.Commit().
NHibernate.Engine.ForeignKeys - Unable
to determine if [project name] with
assigned identifier [primarykey] is
transient or detached; querying the
database. Use explicit Save() or
Update() in session to prevent this.
I am inserting a new object. Google search tell me to call Save() instead of SaveOrUpdate(): means Save() is only for inserting.
I search in Google and do not see much about this.
Could anyone give me suggestion for this problem or this warning?
Edit:
Here is the simulated sample mapping files -
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly=""
namespace="">
<class name="Customer" table="[dbo].[Customer]" optimistic-lock="none" >
<id name="CustomerId" column="CustomerId" >
<generator class="assigned"/>
</id>
<property name="Name" column="Name" />
<property name="Age" column="Age" />
<set name="CustomerDetails" cascade="none" inverse="true" fetch="select">
<key>
<column name="CustomerId"/>
</key>
<one-to-many class="CustomerDetail"/>
</set>
<many-to-one name="MGender" fetch="select" cascade="none">
<column name="GenderCode"/>
</many-to-one>
</class>
</hibernate-mapping>
<class name="CustomerDetails" table="[dbo].[CustomerDetail]" optimistic-lock="none" >
<id name="CustomerDetailId" column="CustomerDetailId" >
<generator class="assigned"/>
</id>
<property name="Detail1" column="Detail1" />
<many-to-one name="Customer" fetch="select" cascade="none">
<column name="CustomerId"/>
</many-to-one>
</class>
<class name="MGender" table="[dbo].[MGender]" optimistic-lock="none" >
<id name="GenderCode" column="GenderCode" >
<generator class="assigned"/>
</id>
<property name="Description" column="Description" />
<set name="Customers" cascade="none" inverse="true" fetch="select">
<key>
<column name="GenderCode"/>
</key>
<one-to-many class="Customer"/>
</set>
</class>
You're using an assigned identifier so you need to set the unsaved-value attribute so that NHibernate can determine if an entity should be inserted or updated. Or you can explicitly call Save for new entities.
<id name="CustomerId" column="CustomerId" unsaved-value="???" >
<generator class="assigned"/>
</id>
Please note that if you are using a custom id, ie not using Id(x => x.Id).GeneratedBy.Assigned() and you do not have any version or timestamp columns, there is no way NHibernate would know if thuis is a new row or an existing one. So, I fixed my problem by using GeneratedBy.UuidString(). Works like a charm. Almost put my code into production with this error message, fortunately my information was being saved.
I have the following map. My intention is for the order.BasketId to map to orderItem.BasketId. Tho when i look at the sql i see that it's mapping
order.Id to orderItem.BasketId. How do i define in my order map which order property to map against basketId. It seems to default to the primary key.
<class name="Order" table="Orders">
<id name="Id" type="Int32" column="Order_ID" unsaved-value="0">
<generator class="identity"/>
</id>
<property name="BasketId" column="Basket_ID" type="Int32"/>
<set name="OrderItems" table="item_basket_contents" generic="true" inverse="true" >
<key column="Basket_ID" />
<one-to-many class="EStore.Domain.Model.OrderItem, EStore.Domain"/>
</set>
</class>
and orderItem
<class name="OrderItem" table="Item_Basket_Contents">
<id name="Id" type="Int32" column="ID" unsaved-value="0">
<generator class="identity"/>
</id>
<property name="BasketId" column="Basket_ID" type="Int32"/>
</class>
Use the following:
<key column="Basket_ID" property-ref="BasketId" />
That's it.
Can you change the structure? I would have a Basket entity, which contains the order items. Your order then refers to this basket, and the basket contains the items. The explicit basketID in Order is circumventing some of the ORM that hibernate does.
I'm new to nhibernate so this should be easy. I have a mapping file as below although I deleted some fields that aren't relevant to this question. The streamfields class contains a bag of fieldmappings. I want the join to be on field_no column but the sql that is sent is on the id field (str_fld_id") as seen below.
I see what the below sql is doing but it's not what I wanted. It's trying to query the field_mappings table based on the values found in the id column str_fld_id in the StreamFields class when I thought it was clear I wanted the field_no to be used on both ends. I say I thought it was clear because the mapping for the field_mapping class has the below attribute and they both have the same named field
Below is in my FieldMappings mapping file.
<many-to-one name="FieldNo" cascade="none" column="`Field_No`" not-null="true">
Sql sent
NHibernate: SELECT fkfieldmap0_.[field_no] as field5_1_, fkfieldmap0_.[Mapping_Id] as Mapping1_1_, fkfieldmap0_.[Mapping_Id] as Mapping1_3_0_, fkfieldmap0_.[Std_fld_Id] as Std2_3_0_, fkfieldmap0_.[Field_Position] as Field3_3_0_, fkfieldmap0_.[Field_No] as Field4_3_0_ FROM [Field_Mappings] fkfieldmap0_ WHERE fkfieldmap0_.[field_no]=#p0; #p0 = '20'
StreamFields mapping
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="DataTransfer.StreamFields,DataTransfer" table="`stream_fields`" lazy="true">
<id name="StrFldId" column="`str_fld_id`" type="int">
<generator class="native" />
</id>
<property type="int" not-null="true" name="FieldNo" column="`field_no`" />
<many-to-one name="StreamId" cascade="none" column="`stream_Id`" />
<bag name="FkFieldMappingsStreamFields" inverse="true" lazy="false" cascade="all">
<key column="`field_no`" />
<one-to-many class="DataTransfer.FieldMappings,DataTransfer"/>
</bag>
</class>
[Edited - with old comments]
Okay, i think i finally got you right and i might admit the problems i had understanding what you want took me a while and result of the lack of information you provided. In the future please provide the mapping of both tables clarify on the point wheather it is a mapping or a query issue. Thx.
IMO you have misunderstood the idea of a parent/child-relation.
The bag you mentioned like to have within the StreamFields class shouldn't be a bag but a direct association. Like this:
<class name="DataTransfer.StreamFields,DataTransfer" table="stream_fields" >
<id name="StrFldId" column="str_fld_id" type="int">
<generator class="native" />
</id>
<property type="int" not-null="true" name="FieldNo" column="field_no" />
<many-to-one name="FieldMapping" class="FiueldMapping" column="Field_No" />
</class>
This of course will only work if you have a property of type FiledMapping in your class.
You want to map FieldMapping to the column Field_No within StreamFields class. There can only be one value within this column, so a bag makes no sense at all. If you want to have a bag of course you can keep it the way it already worked but be aware that the 'key-column' within the bag refers to the child table - in an other way it makes no sense cause a ForeignKey has to map to a PrimaryKey on its parent table. This ensures it is unique and set.
I really don't want to rant but would strongly encourage you to review the hibernate reference about collection mapping to get a deeper clue however.
Hopely this will solve your problem.
Below are the mappings for the classes.
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="DataTransfer.StreamFields,DataTransfer" table="`stream_fields`" lazy="true">
<id name="StrFldId" column="`str_fld_id`" type="int">
<generator class="native" />
</id>
<property type="string" length="50" name="FieldName" column="`field_name`" />
<property type="int" name="InputFieldPosition" column="`input_field_position`" />
<property type="int" name="Start" column="`start`" />
<property type="int" name="Width" column="`width`" />
<property type="string" length="50" name="Datatype" column="`datatype`" />
<property type="int" not-null="true" name="FieldNo" column="`field_no`" />
<property type="int" name="FieldOrder" column="`field_order`" />
<property type="int" name="StdId" column="`Std_Id`" />
<many-to-one name="StreamId" cascade="none" column="`stream_Id`" />
<bag name="FkFieldMappingsStreamFields" inverse="true" lazy="false" cascade="all">
<key column="`field_no`" />
<one-to-many class="DataTransfer.FieldMappings,DataTransfer"/>
</bag>
</class>
</hibernate-mapping>
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="DataTransfer.FieldMappings,DataTransfer" table="`Field_Mappings`" lazy="false">
<id name="MappingId" column="`Mapping_Id`" type="int">
<generator class="native" />
</id>
<property type="int" name="StdFldId" column="`Std_fld_Id`" />
<property type="int" name="FieldPosition" column="`Field_Position`" />
<many-to-one name="FieldNo" cascade="none" column="`Field_No`" not-null="true" property-ref="FieldNo" />
</class>
</hibernate-mapping>
To make things easy on myself, there is one record in stream_fields and the field_no value is 1 and 20 is the value in StrFldId.
SELECT fkfieldmap0_.[field_no] as field5_1_, fkfieldmap0_.[Mapping_Id] as Mapping1_1_, fkfieldmap0_.[Mapping_Id] as Mapping1_3_0_, fkfieldmap0_.[Std_fld_Id] as Std2_3_0_, fkfieldmap0_.[Field_Position] as Field3_3_0_, fkfieldmap0_.[Field_No] as Field4_3_0_ FROM [Field_Mappings] fkfieldmap0_ WHERE fkfieldmap0_.[field_no]=#p0; #p0 = '20' –