@@ -1216,130 +1216,193 @@ sessionFactory.EvictCollection("Eg.Cat.Kittens");]]></programlisting>
1216
1216
</sect1 >
1217
1217
1218
1218
<sect1 id =" performance-multi-query" >
1219
- <title >Multi Query</title >
1220
-
1219
+ <title >Query batch </title >
1220
+
1221
1221
<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
1223
1223
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
1225
1225
example:
1226
1226
</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 >
1227
1266
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 >
1236
1278
1237
1279
<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.
1241
1282
</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
+ )));
1242
1291
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 >
1251
1294
1252
1295
<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.
1255
1299
</para >
1256
1300
1257
1301
<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 .
1260
1304
</para >
1261
1305
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
+
1262
1340
<para >
1263
1341
Multi query is executed by concatenating the queries and sending the query to the database
1264
1342
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 .
1266
1344
</para >
1267
1345
1268
1346
<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
1271
1356
<literal >in</literal > with a large number of arguments passed as parameters may easily exceed
1272
1357
this limit. For example, SQL Server has a limit of 2,100 parameters per round-trip, and will
1273
1358
throw an exception executing this query:
1274
1359
</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 >
1283
1371
1284
1372
<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
1286
1374
round-trip, without an expensive cartesian product (blog * users * posts).
1287
1375
</para >
1288
1376
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 >
1294
1385
1295
- </sect1 >
1296
-
1297
- <sect1 id =" performance-multi-criteria" >
1298
- <title >Multi Criteria</title >
1299
-
1300
1386
<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:
1305
1388
</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 >
1317
1389
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;
1322
1391
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
+ ...
1336
1393
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 >
1339
1401
1340
1402
<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 .
1343
1405
</para >
1406
+
1344
1407
</sect1 >
1345
1408
</chapter >
0 commit comments