many-to-one and where clause - nhibernate

I am working on a legacy database which is quite intricate.
The table customers is shared with the suppliers and who created this structure used a flag to identify the customers.
Since I am only interested in working with records defined as customers I've added a where clause to my mapping:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="MyAssembly" namespace="MyAssembly.Domain">
<class name="Customer" table="ANSADID" mutable="false" where="ANFCLI = 'Y'">
<composite-id>
<key-property name="CustomerCode" column="ANCOCO" type="String" length="10"></key-property>
<key-property name="Company" column="ANCOSO" type ="String" length="5"></key-property>
</composite-id>
<property name="Name" column="ANINCO" type="String" length="100"></property>
</class>
</hibernate-mapping>
As you can see I've pre-filtered all my customers with this clause: ANFCLI = 'Y'
Everything works perfectly fine if I query customers (the where clause is used):
var customers = session.QueryOver<Domain.Customer>()
.Where(t => t.Company == "ABC01")
.List();
But if I query the orders table - where I've got a many-to-one association:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="MyAssembly" namespace="MyAssembly.Domain">
<class name="Order" table="OCSAORH" mutable="false" where="OCHAMND = 0">
<composite-id>
<key-property name="Number" column="OCHORDN" type="String" length="10"></key-property>
<key-property name="Ver" column="OCHAMND" type="Int32"></key-property>
<key-property name="Company" column="OCHCOSC" type="String" length="5"></key-property>
</composite-id>
<many-to-one name="Customer" class="Customer" lazy="proxy" fetch="join">
<column name="OCHCLII" not-null="true"/>
<column name="OCHCOSC" not-null="true"/>
</many-to-one>
</class>
</hibernate-mapping>
the filter on the entity customers is lost.
I was reading somewhere that the where clause doesn't work on a association and you have to use a where clause on the collection (bag, set, etc etc) but, how can I do that with a many-to-one?
Thanks for you help.

What about mapping Customer using a discriminator using ANFCLI and then setting the discriminator value to 'Y'. I think NHibernate will treat this a little more rigourously than a where clause.
<class name="Customer" table="ANSADID" mutable="false" discriminator-value="Y">
<composite-id>
<key-property name="CustomerCode" column="ANCOCO" type="String" length="10" />
<key-property name="Company" column="ANCOSO" type ="String" length="5" />
</composite-id>
<discriminator column="ANFCLI" />
<property name="Name" column="ANINCO" type="String" length="100" />
</class>

