Select unsaved object out of the session - nhibernate

Using NHibernate 2.1 on mssql with C#.
I have 2 diffrent classes witch use an "group" object.
<class name="OrderCode" table="OrderCode" polymorphism="explicit">
<id name="Id" column="ID">
<generator class="native" />
</id>
...
<many-to-one class="Group" name="Group" column="GRUPPE" cascade="all" />
...
and
<class name="Alotment" table="Alotment">
<id name="Id" column="ID">
<generator class="native" />
</id>
...
<many-to-one class="Group" name="Group" column="GRUPPE" cascade="all" />
...
An import script now creates first the OrderCode and select the group, if the group not exist it makes an new one. Later in the same script I create the alotment. Same way, select if there is the right group, if not the script create a group.
Sometimes OrderCode and Alotment need the same group, but if the group dosen't exist, the script creates two groups cause the secend select don't get the group out off the session.
I know its the same session. Is there a way to let IQuery, ICriteria or Session.Linq select not commit groups out of the session?

As far as I know if you save or update an entity using a session you should flush or commit your changes before getting it back from the same session.
Can't you use group object reference like this?
using(var session=SessionFactory.OpenSession())
{
var group=(Group)session.CreateQuery(group_query_string).UniqueResult;
if(group==null)
{
group=new Group();
session.SaveOrUpdate(group);
}
...
var orderCode=new OrderCode{Group=group};
session.SaveOrUpdate(orderCode);
...
// and later in your code
var alotment=new Alotment{Group=group};
session.SaveOrUpdate(alotment);
....
session.Flush();
}

Related

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.

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 and concurrency check

I want to achieve concurrency check using nHibernate 3 using UnitOfWork pattern.
To be more precise:
open new session session,
load entity in a session,
close session,
give user some time to edit data in loaded entity,
open new session,
update data
close session.
I'm using timestap to version entity.
Here is my mapping file
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="...."
namespace="...."
default-lazy="false">
<class name="Employee"
optimistic-lock="version"
dynamic-update="true">
<id name="Id">
<generator class="native" />
</id>
<version column="LastEditDate" generated="always" type="timestamp" />
<property name="Name" not-null="1" length="255" />
<property name="LastEditUser" not-null="1" length="255"/>
</class>
</hibernate-mapping>
I have no idea how to update entity in session context
var entity = <updated by user>
using (var session = GetNewSession())
{
//todo: load current version value / attach entity to context
session.SaveOrUpdate(entity);
//if concurency check fails, StaleObjectException (or similar) is expected to be thrown
}
In SQL it should work like this
UPDATE ENTITY SET LastEditDate = #P1, ... WHERE ID = #P2 AND LastEditDate = #P3
where:
#P1 - new LastEditDate
#P2 - entity ID
#P3 - previous LastEditDate
If ROWSMODIFIED = 1 then update was successfull, else if = 0 then ConcurrencyException
Using Linq2Sql it was very simple: create versioning column, attach entity to new session context and try to update.
How can I do it in nHiberate? Is it supported?
session.Update(entity)
should be enough.
I think you should use session.Lock(entity,LockMode.Update).

NHibernate.PropertyValueException : not-null property references a null or transient

I am getting the following exception.
NHibernate.PropertyValueException : not-null property references a null or transient
Here are my mapping files.
Product
<class name="Product" table="Products">
<id name="Id" type="Int32" column="Id" unsaved-value="0">
<generator class="identity"/>
</id>
<set name="PriceBreaks" table="PriceBreaks" generic="true" cascade="all" inverse="true" >
<key column="ProductId" />
<one-to-many class="EStore.Domain.Model.PriceBreak, EStore.Domain" />
</set>
</class>
Price Breaks
<class name="PriceBreak" table="PriceBreaks">
<id name="Id" type="Int32" column="Id" unsaved-value="0">
<generator class="identity"/>
</id>
<property name="ProductId" column="ProductId" type="Int32" not-null="true" />
<many-to-one name="Product" column="ProductId" not-null="true" cascade="all" class="EStore.Domain.Model.Product, EStore.Domain" />
</class>
I get the exception on the following method
[Test]
public void Can_Add_Price_Break()
{
IPriceBreakRepository repo = new PriceBreakRepository();
var priceBreak = new PriceBreak();
priceBreak.ProductId = 19;
repo.Add(priceBreak);
Assert.Greater(priceBreak.Id, 0);
}
Following up on Jan reply. I've removed the ProductId from priceBreak map. This works!!
public int AddPriceBreak(Product product, PriceBreak priceBreak)
{
using (ISession session = EStore.Domain.Helpers.NHibernateHelper.OpenSession())
using (ITransaction transaction = session.BeginTransaction())
{
product.AddPriceBreak(priceBreak);
session.SaveOrUpdate(product);
transaction.Commit();
}
return priceBreak.Id;
}
Remove the ProductId property from the mapping and from the PriceBreak class. And use the PriceBreaks collection to add PriceBreaks, you don't need the PriceBreakRepository, but only a ProductRepository.
Example:
using (var session = sessionFactory.OpenSession())
{
using (var tx = session.BeginTransaction())
{
var product = session.Get<Product>(19);
product.AddPriceBreak(new PriceBreak());
tx.Commit();
}
}
And in the Product:
class Product
{
// ...
public void AddPriceBreak(PriceBreak pb)
{
pb.Product = this;
PriceBreaks.Add(pb);
}
}
Your usage of Id properties along with the actual references is incorrect.
First, remove this line:
<property name="ProductId" column="ProductId" type="Int32" not-null="true" />
Then, instead of assigning ProductId (you should remove that property completely), use:
priceBreak.Product = session.Load<Product>(19);
(You might need to add the Load method to your repository).