摘要
對於批量插入和批量修改數據,通過設置NHibernate配置文件的BatchSize屬性,可以大量減少NHibernate與數據庫交互的次數。
1. Batch屬性介紹
設置了BatchSize屬性后,NHibernate將對批量的Insert/Update操作進行自動分組,按分組提交到數據庫,大量減少了與數據庫交互的次數。
NHibernate目前只支持對SQL Server數據庫和Oracle數據庫的批量Insert/Update操作進行BatchSize優化。
對於Insert操作,NHibernate只能對主鍵數據類型是UNIQUEIDENTIFIER的表提供BatchSize優化。
1)IDENTITY類型主鍵
在上一篇文章中,Customer表主鍵類型是IDENTITY類型的。
程序演示
修改SessionFactory屬性,添加x.BatchSize = 10的配置。
1 public static ISessionFactory SessionFactory 2 { 3 get 4 { 5 if (_sessionFactory == null) 6 { 7 var cfg = new Configuration(); 8 cfg.DataBaseIntegration(x=> { 9 x.BatchSize = 10; 10 }); 11 cfg.Configure(); 12 _sessionFactory = cfg.BuildSessionFactory(); 13 } 14 return _sessionFactory; 15 } 16 }
修改Main函數
1 static void Main(string[] args) 2 { 3 HibernatingRhinos.Profiler.Appender.NHibernate.NHibernateProfiler.Initialize(); 4 5 using (var session = SessionFactory.OpenSession()) 6 { 7 for (int i = 0; i < 25; i++) 8 { 9 var customer = new Customer 10 { 11 FirstName = "FirstName" + i, 12 LastName = "LastName" + i, 13 MemberSince = new DateTime(2012, 1, 1) 14 }; 15 session.Save(customer); 16 } 17 session.Flush(); 18 Console.WriteLine("fetch the complete list"); 19 var list = session.CreateCriteria<Customer>().List<Customer>(); 20 foreach (Customer customer in list) 21 { 22 Console.WriteLine("{0} {1}", customer.FirstName,customer.LastName); 23 } 24 } 25 26 Console.WriteLine("Completed"); 27 Console.ReadLine(); 28 }
打開NHibernate Profile,清空Session,執行程序,得到結果
監控到NHibernate執行了26次SQL語句,25次Insert語句和1次Select語句。
這是因為,雖然配置了BatchSize=10,但是Customer表的主鍵列的生成策略是Identity,每次Insert操作完成后都要計算下一條新記錄的主鍵值,因此BatchSize沒辦法對其進行分組優化。
選擇一條Insert監控數據,在Detail欄中看到執行了select SCOP_IDENTITY()語句。
2)UNIQUEIDENTIFIER類型主鍵
刪除Customer表,用UNIQUEIDENTIFIER主鍵重建Customer表。
重建Customer表的SQL語句。
1 CREATE TABLE [dbo].[Customer]( 2 --[Id] [int] IDENTITY(1,1) NOT NULL, 3 Id UNIQUEIDENTIFIER NOT NULL, 4 [FirstName] [nvarchar](100) NOT NULL, 5 [LastName] [nvarchar](100) NOT NULL, 6 [Points] [int] NULL, 7 [HasGoldStatus] [bit] NULL, 8 [MemberSince] [date] NULL, 9 [CreditRating] [nchar](20) NULL, 10 [AverageRating] [decimal](18, 4) NULL, 11 [Street] [nvarchar](100) NULL, 12 [City] [nvarchar](100) NULL, 13 [Province] [nvarchar](100) NULL, 14 [Country] [nvarchar](100) NULL, 15 CONSTRAINT [PK_Customer] PRIMARY KEY CLUSTERED 16 ( 17 [Id] ASC 18 )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 19 ) ON [PRIMARY]
修改Customer類,Id屬性類型改成了Guid。
1 public class Customer 2 { 3 public virtual Guid Id { get; set; } 4 public virtual string FirstName { get; set; } 5 public virtual string LastName { get; set; } 6 public virtual double AverageRating { get; set; } 7 public virtual int Points { get; set; } 8 public virtual bool HasGoldStatus { get; set; } 9 public virtual DateTime MemberSince { get; set; } 10 public virtual CustomerCreditRating CreditRating { get; set; } 11 public virtual string Street { get; set; } 12 public virtual string City { get; set; } 13 public virtual string Province { get; set; } 14 public virtual string Country { get; set; } 15 }
修改Customer.hbm.xml文件,將Id的generator class改成了guid.comb。
<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernateDemoApp" namespace="NHibernateDemoApp"> <class name="Customer" table="Customer"> <id name="Id"> <generator class="guid.comb"/> </id> <property name="FirstName" not-null="true"/> <property name="LastName" not-null ="true"/> <property name="AverageRating"/> <property name="Points"/> <property name="HasGoldStatus"/> <property name="MemberSince"/> <property name="CreditRating" type="CustomerCreditRating"/> <property name="Street"/> <property name="City"/> <property name="Province"/> <property name="Country"/> </class> </hibernate-mapping>
清空NHibernate Profile的Session,執行程序,得到結果。
NHibernate只與數據庫交互了四次,三次Insert,一次Select。因為表的主鍵類型是UNIQUEIDENTIFIER,每次Insert記錄時NHibernate自動生成新記錄主鍵值,不需要數據庫計算下一條記錄的主鍵值。
設置了BatchSize=10,NHibernate將25次Insert操作自動分成三組,第一組10條Insert語句,第二組10條Insert語句,第三組5條Insert語句,再加上最后的一次查詢語句,一共是4次與數據庫交互。