@@ -1216,130 +1216,193 @@ sessionFactory.EvictCollection("Eg.Cat.Kittens");]]></programlisting>
12161216 </sect1 >
12171217
12181218 <sect1 id =" performance-multi-query" >
1219- <title >Multi Query</title >
1220-
1219+ <title >Query batch </title >
1220+
12211221 <para >
1222- This functionality allows you to execute several HQL queries in one round-trip
1222+ This functionality allows you to execute several queries in one round-trip
12231223 against the database server. A simple use case is executing a paged query while
1224- also getting the total count of results, in a single round-trip. Here is a simple
1224+ also getting the total count of results, in a single round-trip. Here is an
12251225 example:
12261226 </para >
1227+
1228+ <programlisting ><![CDATA[ using NHibernate.Multi;
1229+
1230+ ...
1231+
1232+ IQueryBatch queries = s.CreateQueryBatch()
1233+ .Add<Item>(
1234+ s.CreateQuery("from Item i where i.Id > ?")
1235+ .SetInt32(0, 50).SetFirstResult(10))
1236+ .Add<long>(
1237+ s.CreateQuery("select count(*) from Item i where i.Id > :id")
1238+ .SetInt32("id", 50));
1239+ IList<Item> items = queries.GetResult<Item>(0);
1240+ long count = queries.GetResult<long>(1).Single();]]> </programlisting >
1241+
1242+ <para >
1243+ The results are got by index, ordered according to the order of queries
1244+ added to the query batch. Instead of relying on this ordering, a key can be
1245+ associated with each query for later retrieval:
1246+ </para >
1247+
1248+ <programlisting ><![CDATA[ using NHibernate.Multi;
1249+
1250+ ...
1251+
1252+ var queries = s.CreateQueryBatch()
1253+ .Add("list", s.Query<Item>().Where(i => i.Id > 50))
1254+ .Add("count", s.Query<Item>().Where(i => i.Id > 50), q => q.Count());
1255+ var count = queries.GetResult<int>("count").Single();
1256+ var items = queries.GetResult<Item>("list");]]> </programlisting >
1257+
1258+ <para >
1259+ The namespace <literal >NHibernate.Multi</literal > has to be imported since most query
1260+ batch methods are extension methods.
1261+ </para >
1262+
1263+ <para >
1264+ Criteria queries are also supported by the query batch:
1265+ </para >
12271266
1228- <programlisting ><![CDATA[ IMultiQuery multiQuery = s.CreateMultiQuery()
1229- .Add(s.CreateQuery("from Item i where i.Id > ?")
1230- .SetInt32(0, 50).SetFirstResult(10))
1231- .Add(s.CreateQuery("select count(*) from Item i where i.Id > ?")
1232- .SetInt32(0, 50));
1233- IList results = multiQuery.List();
1234- IList items = (IList)results[0];
1235- long count = (long)((IList)results[1])[0];]]> </programlisting >
1267+ <programlisting ><![CDATA[ var queries = s.CreateQueryBatch()
1268+ .Add<Item>(
1269+ s.CreateCriteria(typeof(Item))
1270+ .Add(Expression.Gt("Id", 50))
1271+ .SetFirstResult(10))
1272+ .Add<long>(
1273+ s.CreateCriteria(typeof(Item))
1274+ .Add(Expression.Gt("Id", 50))
1275+ .SetProject(Projections.RowCount()));
1276+ var items = queries.GetResult<Item>(0);
1277+ var count = queries.GetResult<long>(1).Single();]]> </programlisting >
12361278
12371279 <para >
1238- The result is a list of query results, ordered according to the order of queries
1239- added to the multi query. Named parameters can be set on the multi query, and are
1240- shared among all the queries contained in the multi query, like this:
1280+ You can add <literal >ICriteria</literal > or <literal >DetachedCriteria</literal > to the query batch.
1281+ In fact, using DetachedCriteria in this fashion has some interesting implications.
12411282 </para >
1283+ <programlisting ><![CDATA[ DetachedCriteria customersCriteria = AuthorizationService.GetAssociatedCustomersQuery();
1284+ IQueryBatch queries = session.CreateQueryBatch()
1285+ .Add<Customer>(customersCriteria)
1286+ .Add<Policy>(DetachedCriteria.For<Policy>()
1287+ .Add(Subqueries.PropertyIn("id",
1288+ CriteriaTransformer.Clone(customersCriteria)
1289+ .SetProjection(Projections.Id())
1290+ )));
12421291
1243- <programlisting ><![CDATA[ IList results = s.CreateMultiQuery()
1244- .Add(s.CreateQuery("from Item i where i.Id > :id")
1245- .SetFirstResult(10))
1246- .Add("select count(*) from Item i where i.Id > :id")
1247- .SetInt32("id", 50)
1248- .List();
1249- IList items = (IList)results[0];
1250- long count = (long)((IList)results[1])[0];]]> </programlisting >
1292+ IList<Customer> customers = queries.GetResult<Customer>(0);
1293+ IList<Policy> policies = queries.GetResult<Policy>(1);]]> </programlisting >
12511294
12521295 <para >
1253- Positional parameters are not supported on the multi query, only on the individual
1254- queries.
1296+ We get a query that represents the customers we can access, and then we can utilize this
1297+ query further in order to perform additional logic (getting the policies of the customers we are
1298+ associated with), all in a single database round-trip.
12551299 </para >
12561300
12571301 <para >
1258- As shown above, if you do not need to configure the query separately, you can simply
1259- pass the HQL directly to the < literal >IMultiQuery.Add()</ literal > method .
1302+ The query batch also supports QueryOver and sql queries. All kind of queries can be mixed in the
1303+ same batch .
12601304 </para >
12611305
1306+ <programlisting ><![CDATA[ using NHibernate.Multi;
1307+
1308+ ...
1309+
1310+ var queries = s.CreateQueryBatch()
1311+ .Add("queryOverList", s.QueryOver<Item>().Where(i => i.Category == "Food"))
1312+ .Add<long>("sqlCount",
1313+ s.CreateSQLQuery("select count(*) as count from Item i where i.Category = :cat")
1314+ .AddScalar("count", NHibernateUtil.Int64)
1315+ .SetString("cat", "Food"));
1316+ var count = queries.GetResult<long>("sqlCount").Single();
1317+ var items = queries.GetResult<Item>("queryOverList");]]> </programlisting >
1318+
1319+ <para >
1320+ Second level cache is supported by the query batch. Queries flagged as cacheable will be retrieved
1321+ from the cache if already cached, otherwise their results will be put in the cache.
1322+ </para >
1323+
1324+ <programlisting ><![CDATA[ using NHibernate.Multi;
1325+
1326+ ...
1327+
1328+ var queries = s.CreateQueryBatch()
1329+ .Add("list",
1330+ s.Query<Item>()
1331+ .Where(i => i.Id > 50)
1332+ .WithOptions(o => o.SetCacheable(true)))
1333+ .Add<long>("count",
1334+ s.CreateQuery("select count(*) from Item i where i.Id > :id")
1335+ .SetInt32("id", 50)
1336+ .SetCacheable(true));
1337+ var count = queries.GetResult<long>("count").Single();
1338+ var items = queries.GetResult<Item>("list");]]> </programlisting >
1339+
12621340 <para >
12631341 Multi query is executed by concatenating the queries and sending the query to the database
12641342 as a single string. This means that the database should support returning several result sets
1265- in a single query. At the moment this functionality is only enabled for Microsoft SQL Server and SQLite .
1343+ in a single query. Otherwise each query will be individually executed instead .
12661344 </para >
12671345
12681346 <para >
1269- Note that the database server is likely to impose a limit on the maximum number of parameters
1270- in a query, in which case the limit applies to the multi query as a whole. Queries using
1347+ The first <literal >GetResult</literal > call triggers execution of the whole query batch, which
1348+ then stores all results. Later calls only retrieve the stored results. A query batch can be
1349+ re-executed by calling its <literal >Execute</literal > method. Once executed, no new query can be
1350+ added to the batch.
1351+ </para >
1352+
1353+ <para >
1354+ Note that the database server is likely to enforce a limit on the maximum number of parameters
1355+ in a query, in which case the limit applies to the query batch as a whole. Queries using
12711356 <literal >in</literal > with a large number of arguments passed as parameters may easily exceed
12721357 this limit. For example, SQL Server has a limit of 2,100 parameters per round-trip, and will
12731358 throw an exception executing this query:
12741359 </para >
1275-
1276- <programlisting ><![CDATA[ IList allEmployeesId = ...; //1,500 items
1277- IMultiQuery multiQuery = s.CreateMultiQuery()
1278- .Add(s.CreateQuery("from Employee e where e.Id in :empIds")
1279- .SetParameter("empIds", allEmployeesId).SetFirstResult(10))
1280- .Add(s.CreateQuery("select count(*) from Employee e where e.Id in :empIds")
1281- .SetParameter("empIds", allEmployeesId));
1282- IList results = multiQuery.List(); // will throw an exception from SQL Server]]> </programlisting >
1360+
1361+ <programlisting ><![CDATA[ int[] allEmployeesId = ...; // 1,500 items
1362+ var queries = s.CreateQueryBatch()
1363+ .Add<Employee>(
1364+ s.CreateQuery("from Employee e where e.Id in :empIds")
1365+ .SetParameterList("empIds", allEmployeesId)
1366+ .SetFirstResult(10))
1367+ .Add<long>(
1368+ s.CreateQuery("select count(*) from Employee e where e.Id in :empIds")
1369+ .SetParameterList("empIds", allEmployeesId));
1370+ queries.Execute(); // will throw an exception from SQL Server]]> </programlisting >
12831371
12841372 <para >
1285- An interesting usage of this feature is to load several collections of an object in one
1373+ An interesting usage of the query batch is to load several collections of an object in one
12861374 round-trip, without an expensive cartesian product (blog * users * posts).
12871375 </para >
12881376
1289- <programlisting ><![CDATA[ Blog blog = s.CreateMultiQuery()
1290- .Add("select b from Blog b left join fetch b.Users where b.Id = :id")
1291- .Add("select b from Blog b left join fetch b.Posts where b.Id = :id")
1292- .SetInt32("id", 123)
1293- .UniqueResult<Blog>();]]> </programlisting >
1377+ <programlisting ><![CDATA[ Blog blog = s.CreateQueryBatch()
1378+ .Add(
1379+ s.CreateQuery("select b from Blog b left join fetch b.Users where b.Id = :id")
1380+ .SetInt32("id", 123))
1381+ .Add(
1382+ s.CreateQuery("select b from Blog b left join fetch b.Posts where b.Id = :id")
1383+ .SetInt32("id", 123))
1384+ .GetResult<Blog>(0).FirstOrDefault();]]> </programlisting >
12941385
1295- </sect1 >
1296-
1297- <sect1 id =" performance-multi-criteria" >
1298- <title >Multi Criteria</title >
1299-
13001386 <para >
1301- This is the counter-part to Multi Query, and allows you to perform several criteria queries
1302- in a single round trip. A simple use case is executing a paged query while
1303- also getting the total count of results, in a single round-trip. Here is a simple
1304- example:
1387+ You can also add queries as future queries to a query batch:
13051388 </para >
1306-
1307- <programlisting ><![CDATA[ IMultiCriteria multiCrit = s.CreateMultiCriteria()
1308- .Add(s.CreateCriteria(typeof(Item))
1309- .Add(Expression.Gt("Id", 50))
1310- .SetFirstResult(10))
1311- .Add(s.CreateCriteria(typeof(Item))
1312- .Add(Expression.Gt("Id", 50))
1313- .SetProject(Projections.RowCount()));
1314- IList results = multiCrit.List();
1315- IList items = (IList)results[0];
1316- long count = (long)((IList)results[1])[0];]]> </programlisting >
13171389
1318- <para >
1319- The result is a list of query results, ordered according to the order of queries
1320- added to the multi criteria.
1321- </para >
1390+ <programlisting ><![CDATA[ using NHibernate.Multi;
13221391
1323- <para >
1324- You can add <literal >ICriteria</literal > or <literal >DetachedCriteria</literal > to the Multi Criteria query.
1325- In fact, using DetachedCriteria in this fashion has some interesting implications.
1326- </para >
1327- <programlisting ><![CDATA[ DetachedCriteria customersCriteria = AuthorizationService.GetAssociatedCustomersQuery();
1328- IList results = session.CreateMultiCriteria()
1329- .Add(customersCriteria)
1330- .Add(DetachedCriteria.For<Policy>()
1331- .Add(Subqueries.PropertyIn("id",
1332- CriteriaTransformer.Clone(customersCriteria)
1333- .SetProjection(Projections.Id())
1334- )))
1335- .List();
1392+ ...
13361393
1337- ICollection<Customer> customers = CollectionHelper.ToArray<Customer>(results[0]);
1338- ICollection<Policy> policies = CollectionHelper.ToArray<Policy>(results[1]);]]> </programlisting >
1394+ var queries = s.CreateQueryBatch();
1395+ var list = queries.AddAsFuture(s.Query<Item>().Where(i => i.Id > 50)));
1396+ var countValue = queries.AddAsFutureValue<long>(
1397+ s.CreateQuery("select count(*) from Item i where i.Id > :id")
1398+ .SetInt32("id", 50));
1399+ var count = countValue.Value;
1400+ var items = list.GetEnumerable();]]> </programlisting >
13391401
13401402 <para >
1341- As you see, we get a query that represents the customers we can access, and then we can utilize this query further in order to
1342- perform additional logic (getting the policies of the customers we are associated with), all in a single database round-trip .
1403+ Futures built from a query batch are executed together the first time the result of one of
1404+ them is accessed. They are independent of futures obtained directly from the queries .
13431405 </para >
1406+
13441407 </sect1 >
13451408</chapter >
0 commit comments