How to write a Hibernate query using the WITH clause to customize the ON clause - sql

I am using the following HQL query:
feeds = (List<Album>) session.createQuery(
"select distinct album from Album as album "
+ "left join album.postImageses as pi with pi.isAlbumCover=:isCover "
+ "where album.atom.id=:aid ")
.setParameter("isCover", "Yes")
.setParameter("aid", id)
.list();
This query is working properly.
But problem is that I want to fetch only Images frompostImageses whose isAlbumCover value is 'Yes'
but above query is fetching all images(images are album cover or not).
So how can I fetch only images(not other) with isAlbumCover='Yes'.
How to resolve this.

Hibernate cannot give you partial one-to-many results. You always get all children to ensure consistency.
You need to turn your query up-side down:
select ps
from PostImage ps
inner join fetch ps.album albm
inner join fetch albm.atom atm
where ps.isAlbumCover = :isCover and atm.id = :aid
This query assumes you have a bi-directional association between an Album and a PostImage, so a PostImage has a #ManyToOne album reference too.
For a proof this is not possible, this is what the Hibernate documentation says:
A fetch join does not usually need to assign an alias because the
associated objects should not be used in the where clause (or any
other clause).
So the fetch is not affected by where clauses. In your case the with clause can at most filter the root entity (Album), but all associated collections (postImageses) will be subject to the fetching policy.
Because you don't explicitly use a fetch directive, it means the query will use the mapping fetch policy. If the one-to-many association is LAZY, it won;t be initialized in this query. Once you access it, Hibernate will issue a new query to initialize it, that's why the with clause doesn't filter the children collection.

Related

I need to use join fetch, but it leads to "query specified join fetching, but the owner of the fetched association was not present in the select list"

