|
From: <fab...@us...> - 2009-07-14 17:32:30
|
Revision: 4618
http://nhibernate.svn.sourceforge.net/nhibernate/?rev=4618&view=rev
Author: fabiomaulo
Date: 2009-07-14 17:32:28 +0000 (Tue, 14 Jul 2009)
Log Message:
-----------
Merge r4617
Modified Paths:
--------------
trunk/nhibernate/doc/reference/master.xml
Added Paths:
-----------
trunk/nhibernate/doc/reference/modules/batch.xml
Modified: trunk/nhibernate/doc/reference/master.xml
===================================================================
--- trunk/nhibernate/doc/reference/master.xml 2009-07-14 17:30:54 UTC (rev 4617)
+++ trunk/nhibernate/doc/reference/master.xml 2009-07-14 17:32:28 UTC (rev 4618)
@@ -13,6 +13,7 @@
<!ENTITY manipulating-data SYSTEM "modules/manipulating_data.xml">
<!ENTITY transactions SYSTEM "modules/transactions.xml">
<!ENTITY events SYSTEM "modules/events.xml">
+<!ENTITY batch SYSTEM "modules/batch.xml">
<!ENTITY query-hql SYSTEM "modules/query_hql.xml">
<!ENTITY query-criteria SYSTEM "modules/query_criteria.xml">
<!ENTITY query-sql SYSTEM "modules/query_sql.xml">
@@ -59,7 +60,7 @@
&manipulating-data;
&transactions;
&events;
-
+ &batch;
&query-hql;
&query-criteria;
&query-sql;
Added: trunk/nhibernate/doc/reference/modules/batch.xml
===================================================================
--- trunk/nhibernate/doc/reference/modules/batch.xml (rev 0)
+++ trunk/nhibernate/doc/reference/modules/batch.xml 2009-07-14 17:32:28 UTC (rev 4618)
@@ -0,0 +1,315 @@
+<chapter id="batch">
+ <title>Batch processing</title>
+
+ <para>
+ A naive approach to inserting 100 000 rows in the database using Hibernate might
+ look like this:
+ </para>
+
+<programlisting><![CDATA[ISession session = sessionFactory.OpenSession();
+ITransaction tx = session.BeginTransaction();
+for ( int i=0; i<100000; i++ ) {
+ Customer customer = new Customer(.....);
+ session.Save(customer);
+}
+tx.Commit();
+session.Close();]]></programlisting>
+
+ <para>
+ This would fall over with an <literal>OutOfMemoryException</literal> somewhere
+ around the 50 000th row. That's because NHibernate caches all the newly inserted
+ <literal>Customer</literal> instances in the session-level cache.
+ </para>
+
+ <para>
+ In this chapter we'll show you how to avoid this problem. First, however, if you
+ are doing batch processing, it is absolutely critical that you enable the use of
+ ADO batching, if you intend to achieve reasonable performance. Set the ADO batch
+ size to a reasonable number (say, 10-50):
+ </para>
+
+ <programlisting><![CDATA[adonet.batch_size 20]]></programlisting>
+
+ <para id="disablebatching" revision="1">
+ Note that NHibernate disables insert batching at the ADO level transparently if you
+ use an <literal>identiy</literal> identifier generator.
+ </para>
+
+ <para>
+ You also might like to do this kind of work in a process where interaction with
+ the second-level cache is completely disabled:
+ </para>
+
+<programlisting><![CDATA[cache.use_second_level_cache false]]></programlisting>
+
+ <para>
+ However, this is not absolutely necessary, since we can explicitly set the
+ <literal>CacheMode</literal> to disable interaction with the second-level cache.
+ </para>
+
+ <sect1 id="batch-inserts">
+ <title>Batch inserts</title>
+
+ <para>
+ When making new objects persistent, you must <literal>Flush()</literal> and
+ then <literal>Clear()</literal> the session regularly, to control the size of
+ the first-level cache.
+ </para>
+
+<programlisting><![CDATA[ISession session = sessionFactory.openSession();
+ITransaction tx = session.BeginTransaction();
+
+for ( int i=0; i<100000; i++ ) {
+ Customer customer = new Customer(.....);
+ session.Save(customer);
+ if ( i % 20 == 0 ) { //20, same as the ADO batch size
+ //flush a batch of inserts and release memory:
+ session.Flush();
+ session.Clear();
+ }
+}
+
+tx.Commit();
+session.Close();]]></programlisting>
+
+ </sect1>
+
+ <sect1 id="batch-statelesssession">
+ <title>The StatelessSession interface</title>
+ <para>
+ Alternatively, Hibernate provides a command-oriented API that may be used for
+ streaming data to and from the database in the form of detached objects. A
+ <literal>IStatelessSession</literal> has no persistence context associated
+ with it and does not provide many of the higher-level life cycle semantics.
+ In particular, a stateless session does not implement a first-level cache nor
+ interact with any second-level or query cache. It does not implement
+ transactional write-behind or automatic dirty checking. Operations performed
+ using a stateless session do not ever cascade to associated instances. Collections
+ are ignored by a stateless session. Operations performed via a stateless session
+ bypass Hibernate's event model and interceptors. Stateless sessions are vulnerable
+ to data aliasing effects, due to the lack of a first-level cache. A stateless
+ session is a lower-level abstraction, much closer to the underlying ADO.
+ </para>
+
+<programlisting><![CDATA[IStatelessSession session = sessionFactory.OpenStatelessSession();
+ITransaction tx = session.BeginTransaction();
+
+var customers = session.GetNamedQuery("GetCustomers")
+ .Enumerable<Customer>();
+while ( customers.MoveNext() ) {
+ Customer customer = customers.Current;
+ customer.updateStuff(...);
+ session.Update(customer);
+}
+
+tx.Commit();
+session.Close();]]></programlisting>
+
+ <para>
+ Note that in this code example, the <literal>Customer</literal> instances returned
+ by the query are immediately detached. They are never associated with any persistence
+ context.
+ </para>
+
+ <para>
+ The <literal>insert(), update()</literal> and <literal>delete()</literal> operations
+ defined by the <literal>StatelessSession</literal> interface are considered to be
+ direct database row-level operations, which result in immediate execution of a SQL
+ <literal>INSERT, UPDATE</literal> or <literal>DELETE</literal> respectively. Thus,
+ they have very different semantics to the <literal>Save(), SaveOrUpdate()</literal>
+ and <literal>Delete()</literal> operations defined by the <literal>ISession</literal>
+ interface.
+ </para>
+
+ </sect1>
+
+ <sect1 id="batch-direct" revision="3">
+ <title>DML-style operations</title>
+
+ <para>
+ As already discussed, automatic and transparent object/relational mapping is concerned
+ with the management of object state. This implies that the object state is available
+ in memory, hence manipulating (using the SQL <literal>Data Manipulation Language</literal>
+ (DML) statements: <literal>INSERT</literal>, <literal>UPDATE</literal>, <literal>DELETE</literal>)
+ data directly in the database will not affect in-memory state. However, Hibernate provides methods
+ for bulk SQL-style DML statement execution which are performed through the
+ Hibernate Query Language (<link linkend="queryhql">HQL</link>).
+ </para>
+
+ <para>
+ The pseudo-syntax for <literal>UPDATE</literal> and <literal>DELETE</literal> statements
+ is: <literal>( UPDATE | DELETE ) FROM? EntityName (WHERE where_conditions)?</literal>. Some
+ points to note:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ In the from-clause, the FROM keyword is optional
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ There can only be a single entity named in the from-clause; it can optionally be
+ aliased. If the entity name is aliased, then any property references must
+ be qualified using that alias; if the entity name is not aliased, then it is
+ illegal for any property references to be qualified.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ No <link linkend="queryhql-joins-forms">joins</link> (either implicit or explicit)
+ can be specified in a bulk HQL query. Sub-queries may be used in the where-clause;
+ the subqueries, themselves, may contain joins.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The where-clause is also optional.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ As an example, to execute an HQL <literal>UPDATE</literal>, use the
+ <literal>IQuery.ExecuteUpdate()</literal> method:
+ </para>
+
+<programlisting><![CDATA[ISession session = sessionFactory.OpenSession();
+ITransaction tx = session.BeginTransaction();
+
+string hqlUpdate = "update Customer c set c.name = :newName where c.name = :oldName";
+// or string hqlUpdate = "update Customer set name = :newName where name = :oldName";
+int updatedEntities = s.CreateQuery( hqlUpdate )
+ .SetString( "newName", newName )
+ .SetString( "oldName", oldName )
+ .ExecuteUpdate();
+tx.Commit();
+session.Close();]]></programlisting>
+
+ <para>
+ HQL <literal>UPDATE</literal> statements, by default do not effect the
+ <link linkend="mapping-declaration-version">version</link>
+ or the <link linkend="mapping-declaration-timestamp">timestamp</link> property values
+ for the affected entities. However,
+ you can force NHibernate to properly reset the <literal>version</literal> or
+ <literal>timestamp</literal> property values through the use of a <literal>versioned update</literal>.
+ This is achieved by adding the <literal>VERSIONED</literal> keyword after the <literal>UPDATE</literal>
+ keyword.
+ </para>
+<programlisting><![CDATA[ISession session = sessionFactory.OpenSession();
+ITransaction tx = session.BeginTransaction();
+string hqlVersionedUpdate = "update versioned Customer set name = :newName where name = :oldName";
+int updatedEntities = s.CreateQuery( hqlUpdate )
+ .SetString( "newName", newName )
+ .SetString( "oldName", oldName )
+ .ExecuteUpdate();
+tx.Commit();
+session.Close();]]></programlisting>
+
+ <para>
+ Note that custom version types (<literal>NHibernate.Usertype.IUserVersionType</literal>)
+ are not allowed in conjunction with a <literal>update versioned</literal> statement.
+ </para>
+
+ <para>
+ To execute an HQL <literal>DELETE</literal>, use the same <literal>IQuery.ExecuteUpdate()</literal>
+ method:
+ </para>
+
+<programlisting><![CDATA[ISession session = sessionFactory.OpenSession();
+ITransaction tx = session.BeginTransaction();
+
+String hqlDelete = "delete Customer c where c.name = :oldName";
+// or String hqlDelete = "delete Customer where name = :oldName";
+int deletedEntities = s.CreateQuery( hqlDelete )
+ .SetString( "oldName", oldName )
+ .ExecuteUpdate();
+tx.Commit();
+session.Close();]]></programlisting>
+
+ <para>
+ The <literal>int</literal> value returned by the <literal>IQuery.ExecuteUpdate()</literal>
+ method indicate the number of entities effected by the operation. Consider this may or may not
+ correlate to the number of rows effected in the database. An HQL bulk operation might result in
+ multiple actual SQL statements being executed, for joined-subclass, for example. The returned
+ number indicates the number of actual entities affected by the statement. Going back to the
+ example of joined-subclass, a delete against one of the subclasses may actually result
+ in deletes against not just the table to which that subclass is mapped, but also the "root"
+ table and potentially joined-subclass tables further down the inheritence hierarchy.
+ </para>
+
+ <para>
+ The pseudo-syntax for <literal>INSERT</literal> statements is:
+ <literal>INSERT INTO EntityName properties_list select_statement</literal>. Some
+ points to note:
+ </para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>
+ Only the INSERT INTO ... SELECT ... form is supported; not the INSERT INTO ... VALUES ... form.
+ </para>
+ <para>
+ The properties_list is analogous to the <literal>column speficiation</literal>
+ in the SQL <literal>INSERT</literal> statement. For entities involved in mapped
+ inheritence, only properties directly defined on that given class-level can be
+ used in the properties_list. Superclass properties are not allowed; and subclass
+ properties do not make sense. In other words, <literal>INSERT</literal>
+ statements are inherently non-polymorphic.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ select_statement can be any valid HQL select query, with the caveat that the return types
+ must match the types expected by the insert. Currently, this is checked during query
+ compilation rather than allowing the check to relegate to the database. Note however
+ that this might cause problems between NHibernate <literal>Type</literal>s which are
+ <emphasis>equivalent</emphasis> as opposed to <emphasis>equal</emphasis>. This might cause
+ issues with mismatches between a property defined as a <literal>NHibernate.Type.DateType</literal>
+ and a property defined as a <literal>NHibernate.Type.TimestampType</literal>, even though the
+ database might not make a distinction or might be able to handle the conversion.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ For the id property, the insert statement gives you two options. You can either
+ explicitly specify the id property in the properties_list (in which case its value
+ is taken from the corresponding select expression) or omit it from the properties_list
+ (in which case a generated value is used). This later option is only available when
+ using id generators that operate in the database; attempting to use this option with
+ any "in memory" type generators will cause an exception during parsing. Note that
+ for the purposes of this discussion, in-database generators are considered to be
+ <literal>NHibernate.Id.SequenceGenerator</literal> (and its subclasses) and
+ any implementors of <literal>NHibernate.Id.IPostInsertIdentifierGenerator</literal>.
+ The most notable exception here is <literal>NHibernate.Id.TableHiLoGenerator</literal>,
+ which cannot be used because it does not expose a selectable way to get its values.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ For properties mapped as either <literal>version</literal> or <literal>timestamp</literal>,
+ the insert statement gives you two options. You can either specify the property in the
+ properties_list (in which case its value is taken from the corresponding select expressions)
+ or omit it from the properties_list (in which case the <literal>seed value</literal> defined
+ by the <literal>NHibernate.Type.IVersionType</literal> is used).
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <para>
+ An example HQL <literal>INSERT</literal> statement execution:
+ </para>
+
+<programlisting><![CDATA[ISession session = sessionFactory.OpenSession();
+ITransaction tx = session.BeginTransaction();
+
+var hqlInsert = "insert into DelinquentAccount (id, name) select c.id, c.name from Customer c where ...";
+int createdEntities = s.CreateQuery( hqlInsert )
+ .ExecuteUpdate();
+tx.Commit();
+session.Close();]]></programlisting>
+
+ </sect1>
+
+</chapter>
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|