zoukankan      html  css  js  c++  java
  • NHibernate官方文档中文版——批量插入(Batch inserts)

    A naive approach t7o inserting 100 000 rows in the database using NHibernate might look like this:

    一个简单的使用NHibernate来向数据库插入100000条记录的操作的方式也许是这样子的:

    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();

    This would fall over with an OutOfMemoryException somewhere around the 50 000th row. That's because NHibernate caches all the newly inserted Customer instances in the session-level cache.

    在执行到差不多50000行数据的时候,这个方法可能会在某个地方造成OutOfMemoryException异常。这个是由于NHibernate在一级缓存中缓存了所有新插入的customer实例造成的。

    在这个小节我们会想你展示如何避免这个问题。首先,如果你正在进行批量任务,如果你想要达到合理的性能的话,你肯定需要允许使用ADO的批量处理。将ADO的batch size设置成一个合理的值(例如,10-50):

    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):

    adonet.batch_size 20

    要注意,如果你使用一个identity的标识符生成方式的话,NHibernate会自动在ADO层级禁用批量插入功能。

    你还需用禁用与二级缓存的交互。

    Note that NHibernate disables insert batching at the ADO level transparently if you use an identiyidentifier generator.

    You also might like to do this kind of work in a process where interaction with the second-level cache is completely disabled:

    cache.use_second_level_cache false

    However, this is not absolutely necessary, since we can explicitly set the CacheMode to disable interaction with the second-level cache.

    然而,这并不是必要的,因为我们可以显式地设置CacheMode来禁用和二级缓存的交互。

    批量插入

    当持久化一个新对象的时候,你必须有计划地flush()和clear()这个session来控制第一级缓存的规模。

    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();

    无状态的session接口

    此外,NHibernate提供了一个面向指令的API来以游离态的形式来将数据从数据库取出或者存入。IStatelessSession接口不包含与之相关的持久化上下文并且也不提供更多的更高级的生命周期语法。特别的,一个无状态的session并不实现一级缓存也不和二级或者查询缓存有任何的交互。它不实现事物级别的write-behind或者自动脏数据检查。使用无状态session来实现的操作也不级联关联其他实例。集合都会被无状态session忽略。使用无状态session的操作会忽略所有的NHibernate事件模型和拦截器。无状态session很容易因为数据别名的原因产生错误,这个是由于它缺乏一级缓存。无状态session是一个低级抽象,和底层的ADO更接近。

    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();

    要注意这个代码段例子中,customer实例在查询之后返回,并且立即变成游离态状态。他们不会和任何持久化上下文有任何关联。

    statelesssession接口定义的insert(),update()和delete()操作通常被当作直接面向数据库的行级别的操作,这个操作机会直接导致相应的insert,update和delete操作。因此,他们和Isession接口中定义的Save(), SaveOrUpdate()和Delete() 操作的语法都不相同。

    Alternatively, NHibernate provides a command-oriented API that may be used for streaming data to and from the database in the form of detached objects. A IStatelessSession 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 NHibernate'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.

    Note that in this code example, the Customer instances returned by the query are immediately detached. They are never associated with any persistence context.

    The insert(), update() and delete() operations defined by the StatelessSession interface are considered to be direct database row-level operations, which result in immediate execution of a SQLINSERT, UPDATE or DELETE respectively. Thus, they have very different semantics to the Save(), SaveOrUpdate() and Delete() operations defined by the ISession interface.

    DML类型操作

    就像我们已经讨论过的那样,自动并且简单的对象关系映射主要关注的是对象的状态管理。这就意味着对象状态已经加载在内存中了,因此直接在数据库中对数据进行数据操作(使用SQL数据操纵语言(DML):INSERT,UPDATE,DELETE)将不会影响到内存中的数据状态。NHibernate为bulk SQL类型的DML执行提供了一些方法,这些方法可以通过Hibernate查询语句来完成(HQL)。

    这些虚拟的UPDATE和DELETE语句类似于:( UPDATE | DELETE ) FROM? EntityName (WHERE where_conditions)?。需要注意一些东西:

    • 在from语句中,这个FROM关键字是可选的
    • 在from语句中只能有一个实体名称,它可以是一个自定义的别名。如果这个实体名称是别名,那么所有关联到这个实体的属性都应该限定使用这个别名,如果这个实体名称不是别名,那么就不能够限定这个引用。
    • 在bulk HQL 查询中,不能使用 joins(无论是显式的还是隐式的)。子查询可能可以在where语句中出现,由于这些子查询自身可能就包含join。
    • where语句也是可选的、

    下面例子中可以看到,可以使用IQuery.ExecuteUpdate() 方法来执行一个HQL 的UPDATE操作:

    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 Data Manipulation Language (DML) statements: INSERT, UPDATE, DELETE) data directly in the database will not affect in-memory state. However, NHibernate provides methods for bulk SQL-style DML statement execution which are performed through the Hibernate Query Language (HQL).

    The pseudo-syntax for UPDATE and DELETE statements is: ( UPDATE | DELETE ) FROM? EntityName (WHERE where_conditions)?. Some points to note:

    • In the from-clause, the FROM keyword is optional
    • 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.
    • No joins (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.
    • The where-clause is also optional.
    • As an example, to execute an HQL UPDATE, use the IQuery.ExecuteUpdate() method:

      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();

      HQL 的UPDATE语句,默认不会影响到实体的version或者timestamp属性值。然而你可以强制让NHibernate来适当地充值version或者timestamp属性,通过使用带版本更新的update操作。这个可以通过在update关键字后面添加一个version关键字来实现。

      HQL UPDATE statements, by default do not effect the version or the timestamp property values for the affected entities. However, you can force NHibernate to properly reset the version ortimestamp property values through the use of a versioned update. This is achieved by adding the VERSIONED keyword after the UPDATE keyword.

      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();

      许要注意的是,自定义的版本类型(NHibernate.Usertype.IUserVersionType)是不能和带版本更新的update操作一起使用的。

      要执行HQL的DELETE操作,同样可以使用IQuery.ExecuteUpdate() 方法:

      Note that custom version types (NHibernate.Usertype.IUserVersionType) are not allowed in conjunction with a update versioned statement.

      To execute an HQL DELETE, use the same IQuery.ExecuteUpdate() method:

      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();

      IQuery.ExecuteUpdate() 方法返回的int类型的返回值意味着受到这个操作影响的实体数目。要知道这个不一定对应数据库中受到影响的行数。HQL bulk操作可能导致许多关联的子类的SQL查询语句被执行,例如,返回值说明了实际上有多少实体受到语句的影响。回到关联子类的例子中来,对其中一个子类的删除操作可能不仅仅导致子类的数据表,还可能会导致父类的数据表和整个继承结构关联中潜在的其他关联子类数据表数据的删除。

      虚拟的INSERT操作语法是:INSERT INTO EntityName properties_list select_statement. 有一些点需要注意:

    • 只支持INSERT INTO ... SELECT ... 格式,而不支持INSERT INTO ... VALUES ... 这样的格式。
    • _list属性相当于INSERT 语句中对应的字段。对于那些继承映射的实体,只有直接在给定的类中定义的属性可以在_list属性中使用。父类属性是不允许的,且子类属性没有任何意义。话句话书哦,INSERT语句的在继承映射不支持多态。
    • select语句可以是任何合理的HQL select语句,需要注意的是返回值类型必须和插入语句对应的类型相匹配。目前这种检查会在编译查询语句的时候进行,而不是把这种检查交给数据库来进行。然而需要注意的是,这可能会在两种等价的NHibernate类型造成不一致的问题。这会在NHibernate.Type.DateType和NHibernate.Type.TimestampType之间产生问题,尽管数据库不会区分这两种类型或者可能会自动对二者进行转换。
    • 对于id属性,insert声明给了两个方案。你可以在_list属性中显式指定id属性(在这种情况下,它的值会从相应的select表达式中获得),或者在_list属性中缺省设置(在这种情况下,会使用自动生成的值)。后面一种方法仅仅对于使用id在数据库中自动生成的方式适用;如果尝试任何在本地id类型生成方式会在解析的时候造成异常。许要注意的是,我们讨论的目的是,数据库的id生成方式被认为是NHibernate.Id.SequenceGenerator (和它的子类)和任何NHibernate.Id.IPostInsertIdentifierGenerator的实现。最需要注意的例外是NHibernate.Id.TableHiLoGenerator,这种方式不能使用,因为它不暴露任何可以select的方式来获得它的值。
    • 对于映射到version或者timestamp的属性,insert声明给你两种方案。你可以在_list属性中指定对应的属性(在这种情况下,它的值会从相应的select表达式中获得)或者在_list属性中缺省设置(在这种情况下,会使用由NHibernate.Type.IVersionType 定义的种子值)。
    • The int value returned by the IQuery.ExecuteUpdate() 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.

      The pseudo-syntax for INSERT statements is: INSERT INTO EntityName properties_list select_statement. Some points to note:

    • Only the INSERT INTO ... SELECT ... form is supported; not the INSERT INTO ... VALUES ... form.
    • The properties_list is analogous to the column speficiation in the SQL INSERT 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, INSERT statements are inherently non-polymorphic.
    • 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 Types which are equivalent as opposed to equal. This might cause issues with mismatches between a property defined as a NHibernate.Type.DateType and a property defined as a NHibernate.Type.TimestampType, even though the database might not make a distinction or might be able to handle the conversion.
    • 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 NHibernate.Id.SequenceGenerator (and its subclasses) and any implementors of NHibernate.Id.IPostInsertIdentifierGenerator. The most notable exception here is NHibernate.Id.TableHiLoGenerator, which cannot be used because it does not expose a selectable way to get its values.
    • For properties mapped as either version or timestamp, 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 seed value defined by the NHibernate.Type.IVersionType is used).
    • 执行一个HQL插入操作的:

      An example HQL INSERT statement execution:

      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();
  • 相关阅读:
    PRISM概率模型检测器初使用--骰子模型(改进版)
    什么是P问题、NP问题和NPC问题
    kali linux进行arp欺骗和dos攻击
    java log日志的输出。
    sublime text3输入中文的问题.
    java 正则表达式匹配字符串
    python tornado+mongodb的使用
    jasper3
    jasper2
    jasper
  • 原文地址:https://www.cnblogs.com/balavatasky/p/6363992.html
Copyright © 2011-2022 走看看