Hibernate inner join mapping - string where id - nhibernate

I'd like to map the following sql in NHibernate.
Will I need to make a separate entity object i.e RoomTypeVO mapped to tb_tags to do this?
Any help much appreciated.
SELECT
dbo.tb_rooms.id,
dbo.tb_rooms.name,
dbo.tb_tags.name AS 'roomType'
FROM
dbo.tb_rooms
INNER JOIN dbo.tb_tags ON (dbo.tb_rooms.typeID = dbo.tb_tags.id)
<id name="id" column="id">
<generator class="native" />
</id>
<property name="name" />

If you to a straight sql query you do not have to. If you want to use HQL you will have to work with an entity.
But, you can always do sql queries directly.
If you have a mapped entity then you could probably just do something like this:
FROM RoomType

When you refer to 'FROM', are you thinking of something like this?
<property name="totalPrice"
formula="( SELECT SUM (li.quantity*p.price) FROM LineItem li, Product p
WHERE li.productId = p.productId
AND li.customerId = customerId
AND li.orderNumber = orderNumber )"/>

Related

NHibernate many-to-one with lazy false ever executes N queries

I want a list of Purchase searched with some criteria.
Purchase entity contains one Product.
This is the mappings:
<class name="Product" table="Products">
<id name="Id">
<generator class="identity"></generator>
</id>
<property name="Name"></property>
<many-to-one name="Brand" column="BrandId" lazy="false" fetch="join" />
<many-to-one name="Category" column="CategoryId" lazy="false" fetch="join" />
</class>
<class name="Shop" table="Shops">
<id name="Id">
<generator class="identity"></generator>
</id>
<property name="Name"></property>
</class>
<class name="Purchase" table="Purchases" lazy="false">
<id name="Id">
<generator class="identity"></generator>
</id>
<many-to-one name="Product" column="ProductId" lazy="false" fetch="join" />
<many-to-one name="Shop" column="ShopId" lazy="false" fetch="join" />
</class>
And this is the way as I perform the search:
string query = string.Format(#"
select pc from Purchase as pc
inner join pc.Product as p
left outer join p.Brand as b
where
{0} and {1}
and
(
(p.Name like '%{2}%')
or (b.Name like '%{2}%')
)
",
filters.From.HasValue ? "pc.Date >= " + SqlHelper.GetSqlDate(filters.From) : "1=1",
filters.To.HasValue ? "pc.Date <= " + SqlHelper.GetSqlDate(filters.To) : "1=1",
filters.Text);
var list = session.CreateQuery(query)
.List<Purchase>(); //.Future<Purchase>().ToList();
return list;
(I didn't remove other filters on query just to "show" that a simple Get/Query/QueryOver is not possible (it is not readable as a SQL or HQL)
It can be improved... but the problem here is that this code produce 1 query for purchase list and N queries for Product plus M queries for Shop, for example 251 or 770 queries was executed instead of one.
I aspect that just one query was executed, because of lazy="false" on the many-to-one relation.
Why NHibernate make a query on Product (and on Shop) for every Purchase?
Ho can I change the mapping to obtain the execution of one query?
Thanks,
Alessandro
HQL does not respect fetch="join" (in the mapping). I explicit it adding fetch in the join:
select pc from Purchase as pc
inner join **fetch** pc.Product as p
left outer join **fetch** p.Brand as b
... and it works: only one query is executed.

NHibernate inner join gives "Path expected for join"

I have three Tables:
- Person
- User
- PersonSecret
where PersonSecret reference to Person and User:
<class name="PersonSecret" table="PersonSecret" lazy="false" >
<id name="Id" column="Id" type="Guid">
<generator class="assigned"/>
</id>
...
<many-to-one name="Person" class="Person" foreign-key="FK_Person_PersonSecret" lazy="proxy" fetch="select">
<column name="PersonId"/>
</many-to-one>
<many-to-one name="User" class="User" foreign-key="FK_User_PersonSecret" lazy="proxy" fetch="select">
<column name="UserId"/>
</many-to-one>
This is the mapping from User to PersonSecret:
<set name="PersonSecrets" lazy="true" inverse="true" cascade="save-update" >
<key>
<column name="UserId"/>
</key>
<one-to-many class="PersonSecret"/>
And this from Person to PersonSecret:
<set name="PersonSecrets" lazy="true" inverse="true" cascade="save-update" >
<key>
<column name="PersonId"/>
</key>
<one-to-many class="PersonSecret"/>
Now, i try to select all Persons, which has a Entry in PersonSecret for a specific User:
var query = this.Session.CreateQuery(#"from Person a inner join PersonSecret b
where b.UserId = :userId and b.RemindeBirthday = :remind");
This gives me now the ExceptionMessage: "Path expected for join"
Can someone help me, what I am doing wrong? - Thanks.
There are a couple of issues with your HQL query:
When using HQL you need to reference the names of the properties on your model classes instead of the column names they map to. In this case you should reference the PersonSecret.User.Id property in your where clause, instead of the UserId column.
You should also specify the Person.PersonSecrets property in the join clause, otherwise NHibernate won't know which columns to join on.
You should specify your additional filter as a join condition using the with keyword instead of in the where clause.
Here's the correct version:
var query = this.Session.CreateQuery(
#"from Person as a inner join a.PersonSecrets as b with b.User.Id = :userId and b.RemindeBirthday = :remind");
Related resources:
HQL: The Hibernate Query Language - Associations and joins
you have forgotten the "ON" term.
inner join PersonSecret b ON b.PersonId = a.PersonId

Multiple many-to-one to a single table

i have a Product table with two many-to-one references (Title & Description) to a single table named TextRef :
Product:
<many-to-one lazy="false" name="Title" class="TextRef" column="TitleRef" fetch="join" cascade="save-update"/>
<many-to-one lazy="false" name="Description" class="TextRef" column="DescriptionRef" fetch="join" cascade="save-update"/>
every TextRef has a one-to-many to TextRefItem table :
<bag lazy="false" name="Values" table="TextRefItem" cascade="save-update" inverse="true" fetch="join">
<key column="TextId"></key>
<one-to-many class="TextRefItem"/>
</bag>
now i want to load all of TextRefItem(s) for Title & Description in one go but NHibernate only create a join with the first reference (Title)
SELECT this_.ProductId as ProductId7_2_, this_.CategoryId as CategoryId7_2_,
this_.DescriptionRef as Descript3_7_2_,
textref2_.TextId as TextId8_0_, values3_.TextId as TextId4_,
values3_.TextItemId as TextItemId4_, values3_.TextItemId as TextItemId9_1_,values3_.LangId as LangId9_1_,
values3_.Text as Text9_1_, values3_.TextId as TextId9_1_
FROM
Product this_
inner join TextRef textref2_ on this_.DescriptionRef=textref2_.TextId
left outer join TextRefItem values3_ on textref2_.TextId=values3_.TextId
WHERE this_.ProductId = 1
for the other one(Description) it makes a separate select query
how could i tell NHibernate to avoid that ?
I don't know why a separate query is made for the second reference (Description). But it is possible to extend fetching strategies via criterias to load a product with all TextRefs and TextRefItems in one shot:
var criteria = session.CreateCriteria(typeof(Product))
.SetFetchMode("Description.Values", FetchMode.Join);
criteria.SetResultTransformer(new DistinctRootEntityResultTransformer());
criteria.Add(Restrictions.Eq("ProdId", 1));
var list = criteria.List<Product>();

sql alias to dictionary returns duplicate results

I have following class
<class name="Product" table="Product">
<id name="ID" />
...
<map name="CustomFields" table="CustomFieldView">
<key column="RECORDID" />
<map-key column="CFName" type="String" />
<element column="CFValue" type="String" />
</map>
</class>
and SP to select product with CustomFields dictionary
<sql-query name="GetProducts">
<return alias="p" class="Product" />
<return-join alias="cf" property="p.CustomFields" />
SELECT {p.*}, {cf.*}
FROM Product p
INNER JOIN CustomFieldView cf ON p.ID = cf.RECORDID
// WHERE
</sql-query>
when I select single product like WHERE ID = 1234, then it works as expected - returns one Product with populated CustomFields Dictionary property.
But when I select not single Product like WHERE ID IN (18780, 21642) or other criterias then I get Products duplicated 'CustomFields.Count' times, e.g. 2 Products and each has 20 Custom Fields, then 40 Products and each has 20 valid custom fields.
Do I missed something in mapping ?
You are returning a Cartesian product and therefore your product is being returned x times for every custom field.
To get around this problem you will need to use something like:-
var query = Session
.GetNamedQuery("GetProducts")
.SetResultTransformer(new DistinctRootEntityResultTransformer());
return query.List<Product>();
Please note that you will send all the data down the wire and NHibernate will perform the distinct transformer client (meaning web server or desktop app) side.
I am not 100% sure if the return join will be populated as I have never done things this way, you will need to test this.
edit
I think you fetching strategy is not quite right. Do you really need a <sql-query...> Could you use another strategy e.g. HQL?

NHibernate: Criteria expression to retrieve all entities with null count child collection

In nhibernate, I have two classes that are associated with a many-to-one mapping:
<class name="Employee" table="Employee">
..
<bag name="orgUnits">
<key column="id" />
<one-to-many name="OrgUnit" class="OrgUnit">
</bag>
..
</class>
I would like to use a criteria expression to get only Employees where the the collection is null (ie no orgunits) , something like this :
IList employeesWithNoOrgUnit = sess.CreateCriteria(typeof(Employee))
.Add( Expression.IsNull("OrgUnits") )
.List();
This doesn't filter the collection as I expect.
Colleague just found one way that works.
IList employeesWithNoOrgUnit = sess.CreateCriteria(typeof(Employee))
.Add( Restrictions.IsEmpty("OrgUnits") )
.List();