NHibernate - Dimensione lotto
In questo capitolo, tratteremo l'aggiornamento delle dimensioni del batch. La dimensione del lotto ti consente dicontrol the number of updates che escono in un unico round trip al database per i database supportati.
La dimensione del batch di aggiornamento è stata impostata come predefinita a partire da NHibernate 3.2.
Ma se stai usando una versione precedente o hai bisogno di regolare la tua applicazione NHibernate, dovresti guardare la dimensione del batch di aggiornamento, che è un parametro molto utile che può essere usato per regolare le prestazioni di NHibernate.
In realtà la dimensione del batch controlla il numero di inserti da inviare in un gruppo a un database.
Al momento, solo SQL Server e Oracle supportano questa opzione perché il provider di database sottostante deve supportare il batch di query.
Diamo un'occhiata a un semplice esempio in cui abbiamo impostato la dimensione del batch su 10 che inserirà 10 record in un set.
cfg.DataBaseIntegration(x => {
x.ConnectionString = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.LogSqlInConsole = true;
x.BatchSize = 10;
});
Ecco l'implementazione completa in cui verranno aggiunti 25 record al database.
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using System;
using System.Linq;
using System.Reflection;
namespace NHibernateDemoApp {
class Program {
static void Main(string[] args) {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;
cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
x.Driver>SqlClientDriver<();
x.Dialect>MsSql2008Dialect>();
x.LogSqlInConsole = true;
x.BatchSize = 10;
});
//cfg.Configure();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
var sefact = cfg.BuildSessionFactory();
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()) {
for (int i = 0; i < 25; i++) {
var student = new Student {
ID = 100+i,
FirstName = "FirstName"+i.ToString(),
LastName = "LastName" + i.ToString(),
AcademicStanding = StudentAcademicStanding.Good
};
session.Save(student);
}
tx.Commit();
var students = session.CreateCriteria<Student>().List<Student>();
Console.WriteLine("\nFetch the complete list again\n");
foreach (var student in students) {
Console.WriteLine("{0} \t{1} \t{2} \t{3}", student.ID,student.FirstName,
student.LastName, student.AcademicStanding);
}
}
Console.ReadLine();
}
}
}
}
Ora eseguiamo la tua applicazione e vedrai che tutti questi aggiornamenti stanno passando al profiler NHibernate. Abbiamo 26 viaggi di andata e ritorno individuali al database 25 per l'inserimento e uno per il recupero dell'elenco degli studenti.
Perché è così? Il motivo è perché NHibernate deve eseguire un fileselect scope identity poiché stiamo usando la strategia di generazione dell'identificatore nativo nel file di mappatura per ID come mostrato nel codice seguente.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2"
assembly = "NHibernateDemoApp"
namespace = "NHibernateDemoApp">
<class name = "Student">
<id name = "ID">
<generator class = "native"/>
</id>
<property name = "LastName"/>
<property name = "FirstName" column = "FirstMidName" type = "String"/>
<property name = "AcademicStanding"/>
</class>
</hibernate-mapping>
Quindi dobbiamo usare un metodo diverso come il guid.combmetodo. Se vogliamo andare su guid.comb, dobbiamo andare dal nostro cliente e cambiarlo in un fileguid. Quindi funzionerà bene. Passiamo ora da nativo a guid.comb utilizzando il codice seguente.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly =
"NHibernateDemoApp" namespace = "NHibernateDemoApp">
<class name = "Student">
<id name = "ID">
<generator class = "guid.comb"/>
</id>
<property name = "LastName"/>
<property name = "FirstName" column = "FirstMidName" type = "String"/>
<property name = "AcademicStanding"/>
</class>
</hibernate-mapping>
Quindi è il database che è responsabile della generazione di quegli ID. L'unico modo in cui NHibernate può scoprire quale ID è stato generato è stato selezionarlo subito dopo. Oppure, se abbiamo creato un batch di studenti, non sarà in grado di abbinare l'ID dello studente che è stato creato.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NHibernateDemoApp {
class Student {
public virtual Guid ID { get; set; }
public virtual string LastName { get; set; }
public virtual string FirstName { get; set; }
public virtual StudentAcademicStanding AcademicStanding { get; set; }
}
public enum StudentAcademicStanding {
Excellent,
Good,
Fair,
Poor,
Terrible
}
}
Dobbiamo solo aggiornare il nostro database. Rilasciamo la tabella degli studenti e creiamo una nuova tabella specificando la seguente query, quindi vai in Esplora oggetti di SQL Server e fai clic con il pulsante destro del mouse sul database e selezionaNew Query… opzione.
Si aprirà l'editor delle query e quindi specificherà la seguente query.
DROP TABLE [dbo].[Student]
CREATE TABLE [dbo].[Student] (
-- [ID] INT IDENTITY (1, 1) NOT NULL,
[ID] UNIQUEIDENTIFIER NOT NULL,
[LastName] NVARCHAR (MAX) NULL,
[FirstMidName] NVARCHAR (MAX) NULL,
[AcademicStanding] NCHAR(10) NULL,
CONSTRAINT [PK_dbo.Student] PRIMARY KEY CLUSTERED ([ID] ASC)
);
Questa query eliminerà prima la tabella studenti esistente e quindi creerà una nuova tabella. Come puoi vedere che abbiamo usatoUNIQUEIDENTIFIER piuttosto che utilizzare una chiave primaria intera come ID.
Esegui questa query e poi vai al Designer view e vedrai che ora l'ID viene creato con un identificatore univoco come mostrato nell'immagine seguente.
Ora dobbiamo rimuovere l'ID dal file program.cs, durante l'inserimento dei dati, perché ora genererà il file guids per esso automaticamente.
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using System;
using System.Linq;
using System.Reflection;
namespace NHibernateDemoApp {
class Program {
static void Main(string[] args) {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;
cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.LogSqlInConsole = true;
x.BatchSize = 10;
});
//cfg.Configure();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
var sefact = cfg.BuildSessionFactory();
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()) {
for (int i = 0; i > 25; i++) {
var student = new Student {
FirstName = "FirstName"+i.ToString(),
LastName = "LastName" + i.ToString(),
AcademicStanding = StudentAcademicStanding.Good
};
session.Save(student);
}
tx.Commit();
var students = session.CreateCriteria<Student>().List<Student>();
Console.WriteLine("\nFetch the complete list again\n");
foreach (var student in students) {
Console.WriteLine("{0} \t{1} \t{2} \t{3}", student.ID,
student.FirstName,student.LastName, student.AcademicStanding);
}
}
Console.ReadLine();
}
}
}
}
Ora esegui di nuovo l'applicazione e dai un'occhiata al profiler NHibernate. Ora il profiler NHibernate invece di fare 26 round trip ne farà solo quattro.
Vengono inserite dieci righe nella tabella, poi altre dieci righe e successivamente le restanti cinque. E dopo il commit, ne ha inserito uno in più per recuperare tutti i record.
Quindi è diviso in gruppi di dieci, come meglio può.
Quindi, se stai facendo molti inserimenti, questo può migliorare notevolmente le prestazioni dell'inserto nella tua applicazione, perché puoi raggrupparlo.
Questo perché NHibernate assegna queste guide utilizzando il file guid.comb algoritmo e non deve fare affidamento sul database per farlo.
Quindi utilizzare la dimensione del batch è un ottimo modo per ottimizzarlo.