I think degorolls is right:
You would need to have a super class called "Person", and two sub types called "Customer" and "Supplier".
Then you set your mapping so it uses the ANFCLI field as a discriminator, with the Y value for Customer and the N value for Supplier.
This way, you'll have a nice and transparent polymorphism.
(you'll be able to do stuff like "from Customer", or "from Supplier", and that will automagicaly add the where clause).
Hope that helps!

I'm also a newbie using NHibernate but perhaps you can map that relation (Order to Customer) using a bag (as if it would be one to many)!

Related

What does discriminator used for?

Forgive me I am a newbie in NHibernate, I found some example in hbm.xml like below.
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="test" >
<class name="test.FieldSetMapping" table="fieldsetmapping" discriminator-value="2352" lazy="false">
<id name="Id" column="fieldsetmapping_id" type="Int64">
<generator class="test.NHibernate.IdGenerator, test">
<param name="table">nextinstanceid</param>
<param name="column">next</param>
<param name="max_lo">9</param>
</generator>
</id>
<discriminator column="mapping_type" type="Int32" />
<version name="LastUpdateNumber" column="last_update_number" type="Int32" unsaved-value="-1"/>
<property name="OwnerName" column= "owner_name" type="String"/>
<component name="FieldSetDefinitionId" class="test.ObjectId">
<property name="InstanceId" column= "fieldsetdefinition_id" type="Int64"/>
</component>
<property name="OwnerTypeClassId" column= "owner_type" />
<bag name="FieldMappings" cascade="all-delete-orphan" generic="true" lazy="false">
<key column="fieldsetmapping_id" not-null="true" />
<one-to-many class="test.FieldSet.FieldMapping, test" />
</bag>
<property name="MappingHandlerClass" column="handler_class" />
</class>
<subclass name="test.EntityFieldSetMapping, test" discriminator-value="2353" extends="test.FieldSetMapping, test" lazy="false" >
<property name="TargetEntityType" type="test.Internal.NHibernate.ClassIdType, test" not-null="false">
<column name="target_entity_type"/>
</property>
</subclass>
<class ...>
...
</class>
<class ...>
...
</class>
</hibernate-mapping>
But I don't know what does discriminator mean . I check the Nhibernate Doc 5.1.6.
The <discriminator> element is required for polymorphic persistence
using the table-per-class-hierarchy mapping strategy and declares a discriminator
column of the table. The discriminator column contains marker values that tell
the persistence layer what subclass to instantiate for a particular row.A restricted
set of types may be used: String, Char, Int32, Byte, Short, Boolean, YesNo, TrueFalse.
Does it mean if the mapping_type > 2352 the NH will initialize the subclass test.EntityFieldSetMapping for the row of the table fieldsetmapping? thanks.
NHibernate will use the discriminator to detect which class needs to instantiate on a polymorphic scenario.
If mapping_type = 2352, it will create an instance of test.FieldSetMapping
if mapping_type = 2353, it will create an instance of test.EntityFieldSetMapping
Any other value should generate an exception.

Another NHibernate Mapping Mystery! Getting count in hbm file

I am trying to retrieve the count of items allocated to a container in my hbm file. I've done a bit of digging and managed to get my hbm code this far (below!). I want the count to be retrieved every time a container object is queried. I could use an interceptor but I assume there's a better way. Am I on the right track or should I use a different strategy to get the count loaded up?
Thanks.
P.S. We're using NH v2.2
<?xml version="1.0" encoding="utf-8"?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-lazy="false" assembly="MyEntities" namespace="Entities.Containers"> <class name="Container" table="[Container]" xmlns="urn:nhibernate-mapping-2.2">
<id name="Id" column="Id" type="Int32" unsaved-value="0">
<generator class="native" />
</id>
<property name="Capacity" column="Capacity">
<column name="Capacity" />
</property>
<property name="Description" column="Description" length="50" type="String">
<column name="Description" />
</property>
<loader query-ref="sqCurrentContainerAllocation"/> </class>
<sql-query name="sqCurrentContainerAllocation">
<return-scalar column="AllocatedItemsCount" type="int"></return-scalar>
SELECT COUNT(*) FROM [ContainerTracking]
WHERE [ContainerId] = :Id </sql-query>
</hibernate-mapping>
If you need to get some calculated property, you can use mapping with formula.
Let's extend your C# class:
public class Container
{
... // ID, Capacity, Description
public virtual int MyCount { get; set; }
And extend your mapping
<class name="Container" table="[Container]"
...
<property name="MyCount" insert="false" update="false" >
<formula>
(
SELECT count(*)
FROM [ContainerTracking] as ct
WHERE ct.[ContainerId] = Id
)
</formula>
</property>
the Id will be replaced with somethink like '_this.Id', the name of the column Id and its alias
This will of course load the count all the time (except projections) so think twice before use it

strange bug when loading entities in NHibernate

I am getting an error in NHibernate.Collection.PersistentBag class when trying to load entities:
The value "MyProject.DomainModel.Operator" is not of type "MyProject.DomainModel.Operator" and cannot be used in this generic collection.
Notice that both value types are exactly the same. I've double-checked them in a comparer tool.
NHibernate is failing to add the value to a List collection at line bag.Add(element) .
The element variable is actually of type *object{DecoratorAopProxy_9cf850624c7e4ef9a8e2d9694bed26fd}*. I've noticed that the objects that can be successfully added to this list are of type object{MyProject.DomainModel.Operator}. This type is obtained from "quick watch" feature in VS2012 from the "Type" column.
Does anyone have an idea as to why NHibernate changes the type of this particular object to a proxy while others have pure entity types?
<class name ="PersonRole" table ="tblPersonRole" mutable ="false">
<id name="Id" column="PersonRoleID" type="Int32" access ="nosetter.lowercase-underscore">
<generator class="native"/>
</id>
<discriminator formula="case when RoleID in (2,4,5,6) THEN RoleID ELSE 0 END" />
<subclass discriminator-value="4" name="AccountManagerRole">
<bag name="Operators" >
<key column="OperatorID"></key>
<one-to-many class="BaseOperator"/>
<loader query-ref="LoadAllocatedOperators_ACCOUNTMANAGER"/>
</bag>
</subclass>
</class>
<class name="BaseOperator" table="tblOperator" lazy="true" >
<id name="Id" column="OperatorID" access ="nosetter.lowercase-underscore" type="Int32" unsaved-value="null">
<generator class="native" />
</id>
<discriminator column="OperatorType" type="string" />
<subclass discriminator-value ="OPR" name ="Operator" lazy="true">
<bag name="Customers" access="nosetter.camelcase-underscore" lazy="true" cascade="all-delete-orphan" inverse="true" fetch="join" >
<key column="OperatorId" />
<one-to-many class="MyProject.DomainModel.Customer, MyProject" not-found="ignore" />
</bag>
<subclass discriminator-value ="OPR2" name ="Operator2" lazy="true" />
</subclass>
</class>
So I do something like "select distinct accManager from AccountManagerRole accManager", which results in operators being loaded one at a time using their ID, and NHibernate crashes on one of them.

Cascade ="all" not saving child entities

I think the object model below is saying a Party and PartyName to have a many to one relatioship. An I think the cascade=all i the Party.hbm sshould be having NHib save the child PartyName(s).
But it clearly isn't...
Can someone explain why PartyName isn't being saved with Party and what to do to fix?
Cheers,
Berryl
MAPPING
<class name="Party" table="Parties">
<id name="Id">
<column name="PartyId" />
<generator class="hilo" />
</id>
<discriminator column="Type" not-null="true" type="string" />
<set access="field.camelcase-underscore" cascade="all" inverse="true" name="Names">
<key foreign-key="Party_PartyName_FK">
<column name="PartyNameId" />
</key>
<one-to-many class="Parties.Domain.Names.PartyName, Parties.Domain" />
</set>
<subclass
name="Smack.Core.TestingSupport.NHibernate.TestableDomain.SomeDopeyDomainModel.Student, Smack.Core.TestingSupport"
discriminator-value="Student"
>
<property name="Number" />
<many-to-one
class="Smack.Core.TestingSupport.NHibernate.TestableDomain.SomeDopeyDomainModel.Course, Smack.Core.TestingSupport"
foreign-key="Course_FK"
name="Course">
<column name="CourseId" index="CourseIndex" />
</many-to-one>
</subclass>
<many-to-one access="field.camelcase-underscore" class="Parties.Domain.Party" foreign-key="Party_FK" name="Party">
<column name="PartyId" index="PartyIndex" not-null="true"/>
</many-to-one>
<property name="TheRequiredName" not-null="true" length="50"/>
<property name="EverythingElse" />
<property name="ContextUsed" length="50"/>
<property name="Salutation" length="20"/>
<property name="EffectivePeriod" type="Smack.Core.Data.NHibernate.UserTypes.DateRangeUserType, Smack.Core.Data">
<column name="EffectiveStart"/>
<column name="EffectiveEnd"/>
</property>
Failing Test (and output)
[Test]
public void CanSaveAndLoad_AllProperties()
{
var partyName = NameSeeds.DevName;
partyName.Party = _party;
Assert.That(_party.Names.First(), Is.EqualTo(partyName));
using (var tx = _session.BeginTransaction())
{
_session.Save(_party);
tx.Commit();
}
_session.Clear();
Party foundParty;
using (var tx = _session.BeginTransaction())
{
foundParty = _session.Get<Party>(_party.Id); *** <=== name s/b saved!!
tx.Commit();
}
PartyName foundName = foundParty.Names.First();
//found.Look();
Assert.That(foundName, Is.EqualTo(partyName));
Assert.That(foundName.Party, Is.Not.Null);
Assert.That(foundName.TheRequiredName, Is.EqualTo(partyName.TheRequiredName));
Assert.That(foundName.EverythingElse, Is.EqualTo(partyName.EverythingElse));
Assert.That(foundName.ContextUsed, Is.EqualTo(partyName.ContextUsed));
Assert.That(foundName.Salutation, Is.EqualTo(partyName.Salutation));
Assert.That(foundName.EffectivePeriod, Is.EqualTo(partyName.EffectivePeriod));
}
NHibernate: INSERT INTO Parties (Type, PartyId) VALUES ('Parties.Domain.Party', #p0);#p0 = 32768 [Type: Int32 (0)]
NHibernate: SELECT party0_.PartyId as PartyId2_0_, party0_.Number as Number2_0_, party0_.CourseId as CourseId2_0_, party0_.Type as Type2_0_ FROM Parties party0_ WHERE party0_.PartyId=#p0;#p0 = 32768 [Type: Int32 (0)]
With the mapping of the Names <set> inverse=true, you will have to explicitly call session.Save(partyNameObject) on each member of the collection. If you are looking to have NHibernate automatically save the members of the set when the PartyObject is saved, you need to change the Names <set> inverse attribute to inverse=false. This tells Nhibernate that you want Party to control the relationship between Party and PartyName. You must also remember to add each partyNameObject to the Party.Names collection. Otherwise, they won't be saved when you call Session.Save(partyObject). Keep in mind that having the parent control the relationship may be handy, but if you happen to save the PartyObject without having Loaded the PartyNames collection, NHibernate will update their Party FK to Null. In this scenario with certain Cascade options set on the Names <set>, you might find Nhibernate Deleting them as well.

Nhibernate one-to-many with table per subclass

I am customizing N2CMS's database structure, and met with an issue. The two classes are listed below.
public class Customer : ContentItem
{
public IList<License> Licenses { get; set; }
}
public class License : ContentItem
{
public Customer Customer { get; set; }
}
The nhibernate mapping are as follows.
<class name="N2.ContentItem,N2" table="n2item">
<cache usage="read-write" />
<id name="ID" column="ID" type="Int32" unsaved-value="0" access="property">
<generator class="native" />
</id>
<discriminator column="Type" type="String" />
</class>
<subclass name="My.Customer,My" extends="N2.ContentItem,N2" discriminator-value="Customer">
<join table="Customer">
<key column="ItemID" />
<bag name="Licenses" generic="true" inverse="true">
<key column="CustomerID" />
<one-to-many class="My.License,My"/>
</bag>
</join>
</subclass>
<subclass name="My.License,My" extends="N2.ContentItem,N2" discriminator-value="License">
<join table="License" fetch="select">
<key column="ItemID" />
<many-to-one name="Customer" column="CustomerID" class="My.Customer,My" not-null="false" />
</join>
</subclass>
Then, when get an instance of Customer, the customer.Licenses is always empty, but actually there are licenses in the database for the customer. When I check the nhibernate log file, I find that the SQL query is like:
SELECT licenses0_.CustomerID as CustomerID1_,
licenses0_.ID as ID1_,
licenses0_.ID as ID2_0_,
licenses0_1_.CustomerID as CustomerID7_0_,
FROM n2item licenses0_
inner join License licenses0_1_
on licenses0_.ID = licenses0_1_.ItemID
WHERE licenses0_.CustomerID = 12 /* #p0 */
It seems that nhibernate believes that the CustomerID is in the 'n2item' table. I don't know why, but to make it work, I think the SQL should be something like this.
SELECT licenses0_.ID as ID1_,
licenses0_.ID as ID2_0_,
licenses0_1_.CustomerID as CustomerID7_0_,
FROM n2item licenses0_
inner join License licenses0_1_
on licenses0_.ID = licenses0_1_.ItemID
WHERE licenses0_1_.CustomerID = 12 /* #p0 */
Could any one point out what's wrong with my mappings? And how can I get the correct licenses of one customer? Thanks in advance.
I'm not sure whether the SQL is incorrect, because the parent class mapping uses a discriminator so I'd expect all properties to be stored in the same table as the base class (n2item). However I'm not familiar with the "join table" syntax, I generally use joined-subclass so I might be misunderstanding.
Assuming the subclass mapping is correct, could the problem with the licenses be something to do with no Cascade setting being set for that collection?