NHibernate Queryover - Is there any way of getting better SQL out of nHibernate when retrieving this object plus collections graph?

╄→гoц情女王★ 提交于 2019-12-25 08:48:42

问题


We have a situation where we are trying to retrieve a couple of levels deep for an object graph using QueryOver. So if our top level is ParentEntity and our children are ChildEntitysA, ChildEntitysB and ChildEntitysC then we have something like the following code to retrieve our graph.

var query = session.QueryOver<ParentEntity>(() => pAlias).Where(() => pAlias.Id == key).Future<ParentEntity>();

var queryA = session.QueryOver<ParentEntity>(() => pAlias).Left.JoinAlias(x => x.ChildEntitysA, () => caAlias).Where(() => pAlias.Id == key).Future<ParentEntity>();

var queryB = session.QueryOver<ParentEntity>(() => pAlias).Left.JoinAlias(x => x.ChildEntitysB, () => cbAlias).Where(() => pAlias.Id == key).Future<ParentEntity>();

var queryC = session.QueryOver<ParentEntity>(() => pAlias).Left.JoinAlias(x => x.ChildEntitysC, () => ccAlias).Where(() => pAlias.Id == key).Future<ParentEntity>();

return query.ToList();

This should generate 4 SQL statements in one single call to the database and brings back what we want. However, the SQL it generates does have some inefficiency as all of the child queries will contain in the SELECT statement all the columns of the parent as well as the child entity's columns, something akin to:

SELECT parent.col0, parent.col1, parent.col2, parent.col3, .... parent.col99 
FROM parent WHERE .....

SELECT  parent.col0, parent.col1, parent.col2, parent.col3, .... parent.col99, childA.col0, childA.col1, childA.col2 .... childA.col99 
FROM parent LEFT OUTER JOIN childA ON ....

SELECT  parent.col0, parent.col1, parent.col2, parent.col3, .... parent.col99, childB.col0, childB.col1, childB.col2 .... childB.col99 
FROM parent LEFT OUTER JOIN childB ON ....

SELECT  parent.col0, parent.col1, parent.col2, parent.col3, .... parent.col99, childC.col0, childC.col1, childC.col2 .... childC.col99 
FROM parent LEFT OUTER JOIN childC ON ....

yet the query generated for the parent entity already contains all the necessary data! This does become a bit of an efficiency concern if we're returning multiples of the parent and the volume of data per child query rises accordingly (especially as the entities involved are quite large in terms of the number of underlying columns).

So, is there any way we can force nHibernate to not generate SQL to return all the parent entity's values for every single part of the query and just limit it to returning minimal key columns only in the SQL? Is Queryover even the optimal API for this situation?


回答1:


The way I would suggest, is to use full power of NHibernate features in these combinations:

  • The first is a complex query.
  • The second is to profit from lazy loading and batch-size setting

So, in the first case we are about to create one and only one QueryOver (maybe including some subqueries)

It must contain projections, that's essential. it can include (many) Left, Inner joins, can be deeply filtered... We will then get the narrowed SELECT statement, containing only required fields. This results in one SQL query, which must be converted into some (unmapped) DTO object

The Projections and DTO means, that at the end we have to use ResultTransformer, which will convert all coming selected fields into DTO object properties.

In the second case, we are trying to profit from lazy behaviour and batch-size mapping. It could look like this:

1) class/ entity

<class name="ParentEntity" batch-size="25" lazy="true">
...

2) collection

 <bag name="Children" lazy="true" batch-size="25"
    ...

See more in documentation: 19.1.5. Using batch fetching

What we gain here is, that we can create soft queries returning only light objects (these which were mapped). This is the first Select. Then all the properties (many-to-one, one-to-many) are loaded in batches - but only if they are accessed, needed.

This leads to more than 1 SELECT clause, but also there is no inflation of SELECT clauses like 1 + N. If NHibernate will find out, that some of the required properties is already loaded in the session... it won't fire the SELECT any more.

Summary: both approaches are really the way. Try to play arround to find out in which situation you get more from the first or the second. BUT the mapping with lazy="true" and batch-size="25" should be used anyway

Some more links about batch-size:

  • How to Eager Load Associations without duplication in NHibernate?
  • NHibernate QueryOver with Fetch resulting multiple sql queries and db hits
  • Is this the right way to eager load child collections in NHibernate


来源:https://stackoverflow.com/questions/23126524/nhibernate-queryover-is-there-any-way-of-getting-better-sql-out-of-nhibernate

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!