NHibernate und das Löschen vieler Daten

by Anton 17. September 2009 20:53

Ich hatte mir heute die Aufgabe gestellt, folgenden Code zu verbessern:

   1: _session.Delete(String.Format("FROM {0}", typeName)); 
   2: _session.Flush();

Dieser Code soll eine gesamte Tabelle leeren. Da diese Operationen auf einer SQLite-DB angewendet werden, kann man “Truncate Table” nicht verwenden (SQLite unterstützt diese Funktion nicht). Der Code arbeitet korrekt, aber extrem langsam. Woran liegt das?

Ein Check mit dem SQLite Administrator ergibt, dass die Delete-Operation sehr schnell funktioniert. Was tut also NHibernate? Also habe ich in der Konfigurationsdatei (lite-nhibernate.cfg.xml) <property name="show_sql">true</property> gesetzt. Die Ausgabe bei 100 Testeinträgen war:

   1: NHibernate: select logentry0_.Id as Id22_, logentry0_.Message as Message22_, logentry0_.Type as Type22_, logentry0_.Category as Category22_, logentry0_.DateCreated as DateCrea5_22_, logentry0_.Level as Level22_ from Log logentry0_ 
   2: NHibernate: DELETE FROM Log WHERE Id = @p0;@p0 = 1 
   3: NHibernate: DELETE FROM Log WHERE Id = @p0;@p0 = 2 
   4: NHibernate: DELETE FROM Log WHERE Id = @p0;@p0 = 3 
   5:
   6: NHibernate: DELETE FROM Log WHERE Id = @p0;@p0 = 99 
   7: NHibernate: DELETE FROM Log WHERE Id = @p0;@p0 = 100

NHibernate nimmt sich also jedes Element einzeln vor, um u.a. mögliche Abhängigkeiten zu aufzulösen. Dadurch gehen die Effekte verloren, die SQLite bei dieser Delete-Operation angewendet hätte (nämlich ein Truncate-ähnliches Delete). Ein Artikel dazu: BulkDataOperations using NHibernate. Der Autor gibt auch eine Lösungsmöglichkeit an, die mir aber zu aufwendig erschien. Ich bin schließlich bei folgendem gelandet:

var tableName = new Configuration().AddAssembly(type.Assembly).GetClassMapping(type).Table.Name; 
_session.CreateSQLQuery(string.Format("DELETE FROM {0}", tableName)).ExecuteUpdate(); 
_session.CreateSQLQuery("VACUUM").ExecuteUpdate();   
_session.Flush();

Funktioniert gut und schnell. Aber wie bekommt man den TabellenNamen raus? Z.B. durch var tableName = new Configuration().AddAssembly(type.Assembly).GetClassMapping(type).Table.Name; Jedoch hat dies einen Nachteil: Die Konfiguration ist für den Client unterschiedlich zu dem Test-Projekt. Ich hatte schon (mit Stefans Hilfe) einen NHibernateConfigurationService eingebaut. Ich war gerade damit beschäftigt viel Code zu refaktorisieren und Autofac zu erweitern.  Dann habe ich noch mal bei stackoverflow nachgeschaut. Da hat mir jemand geholfen. Ich habe den NHibernateConfigurationService wieder rausgenommen. Nun sieht mein Code so aus:

   1: String hqlDelete = string.Format("delete {0} t", typeof(LogEntry)); 
   2: _session.CreateQuery(hqlDelete).ExecuteUpdate(); 
   3: _session.CreateSQLQuery("VACUUM").ExecuteUpdate(); 
   4: _session.Flush(); 
Der Code funktioniert gut und schnell. Das “delete LogEntry t” verstehe ich noch nicht ganz, nehme aber an, dass die gesamte Tabelle die zum Typ LogEntry gemappt wird, geleert wird. Das “VACUUM” ist im Code, damit die DB-Datei nach dem Löschen wieder geschrumpft wird.

enjoyed the post?

Tags:

Comments are closed

About Oliver

shades-of-orange.com code blog logo I build web applications using ASP.NET and have a passion for javascript. Enjoy MVC 4 and Orchard CMS, and I do TDD whenever I can. I like clean code. Love to spend time with my wife and our children. My profile on Stack Exchange, a network of free, community-driven Q&A sites

About Anton

shades-of-orange.com code blog logo I'm a software developer at teamaton. I code in C# and work with MVC, Orchard, SpecFlow, Coypu and NHibernate. I enjoy beach volleyball, board games and Coke.