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. |