I have the following query:
#Query("SELECT new de.projectemployee.ProjectEmployee(employee) " +
"FROM ProjectEmployee projectEmployee JOIN FETCH projectEmployee.employee employee " +
"WHERE projectEmployee.project.id = :id")
List<ProjectEmployee> findInOneRequestByProjectId(#Param("id") long id);
And I get the following error:
org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list
As many solutions suggest, I can replace JOIN FETCH with JOIN. But the problem with this solution is that it leads to n+1 requests. Is there another way to fix the error?
Fetch is used to populate lazy load field in object, it has no use here. Just remove FETCH from your query and it will work.

How to select one table using NHibernate CreateCriteria

How can I create the following SQL statement in Nhibernate using CreateCriteria:
SELECT distinct top 20 a.* from ActivityLog a
left join WallPost w on a.ActivityLogId = w.ActivityLogId left join ItemStatus i on i.StatusId = w.ItemStatus
I always tend to get all columns from all tables returned in the sql statement producing duplicates even though I map it to the ActivityLog table. I am also doing paging as the code below shows:
ICriteria crit = nhelper.NHibernateSession.CreateCriteria(typeof(Model.ActivityLog), "a").CreateAlias("a.WallPosts", "w",CriteriaSpecification.LeftJoin)
.CreateAlias("w.ItemStatus", "i", CriteriaSpecification.LeftJoin)
.SetMaxResults(pageSize).SetFirstResult(startRow).AddOrder(Order.Desc("a.Date"));
Thanks
H
Sounds like you have set lazy loading to false in your mapping files meaning that all the associations and child collections are being loaded up too. Can you verify that?
You ask "How to select one table using NHibernate CreateQuery" (HQL). In that case you can choose what to fetch using select.
In your text you're using criteria. AFAIK, you cannot directly control what columns you want to read. However, if you create a DetachedCriteria for the join, this won't be fetched.

NHibernate cannot use results of containstable() query

I'm in the process of converting a LINQ2SQL project to use NHibernate. One query that I have not been able to port effectively is a full-text search. Previously I was using a stored procedure to accomplish the search but now I'm accessing it as a query directly. The SQL query is:
select a.id, a.parentID, a.text, a.summary
from articles a
inner join containstable(articles, (summary, text), :query) ar on
a.id = ar.[KEY]
order by ar.RANK desc
When I run the query in SSMS I get the results I expect - just the matching items of the articles table. However, NHibernate does not seem to be able to interpret the results. The code I query with is:
IQuery q = session.CreateSQLQuery(
"select a.id, a.parentID, a.[text], a.summary from articles a "
+ "inner join containstable(articles, (summary, text), :query) ar on "
+ "a.id = ar.[KEY] "
+ "order by ar.RANK desc")
.SetParameter<string>("query", query);
var results = q.List<article>();
NHibernate throws an error that System.Object[] cannot be converted to my POCO type. I tried the non-generic List() method and discovered that each item in that list was an array of objects. Other queries on the table work fine, however this is the only SQL query (everything else is Criteria or QueryOver). Why won't this query work?
You need to tell NH what those rows represent.
session.CreateSQLQuery(...)
.AddEntity(typeof(Article))
.SetParameter(...)
.List<Article>();
Read Chapter 16. Native SQL for more details
If you are running the query directly as SQL then NHibernate will have no knowledge of how to map it to your POCOs even if the tables are mapped. You need to supply NHibernate some information about how the SQL projection should be mapped.
Use HQL or LinqToNHibernate if this is an option open to you.

nhibernate cross table query optimization

I have a query I've written with NHibernate's Criteria functionality and I want to optimize it. The query joins 4 tables. The query works, but the generated SQL is returning all the columns for the 4 tables as opposed to just the information I want to return. I'm using SetResultTransformer on the query which shapes the returned data to an Individual, but not until after the larger sql is returned from the server.
Here's the NHibernate Criteria
return session.CreateCriteria(typeof(Individual))
.CreateAlias("ExternalIdentifiers", "ExternalIdentifier")
.CreateAlias("ExternalIdentifier.ExternalIdentifierType", "ExternalIdentifierType")
.CreateAlias("ExternalIdentifierType.DataSource", "Datasource")
.Add(Restrictions.Eq("ExternalIdentifier.Text1", ExternalId))
.Add(Restrictions.Eq("ExternalIdentifierType.Code", ExternalIdType))
.Add(Restrictions.Eq("Datasource.Code", DataSourceCode))
.SetResultTransformer(new NHibernate.Transform.RootEntityResultTransformer());
And the generated sql (from NHProfiler) is
SELECT (all columns from all joined tables)
FROM INDIVIDUAL this_
inner join EXTERNAL_ID externalid1_
on this_.INDIVIDUAL_GUID = externalid1_.GENERIC_GUID
inner join EXTERNAL_ID_TYPE externalid2_
on externalid1_.EXTERNAL_ID_TYPE_GUID = externalid2_.EXTERNAL_ID_TYPE_GUID
inner join SYSTEM_SRC datasource3_
on externalid2_.SYSTEM_SRC_GUID = datasource3_.SYSTEM_SRC_GUID
WHERE externalid1_.EXTERNAL_ID_TEXT_1 = 96800 /* #p0 */
and externalid2_.EXTERNAL_ID_TYPE_CODE = 'PATIENT' /* #p1 */
and datasource3_.SYSTEM_SRC_CODE = 'TOUCHPOINT' /* #p2 */
I only want the columns back from the Individual table. I could set a projection, but then I lose the Individual type.
I could also rewrite this with DetachedCriteria.
Are these my only options?
I had exactly the same question and asked one of NHibernate development team members directly, here is the answer I got:
with the Criteria API, the only thing you could do is to use a projection list and include all of the properties of the root entity... then you would need to use the AliasToBeanResultTransformer. Far from an optimal solution obviously
if you don't mind rewriting the query with hql, then you can do it very easily. an hql query that says "from MyEntity e join e.Association" will select both the entity columns as well as the association's columns (just like the problem you're having with criteria). But an hql query that says "select e from MyEntity e join e.Association" will only select the columns of e.

NHibernate : Count childrens' children

I have an entity type A. Which has many B's. The B entity has many C's.
I need to count how many C's does an A entity have. How can this be done using NHibernate Criteria API?
Using LINQ to NHibernate I was unable to get results since it throws an exception (see this question)
This query becomes simpler if you use C as starting point of the query, rather than A. This is possible since you use bidirectional mappings, according to the mapping you show in your other question.
The way I would do this is to find all Cs which have a B which has a given A, and then count the found Cs.
To Add a constraint on the B of a C, you can add an alias and then add restrictions to that alias. To perform a count query rather than returning the found Cs you can use the SetProjection method and specify a Count projection. Since the count projection returns a single integer value, use UniqueResult to get the count.
using (ISession session = SessionFactorySingleton.OpenSession())
{
int numberOfCsForA1 = session.CreateCriteria<C>()
.SetProjection(Projections.Count("Id"))
.CreateAlias("B", "b")
.Add(Restrictions.Eq("b.A.Id", a1.Id))
.UniqueResult<int>();
// ...
}
The SQL generated by this query looks like this:
SELECT count(this_.Id) as y0_
FROM [C] this_
inner join [B] b1_
on this_.IdB=b1_.Id
WHERE b1_.IdA = #p0;#p0 = 12
As you can see, it is a two-way join, since NHibernate is smart enough to realize that it does not need to join with the A table to get the id of a Bs A. Instead it simply looks at the IdA value of the B.