Using stateless sessions
使用无状态会话
当进行大量数据处理的时候,可能会放弃使用一些高级特性,而使用更接近底层的API来提高性能.在NHibernate中,这种高性能的底层API就是无状态的会话.本节介绍如何使用无状态会话来更新movie对象的价格.
准备
使用第一章的Eg.Core和第二章的Configuring NHibernate with App.config来创建一个控制台应用程序.
步骤
1. 先创建一些数据,在Main方法中添加下面的代码:
using (var session = sessionFactory.OpenStatelessSession()) {using (var tx = session.BeginTransaction()){for (int i = 0; i < 1000; i++)session.Insert(new Movie(){Name = "Movie " + i.ToString(),Description = "A great movie!",UnitPrice = 14.95M,Director = "Johnny Smith"});tx.Commit();} }
2. 下一步,更新movie的价格,在Main方法中添加下面的代码:
using (var session = sessionFactory.OpenStatelessSession()) {using (var tx = session.BeginTransaction()){var movies = GetMovies(session);foreach (var movie in movies){UpdateMoviePrice(movie);session.Update(movie);}tx.Commit();} }
3. 添加GetMovies方法:
static IEnumerable<Movie> GetMovies(IStatelessSession session) {return session.CreateQuery("from Movie").List<Movie>(); }
4. 最后,添加UpdateMoviePrice方法:
static Random rnd = new Random(); static void UpdateMoviePrice(Movie movie) {// Random price between $9.95 and $24.95movie.UnitPrice = (decimal) rnd.Next(10, 26) - 0.05M; }
原理
使用无状态会话,我们创建了1000个movie对象.无状态会话不支持事务处理的迟写机制,这意味着SQL语句不再被延迟到提交事务时.然而因为我们打开了批处理功能,所以这些语句也不会马上生效.累积100条insert语句后会被一起发送.如果关闭了批处理功能,每次调用session.Insert时,每条语句都会被立即执行,也就是一次发送一条语句.
下一步,使用了一个查询从数据库中获取了所有的movie.这些movie是分离状态的,没有同会话相关联.实体无法和无状态会话相关联.情况就是这样的,无论是通过查询获得实体还是通过Get方法.因为无状态会话无法实现自动脏校验,所以我们必须调用session.Update来保存对每个movie的更改.
扩展
无状态会话本质上是标准NHibernate会话的精简版.她不使用一级缓存也不执行自动脏校验,并且不支持延迟加载.事实上,她设置不保持对实体的引用,在处理成千上万的实体时,保持对实体的引用有助于避免内存泄露.级联是被忽略的.必须显式调用insert, update,或delete来操作每个实体,不会级联.无状态会话也会绕开二级缓存,事件监视器,拦截器,甚至NHibernate.SQL的log4net日志记录器.
忽略这些限制,无状态会话在需要处理实际对象的高性能批处理环境下是非常有用的.当你可以处理原始数据时,通常有更好的替代品就像原始的SQL,HQL bulk actions,SqlBulkCopy或者是ETL tools.对于原始的SQL,可以从session.Connection来简单访问ADO.NET连接对象,而且你可以如往常一样写你的ADO.NET代码.