I have a Class that has two bags.
One bag is with a collection of a class and it works, the other is a collection of long values and this one is not persisted.
I've searched all the web for this and my mappings appears to be OK.
In my mapping I have this:
<class name="Event" table="Events">
<id name="Id" type="Int32">
<generator class="native" />
</id>
<property name="Name" />
<property name="Owner" />
<many-to-one name="DeliveryAddress" column="DeliveryAddressId" cascade="save-update, persist" />
<many-to-one name="EventAddress" column="EventAddressId" cascade="save-update, persist" />
<bag name="Friends" table="Event_Friends" lazy="false" inverse="true" cascade="save-update, persist" fetch="join">
<key column="EventId" />
<element column="Friend" type="Int64" />
</bag>
<bag name="Products" table="Event_Products" lazy="false" inverse="true" cascade="all,delete-orphan" fetch="join">
<key column="EventId" />
<one-to-many class="Product" />
</bag>
</class>
When I call SabeOrUpdate in my session NHibernate create both adresses, create the event and all the products, but the friends list is not saved.
After the save I issue an Get, and the select on the database is correct.
I don't know what else can be.
My model for this mapping is this:
public class Event
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Int64 Owner { get; set; }
public virtual Address DeliveryAddress { get; set; }
public virtual Address EventAddress { get; set; }
public virtual ICollection<Int64> Friends { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
and my database looks like this:
Events
-------------------------------
Id int identity
Name varchar
Owner long
DeliveryAddressId int
EventAddressId int
Address
-------------------------------
Id int
-- Code Abbreviated --
Event_Products
-------------------------------
Id int
EventId int
-- Code Abbreviated --
Event_Friends
-------------------------------
EventId int
Friend long
You should change inverse to false for your collection of longs
<bag name="Friends" table="Event_Friends" lazy="false" inverse="false" cascade="save-update, persist" fetch="join">
<key column="EventId" />
<element column="Friend" type="Int64" />
</bag>
Related
I am trying to map a legacy database. I need to implement what I believe should be a discriminator. My problem is discriminators only seem to work when there is a column to differentiate or a formula on the current row. For my case there is no actual differentiator, the data is either joined to one table if it exists, if not, then the other table. To make things even more complicated the table uses a composite key.
Here's an example (it might be oversimplified as I am making it up):
Given my code
public class SomeTable {
public virtual int DataID { get; set; }
public virtual int EmployeeOrCustomer { get; set; }
public virtual Person Person { get; set; }
public virtual int SomeValue { get; set; }
}
public abstract class Person {
public virtual int ID { get; set; }
public virtual string Name { get; set; }
}
public class Employee : Person {
public virtual int EmployeeNumber { get; set; }
}
public class Customer : Person {
public virtual int CustomerNumber { get; set; }
}
And my Data
-SomeTable-
DataID(K) EmployeeOrCustomer(K) SomeValue
1 1 100
1 22 222
-Employee-
ID Name EmployeNumber
1 Joe Blow 12345
-Customer-
ID Name CustomerNumber
22 ACME Inc. 4242
My Mappings:
<class name="SomeTable" abstract="true">
<composite-id>
<key-property name="DataID" />
<key-property name="EmployeeOrCustomer" />
</composite-id>
<property name="SomeValue" />
<!-- ?????? -->
</class>
<class name="Employee">
<id name="ID" />
<Property name="EmployeeNumber">
</Class>
<class name="Customer">
<id name="ID" />
<Property name="CustomerNumber">
</Class>
What I expect
DataID: 1, SomeValue: 100 Person: { Employee: EmployeeNumber: 12345 }
DataID: 1, SomeValue: 222, Person: { Customer: CustomerNumber: 4242 }
I thought about joining the 2 tables and using the merged tables as a join but the 2 tables differ a lot. Also, I can't modify the schema so adding a discriminator column is not an option.
Any ideas?
i think what you are searching for is union subclass inheritance mapping
<class name="Person" abstract="true">
<id name="Id">
<generator class="assigned"/>
</id>
<union-subclass table="Employee" name="Employee">
<property name="Name"/>
<property name="EmployeeNumber"/>
</union-subclass>
<union-subclass table="Customer" name="Customer">
<property name="Name"/>
<property name="CustomerNumber"/>
</union-subclass>
</class>
<class name="SomeTable" abstract="true">
<composite-id>
<key-property name="DataID" />
<key-many-to-one name="Person" column="EmployeeOrCustomer" />
</composite-id>
<property name="SomeValue" />
</class>
Note the extra property EmployeeOrCustomer is not needed.
My classes:
public class Parent
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<Child> Children { get; set; }
/* other properties */
}
public class Child
{
public virtual int Id { get; set; }
public virtual Parent Mother { get; set; }
public virtual int ItemNumber { get; set; }
public virtual string Remarks { get; set; }
/* other properties */
}
My DB:
PARENTS(ID, NAME, ...) -> PRIMARY KEY = ID
CHILDREN (C_ID, MOTHER_ID, ITEM_NO, REMARKS ....) -> PRIMARY KEY = c_ID
NHibernate Mapping:
<class name="Parent" table="PARENTS">
<id name="Id" column="ID">
<generator class="increment" />
</id>
<property name="Name" column="NAME"/>
<list name="Children" cascade="all" inverse="true">
<key column="MOTHER_ID" not-null="true" />
<list-index base="1" column="ITEM_NO"/>
<one-to-many class="Child" />
</list>
</class>
<class name="Child" table="CHILDREN">
<id name="Id" column="C_ID">
<generator class="increment" />
</id>
<many-to-one name="Mother" class="Parent" column="MOTHER_ID" />
<property name="ItemNumber" column="ITEM_NO"/>
<property name="Remarks" column="REMARKS"/>
</class>
My Code:
Parent p = new Parent();
//set parent properties here
Child c = new Child();
//set child properties here
p.Children.add(c);
c.Mother = p;
//Save parent
Question:
The above code saves the parent as well as all children in the respective tables. However, all the children have the default value zero in the field ITEM_NO. Is there any mapping that can help me to automatically assign a progressively incremental value to ITEM_NO starting with 1 for each children of a given parent? i.e the sequence for ITEM_NO should repeat from 1 to the number of children for each Parent.
NB: I need Child to be an independent entity, so I should be able to query it without necessariry going through the parent.
I think the problem is the insert="true" in the list-element, as this tells NHibernate that it's the child's responsibility to set the foreign key. And the child doesn't know about the list mapping.
If I correctly understand your question you have to create a sequence in your database (see description for it), a simple example would be (might be different from database to database):
CREATE SEQUENCE item_no_seq START WITH 1 INCREMENT BY 1
and then change your mapping a bit:
<id name="ItemNumber" type="integer" column="ITEM_NO">
<generator class="sequence">
<param name="sequence">item_no_seq</param>
</generator>
</id>
I'm starting to learn NHibernate (3.0) and picked up a copy of Packt Publishing's NHibernate 3.0 Cookbook.
There's a section on one-to-many mappings which I'm walking through but with my own database. It suggests I should do something like this to model a one to many relationship between customers and their domains:
public class Customer
{
public virtual int Id { get; protected set; }
public virtual string CustomerName { get; set; }
// Customer has many domains
public virtual IList<Domain> Domains { get; set; }
}
public class Domain
{
public virtual int Id { get; protected set; }
public virtual int CustomerID { get; set; }
public virtual string DomainName { get; set; }
}
Customer Mapping:
<class name="Customer" table="tblCustomer">
<id name="Id">
<column name="CustomerID" sql-type="int" not-null="true"/>
<generator class="identity"/>
</id>
<property name="CustomerName" column="Customer"/>
<list name="Domains">
<key column="CustomerID"/>
<one-to-many class="Domain" />
</list>
</class>
When I run this I get the following error:
XML validation error: The element 'list' in namespace 'urn:nhibernate-mapping-2.2' has invalid child element 'one-to-many' in namespace 'urn:nhibernate-mapping-2.2'. List of possible elements expected: 'index, list-index' in namespace 'urn:nhibernate-mapping-2.2'.
The book's example is a bit more complex in that they use table-per-subclass mappings:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Eg.Core"
namespace="Eg.Core">
<subclass name="Movie" extends="Product">
<property name="Director" />
<list name="Actors" cascade="all-delete-orphan">
<key column="MovieId" />
<index column="ActorIndex" />
<one-to-many class="ActorRole"/> <-- Is this wrong?
</list>
</subclass>
</hibernate-mapping>
I'm guessing the book is wrong?
No, your mapping is missing the index element. A list in NHibernate is an ordered set, if you want an unordered set use bag mapping.
I have a class:
public class User
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IDictionary<string, string> Attributes { get; set; }
}
and a mapping file:
<class name="User" table="Users">
<id name="Id">
<generator class="hilo"/>
</id>
<property name="Name"/>
<map name="Attributes" table="UserAttributes">
<key column="UserId"/>
<index column="AttributeName" type="System.String"/>
<element column="Attributevalue" type="System.String"/>
</map>
</class>
So now I can add many attributes and values to a User.
How can I query those attributes so I can get ie.
Get all the users where attributename is "Age" and attribute value is "20" ?
I don't want to do this in foreach because I may have millions of users each having its unique attributes.
Please help
You can do it using HQL.
For example:
from User u join u.Attributes attr
where index(attr) = 'Age' and attr = '20'
Edit: changed class names.
I'm using Fluent NHibernate (v 1.0.0.614) automapping on the following set of classes (where Entity is the base class provided in the S#arp Architecture framework):
public class Car : Entity
{
public virtual int ModelYear { get; set; }
public virtual Company Manufacturer { get; set; }
}
public class Sedan : Car
{
public virtual bool WonSedanOfYear { get; set; }
}
public class Company : Entity
{
public virtual IList<Sedan> Sedans { get; set; }
}
This results in the following Configuration (as written to hbm.xml):
<class name="Company" table="Companies">
<id name="Id" type="System.Int32" unsaved-value="0">
<column name="`ID`" />
<generator class="identity" />
</id>
<bag cascade="all" inverse="true" name="Sedans" mutable="true">
<key>
<column name="`CompanyID`" />
</key>
<one-to-many class="Sedan" />
</bag>
</class>
<class name="Car" table="Cars">
<id name="Id" type="System.Int32" unsaved-value="0">
<column name="`ID`" />
<generator class="identity" />
</id>
<property name="ModelYear" type="System.Int32">
<column name="`ModelYear`" />
</property>
<many-to-one cascade="save-update" class="Company" name="Manufacturer">
<column name="`CompanyID`" />
</many-to-one>
<joined-subclass name="Sedan">
<key>
<column name="`CarID`" />
</key>
<property name="WonSedanOfYear" type="System.Boolean">
<column name="`WonSedanOfYear`" />
</property>
</joined-subclass>
</class>
So far so good! But now comes the ugly part. The generated database tables:
Table: Companies
Columns: ID (PK, int, not null)
Table: Cars
Columns: ID (PK, int, not null)
ModelYear (int, null)
CompanyID (FK, int, null)
Table: Sedan
Columns: CarID (PK, FK, int, not null)
WonSedanOfYear (bit, null)
CompanyID (FK, int, null)
Instead of one FK for Company, I get two!
How can I ensure I only get one FK for Company? Override the automapping? Put a convention in place? Or is this a bug? Your thoughts are appreciated.
I feel your pain.
I did the following at this point in time because i was running out of time
public class JoinedSubclassConvention : IJoinedSubclassConvention
{
public void Apply(IJoinedSubclassInstance instance)
{
switch (instance.EntityType.Name)
{
case "Business":
instance.Key.ForeignKey("FK_Business_Customer");
break;
case "Person":
instance.Key.ForeignKey("FK_Person_Customer");
break;
case "StaffMember":
instance.Key.ForeignKey("FK_StaffMember_Customer");
break;
}
}
}