Cascade ="all" not saving child entities - nhibernate

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.

Related

Hibernate: join with 2 NOT primary key

i want to do a left outer join from 2 table, but with 2 key that are not primary key.
This is the native sql query used:
Maybe there is a better way to do it?
SQLQuery query = session.createSQLQuery("
select {A.*},{B.*} from A_TABLE A left outer join B_TABLE B on A.QUOTE_ID = B.QUOTE_NUM ")
.addEntity("A", A_Class.class)
.addJoin("B", "A.bDocs");
for(Object result : query.list())
{
....
}
The mapping A file:
<class name="A_Class" table="A_TABLE" schema="S">
<id name="rowId" type="string">
<column name="ROW_ID" length="60" />
<generator class="assigned" />
</id>
<set name="BDocs" inverse="true" fetch="select" lazy="false">
<key>
<column name="QUOTE_NUM" length="60" not-null="true" />
</key>
<one-to-many class="B_Class" />
</set>
A_Class.java
public class A_Class implements java.io.Serializable
{
private String rowId;
private String QuoteId;
private Set BDocs= new HashSet(0);
// omitted all the set and get
}
The mapping B file:
<hibernate-mapping>
<class name="B" table="B_TABLE" schema="S">
<id name="rowId" type="string">
<column name="ROW_ID" length="60" />
<generator class="assigned" />
</id>
<many-to-one name="A" class="A_Class" fetch="select" lazy="false" outer-join="true" foreign-key="QuoteId" property-ref="QuoteId">
<column name="QUOTE_NUM" length="60" not-null="true" />
</many-to-one>
B_Class.java
public class B_Class implements java.io.Serializable
{
private String rowId;
private String quoteNum;
// omitted all the set and get
}
From this query i obtain 2 objects, one of A type and the other of B Type with a lot of correct datas but the set BDocs in the object of A type isn't filled. The goal is to get only the A Object with the variable BDocs filled with the B Objects.
I don't understand if the problem is in the query or in the mapping files. Anyone can help me?

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.

HQL Query SELECT with NOT IN

I need a HQL Query but I just dont get it.
SELECT * FROM Poll WHERE pid NOT IN (
SELECT pid FROM votes WHERE uid = 'theuserid')
I want all List of PollĀ“s back where the uid not in the table votes exists.
Also helpfull would be the hql query where the uid in the table votes exists, but I guess this is very similar ;-)
These are the 2 classes:
public class Poll {
private int pid;
private String name;
private String description;
private Date deadline;
private Set<Team> teams = new HashSet<Team>(0);
//some getter & setter
}
public class Vote {
private int vid;
private String uid;
private int pid;
private int tid;
private int votes;
//some getter & setter
}
Can smbdy please help me. I guess it is a join with a WHERE and NOT LIKE but I just dont get it.
Merci!
This is btw the hibernate mapping:
<hibernate-mapping>
<class name="package.model.Poll" table="poll">
<id name="pid" column="pid" >
<generator class="increment"/>
</id>
<property name="name" column="name" />
<property name="description" column="description" />
<property name="deadline" type="timestamp" column="deadline" />
<set name="teams" table="pollchoice"
inverse="false" lazy="false" fetch="select" cascade="all" >
<key>
<column name="pid" not-null="true" />
</key>
<many-to-many entity-name="knt.exceedvote.model.Team">
<column name="tid" not-null="true" />
</many-to-many>
</set>
</class>
</hibernate-mapping>
<hibernate-mapping>
<class name="package.model.Vote" table="votes">
<id name="vid" column="vid" >
<generator class="increment"/>
</id>
<property name="pid" column="pid" />
<property name="uid" column="uid" />
<property name="tid" column="tid" />
<property name="votes" column="votes" />
</class>
</hibernate-mapping>
Please keep in mind, Hibernate is designed using the notion of Object Graph, which is a name given to the relational objects.
That core concept is missing in your mapping (Poll and Vote seem to be isolated) hence I doubt you can use HQL in its current state.
In my opinion, you have two options:
Define the relationship between Poll, pid and Vote, uid. Then you should be able to write simple HQL.
Use native SQL through Hibernate session itself.

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?

Nhibernate query issue

Considering the following mapping file where TemporaryAccessOpenCommand and TemporaryAccessCloseCommand both inherit from base class Command
<class name="Command" table="xxx_Commands" lazy="false">
<id name="Id" type="Int32" unsaved-value="0">
<generator class="identity"/>
</id>
<property name="BeginDate" />
<property name="EndDate" />
<component name="Result" class="CommandResult">
<property name="Status" column="ResultStatus"/>
<property name="Details" column="ResultDetails" />
</component>
<many-to-one name="Requestor" class="xxx.Domain.SessionInfo, xxx.Domain" column="SessionInfoId" lazy="false" />
<joined-subclass name="xxx.TemporaryAccess.Commands.TemporaryAccessCloseCommand, xxx.TemporaryAccess" table="xxx_Commands_TemporaryAccess_Close">
<key column="CommandId"/>
</joined-subclass>
<joined-subclass name="xxx.TemporaryAccess.Commands.TemporaryAccessOpenCommand, xxx.TemporaryAccess" table="xxx_Commands_TemporaryAccess_Open">
<key column="CommandId"/>
<many-to-one name="EndUser" ...... />
<property name="Reason"/>
<property name="AccessRight"/>
<property name="AccessType"/>
<bag name="CloseAccessCommands" cascade="all" lazy="false">
<key column="OpenCommandId"/>
<one-to-many class="xxx.TemporaryAccess.Commands.TemporaryAccessCloseCommand, xxx.TemporaryAccess"/>
</bag>
</joined-subclass>
What would be the nhibernate query to retrieve every OpenAccessCommand having no succeeded CloseAccessCommand ?
I tried this :
public IList<TemporaryAccessOpenCommand> FindOpenCommandsWithoutSucceededCloseCommand()
{
return this.HibernateTemplate.ExecuteFind<TemporaryAccessOpenCommand>(delegate(ISession session)
{
return session.CreateCriteria(typeof(TemporaryAccessOpenCommand))
.CreateCriteria("CloseAccessCommands")
.Add(Expression.Not(Expression.Eq("Result.Status", CommandStatus.Succeeded)))
.List<TemporaryAccessOpenCommand>();
});
}
But it will return an OpenAccessCommand that has tow CloseCommand (one failed and one succeeded) when it should return an empty list.
Thanks for your help (and excuse my poor english)
I managed to get something working this way (I'm open to suggestions if it's not the best way)
public IList<TemporaryAccessOpenCommand> FindOpenCommandsWithoutSucceededCloseCommand()
{
return this.HibernateTemplate.ExecuteFind<TemporaryAccessOpenCommand>(delegate(ISession session)
{
string sql = string.Format(#"
(
SELECT temp_c.OpenCommandId
FROM VSA2_Commands_TemporaryAccess_Close temp_c
INNER JOIN VSA2_Commands c
ON temp_c.CommandId = c.Id
WHERE c.ResultStatus = {0}
) AS OpenCommandId"
, (int)CommandStatus.Succeeded);
var subCriteria = DetachedCriteria.For<TemporaryAccessCloseCommand>();
subCriteria = subCriteria.SetProjection(Projections.SqlProjection(sql, new string[] { "OpenCommandID" }, new IType[] { NHibernateUtil.Int32 }));
return session.CreateCriteria(typeof(TemporaryAccessOpenCommand))
.Add(Expression.Eq("Result.Status", CommandStatus.Succeeded))
.Add(Subqueries.PropertyNotIn("Id", subCriteria))
.List<TemporaryAccessOpenCommand>();
});
}