I use NHibernate 3.2 with MS SQL Server 2008 R2
I have the fallowing mappings
<class name="LocalizedProperty" table="LocalizedProperty">
<cache usage="read-write"/>
<id name="Id" column="Id">
<generator class="guid.comb"/>
</id>
<property name="CultureName" not-null="true"/>
<property name="PropertyName" not-null="true"/>
<property name="PropertyValue" not-null="true"/>
<any id-type="Guid" name="Entity">
<column name="LocalizedEntityClass" not-null="true"/>
<column name="EntityId" not-null="true"/>
</any>
</class>
And this one has a Reference to LocalizedProperty:
<class name="CommunicationType" table="CommunicationType" lazy="false" >
...
<set name="LocalizedProperties" where="LocalizedEntityClass = 'Prayon.Entities.CommunicationType'" cascade="delete">
<key column="EntityId" foreign-key="none" />
<one-to-many class="LocalizedProperty" />
</set>
</class>
My Problem is, when I delete an entity of CommunicationType, NHibernate is executing the fallowing update-statement for LocalizedProperty
UPDATE LocalizedProperty SET EntityId = null WHERE EntityId = #p0 AND (LocalizedEntityClass = 'Prayon.Entities.CommunicationType')
Instead of a Delete-Statement.
Does someone see, what is wrong?
To delete child elements of a table you need to specify cascade="all-delete-orphan".
<bag name="TableClassName" table="TableClassName" cascade="all-delete-orphan" >
<key column="PrimaryKey"/>
<one-to-many class="NameSpace.TableClassName" />
If you put a all-delete-orphan the problem is the all part. It will cascade all the actions, not just the delete.
Related
I have two classes: Order and User
Order has User class inside of it:
[DataMember]
public virtual User User { get; set; }
I have NHibernate mapping for both of them:
For User:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Purchasing" namespace="Purchasing.Other">
<class name="User" table="tUser">
<id name="RID">
<column name="RID" sql-type="bigint"/>
<generator class="native"/>
</id>
<property name="Created"/>
<property name="Modified"/>
<property name="UserName"/>
<property name="Email"/>
<property name="ExternalUserId"/>
</class>
</hibernate-mapping>
And I tried to add User class to Order mapping, BUT looks like something is wrong:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Purchasing" namespace="Purchasing.Other">
<class name="Order" table="tOrder">
<id name="Id">
<column name="OrderId" sql-type="bigint"/>
<generator class="native" />
</id>
<property name="DataOwnerId" />
<property name="UserId" />
<property name="OrderNo"/>
<property name="FirstTaken"/>
<property name="DateRequired" />
<property name="ExternalOrderId" />
<many-to-one name="User" class="User" property-ref="ExternalUserId">
<column name="UserId" not-null="false"/>
</many-to-one>
</class>
</hibernate-mapping>
I have a situation, when, writing Order in tOrder database, I also need to write its User to tUser database.
But looks like it didn't work. (Order mapping without User map works fine).
How in this situation mapping should look correctly, and what am I missing?
P.S Sorry for my bad English.
If we want to persist User with its Order, we can use cascading setting:
5.1.10. many-to-one
An ordinary association to another persistent class is declared using a many-to-one element. The relational model is a many-to-one association. (It's really just an object reference.)
<many-to-one
name="PropertyName" (1)
...
cascade="all|none|save-update|delete" (4)
...
/>
(4) cascade (optional): Specifies which operations should be cascaded from the parent object to the associated object.
So the mapping could be like this:
<many-to-one name="User" class="User"
property-ref="ExternalUserId"
cascade="save-update"
column="UserId" not-null="false"
/>
Now, we only have to be sure, that we try to find a user (it could be existing)
var user = session.Get<User>(userId);
if(user == null)
{
user = new User { ... };
}
And then we can assing that user to the Order
var order = new Order { ... };
order.User = user;
and calling session to persist Order - user will be as well:
session.SaveOrUpdae(order);
NOTE: if you can reference user by its native ID ... it would be better. Mapping then would look like:
<many-to-one name="User" class="User"
cascade="save-update"
column="RID" not-null="false"
/>
Mapping with property-ref should be used exceptionally
Wondering if anyone could point me in the right direction..
I currently have the following 3 tables in my sqlserver database
Parameter
ParameterId
ParameterName
ParameterValue
ParameterValueId
ParameterValue
ParameterParameterValue
ParameterId
ParameterValueId
I'm trying to get it where the Parameter domain object will also fetch all the ParameterValue objects as well (I'm guessing Parameter has a one-to-many relationship with ParameterValue, since a parameter can have more than one value) but I'm getting no where - the msot I've achieved is fetching the first value, rather than all :(
If anyone is willing to help or anything I can post some code and/or the mappings I'm using - as always, any help is much appreciated :)
Mappings for Parameter
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="StockMarketAdvisorDatabaseAccess.Domain"
assembly="StockMarketAdvisorDatabaseAccess">
<class name="Parameter" table="Parameter">
<id name="ParameterId">
<column name="ParameterId" sql-type="int" not-null="true" />
<generator class="identity" />
</id>
<property name="ParameterName" />
<bag name="ParameterValues" table="ParameterParameterValue" cascade="none">
<key column="ParameterValueId" />
<one-to-many class="ParameterValue" />
</bag>
</class>
</hibernate-mapping>
Mappings for ParameterValue
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="StockMarketAdvisorDatabaseAccess.Domain"
assembly="StockMarketAdvisorDatabaseAccess">
<class name="ParameterValue" table="ParameterValue">
<id name="ParameterValueId">
<column name="ParameterValueId" sql-type="int" not-null="true" />
<generator class="identity" />
</id>
<property name="Value" column="ParameterValue"/>
</class>
</hibernate-mapping>
Thanks again, just started using nHibernate so still trying to figure most it out! :)
Your issue is you are describing one thing (1-many) but your table structure is another (many-many)
If you need many values per parameter then you should simplify your table structure to:
Parameter
-------------
* ParameterId
ParameterName
ParameterValue
--------------------
* ParameterValueId
ParameterId
ParameterValue
Then your mapping can use a 1-many mapping:
<class name="Parameter" table="Parameter">
<id name="ParameterId">
<column name="ParameterId" sql-type="int" not-null="true" />
<generator class="identity" />
</id>
<property name="ParameterName" />
<bag name="ParameterValues" table="ParameterValue" cascade="none">
<key column="ParameterId" />
<one-to-many class="ParameterValue" />
</bag>
</class>
I have two tables with a many-to-many relation with a bridge table.
User Class Mapping :
<class name="MatrixCore.User" table="MatrixUser" lazy="false">
<id name="ID" column="ID" unsaved-value="0">
<generator class="native"/>
</id>
<property name="FirstName"/>
<property name="LastName"/>
<property name="UserName"/>
<property name="Password"/>
<many-to-one name="UserType" class="MatrixCore.UserType" />
<set name="Projects" table="UserInProject" cascade="All">
<key column="MatrixUser_ID"/>
<many-to-many class="Project" column="Project_ID"/>
</set>
</class>
Project class mapping :
<class name="MatrixCore.Project" table="Project" lazy="false">
<id name="ID" column="ID" unsaved-value="0">
<generator class="native"/>
</id>
<property name="Name" />
<property name="Acronym"/>
<property name="StartDate"/>
<property name="EndDate"/>
<set name="Users" table="UserInProject" cascade="All">
<key column="Project_ID"/>
<many-to-many class="User" column="MatrixUser_ID" />
</set>
</class>
the classes implementations are too simple each class have a collection
of the other.
I am trying to insert records in the tables the bridge table keep to be empty.
ICollection<Project> ps = new HashSet<Project>() { project};
UserType tp = (UserType)session.Get("UserType", 1);
User u = new User()
{
FirstName = "Hussein",
LastName = "Hussein",
UserName = "Hussein",
Password = "welcome",
UserType = tp,
Projects = ps
};
session.Save(u);
try this mapping
for user class
<set name="Projects" table="UserInProject" inverse="true" cascade="save-update" lazy="false">
<key column="MatrixUser_ID" />
<many-to-many class="Project" column="Project_ID"/>
</set>
and for Project Class
<set name="Users" table="UserInProject" cascade="save-update" lazy="false">
<key column="Project_ID" />
<many-to-many class="User" column="MatrixUser_ID"/>
</set>
You need to add the user to the Users property in each one of the Projects.
Add the following loop before the Save -
foreach (Project project in u.Projects)
{
project.Users.Add(u);
}
What is the correct HBM mapping for the scenario below?
I need to qualify ValueItem in the database as either an Income or Expense item in order for NHibernate to load it into the correct list when loading it.
The problem is: The contetns of the IncomeItems and ExpenseItems collections are the same when retrieving Container from the DB.
C#
public class Container
{
public virtual IList<ValueItem> IncomeItems { get; set; }
public virtual IList<ValueItem> ExpenseItems { get; set; }
}
public class ValueItem
{
public virtual double Amount { get; set; }
public virtual string Description { get; set; }
}
HBM
<class name="Container">
<id name="Id">
<generator class="hilo" />
</id>
<list name="IncomeItems " cascade="all-delete-orphan">
<key column="ContainerId" />
<index column="ItemIndex" />
<one-to-many class="ValueItem"/>
</list>
<list name="ExpenseItems " cascade="all-delete-orphan">
<key column="ContainerId" />
<index column="ItemIndex" />
<one-to-many class="ValueItem"/>
</list>
</class>
<class name="ValueItem">
<id column="Id" type="int">
<generator class="hilo" />
</id>
<property name="Amount" />
<property name="Description" />
</class>
This answer from Don on NHUsers solves my problem:
Don's answer:
I have a similar mapping to yours, except that I am using bags instead of lists, I have inverse="true", cascade set to the default, I explicitly set the table name, I have different column names for each key, and I have references back to what would be your Container with unique names for each. Perhaps it's the inverse=true or the different
column names.
Sorry about the hokey class names. I changed them from the realdomain object names on the spot, and I'm not feeling very creative.
Hope this helps,
<class name="Form" >
<many-to-one name="CreatorPerson" class="Person" />
<many-to-one name="ProcessorPerson" class="Person" />
</class>
<class name="Person">
<bag name="FormsCreated" inverse="true">
<key>
<column name="CreatorPersonId" not-null="true" />
</key>
<one-to-many class="Person" />
</bag>
<bag name="FormsToProcess" inverse="true">
<key>
<column name="ProcessorPerson" not-null="true" />
</key>
<one-to-many class="Person" />
</bag>
</class>
You can either choose an appropriate inheritance strategy:
http://nhibernate.info/doc/nh/en/index.html#inheritance
Alternatively, if your schema can't be changed. You can specify where attributes on your list mappings. These will specify how to fetch them.
just started out with NHIbernate and have one question, probably a bit of a stupi one! ;-)
I have 2 tables, Email and Attachment, an email can have zero or more attachments. so I created a hbm file like this:
<set name="Attachments" table="Attachments">
<key column="EmailId" foreign-key="fk_Attachments_Emails"/>
<composite-element class="Foo.Emails.Attachment, Foo.Emails">
<!-- PROBLEM HERE!!! -->
<property name="Id" column="Id" type="long" />
<!-- END PROBLEM -->
<property name="Name" column="Name" type="String" length="50"/>
<property name="Mime" column="MimeType" type="String" length="50"/>
<property name="Size" column="Size" type="long" />
<property name="FilePath" column="FilePath" type="String" length="256"/>
<property name="Parsed" column="Parsed" type="Boolean" />
</composite-element>
</set>
As I want to be able to search for the attachments by PK (the Id column in the set) I included it, but now everytime I try to save an email with attachments I get an error from the db as Nhibernate tries to insert a value into the PK, which my db naturally wont allow.
So, my question is, can I extract the pk for the Attqachment table but stop Nhiberntate from writing it when inserting a an email/attachment? Should I swap to another container like ?? if so wold you be abler to provide an example as I struggling to find a one that I understand!
THanks for your help!
Perhaps a more practical example? Where you have an object structure like this:
Email
--EmailId
--EmailProperty1
--AttachmentCollection
Attachment
--AttachmentId
--ParentEmail
--AttachmentProperty1
mapped to a table structure like this (not how i'd name it, but it's for example):
email
--emailId int PK, identity
--emailProp1 varchar(50)
emailattachment
--attachmentId int PK, identity
--emailId int, FK to email table
--attachmentProp1 varchar(50)
<hibernate-mapping>
<class name="Email" table="email">
<id name="EmailId">
<!-- this tells NHibernate to use DB to generate id -->
<generator class="native"/>
</id>
<property name="EmailProperty1" column="emailProp1"/>
<bag name="AttachmentCollection" lazy="true" inverse="true">
<key column="emailId"/>
<one-to-many class="Foo.Emails.Attachment, Foo.Emails"/>
</bag>
</class>
<class name="Attachment" table="emailattachment">
<id name="AttachmentId">
<generator class="native"/>
</id>
<property="AttachmentProperty1" column="attachmentProp1"/>
<many-to-one name="ParentEmail" class="Foo.Emails.Email, Foo.Emails" lazy="proxy" column="emailId">
</class>
</hibernate-mapping>
In this map, you'd get the bi-directional relationship, and that generator tag is telling nhibernate that objects with a null Id property (you can also specify another "unsaved-value"), then you're inserting the object, else updating. Should fix your current problem.
Couple other things: examine closely what kind of containers you need to use when mapping (bag vs. set vs. list). There's an excellent writeup in hibernatingrhino's NHibernateFAQ.
Also, since you're new to NHibernate, I very, very greatly recommend the summer of nhibernate screencasts. The best tool I've found so far for learning.
I think you want an bi-directional relationship. This way you can navigate the association both ways. This includes generated keys... Here is an example:
<class name="Order" table="ORDERHEADER" lazy="false" >
<id name="OrderId" column="ORDERID" type="int">
<generator class="sequence">
<param name="sequence">"ORDERID_SEQ"</param>
</generator>
</id>
<property name="OrderType" column="ORDERTYPE" type="Int16"/>
<bag name="OrderDetail" table="ORDERDETAIL" lazy="false" inverse="true">
<key column="OrderId"/>
<one-to-many class="OrderDetail" />
</bag>
<class name="OrderDetail" table="ORDERDETAIL" lazy="false">
<id name="OrderDetailId" column="ORDERDETAILID" type="int">
<generator class="sequence">
<param name="sequence">"ORDERDTLID_SEQ"</param>
</generator>
</id>
<property name="OrderId" column="ORDERID" type="Int32"/>
<property name="ItemNumber" column="ITEMNUMBER" type="Int32"/>
<property name="OrderQuantity" column="ORDERQUANTITY" type="Int32"/>
<many-to-one name="Order" class="Order" column="OrderId" />