韦德国际app官方批量数据插入

  前言:本文标题有一个问号,带个问号的原由即使到本文写成结束“批量数量插入”如故是尚未缓解并困扰着楼猪的一个疑难问题。期待有讨论iBatis透彻的贤良看完本文后不吝赐教,绝非唯有是因为礼貌,真诚地谢谢了先。

一、问题源于
  在楼猪做过的深浅项目中,用iBatis.net实现的价值观CRUD操作里,只能促成一条记下的插入操作,大多数场所下,这些也满意了序列需要,不过在大数据量的批量数码导入的意况下就有瓶颈。在数据量较大的意况下,假如仍旧一条一条数据插入,好比洪水暴发而堤坝只开了一个细微的泄水口,楼猪已经测试过几十万条数据的插入,等的这叫一个狂喜,而且要处理的数量较长期占据内存,影响全部处理性能。
  由于数据库服务器限制,在“直接调用ssis包实现Sql
Server的数量导入效用
”无法接纳后,楼猪google了网上用iBatis批量插入的篇章。在google的结果上草草看了几页,赫然发现就是那么两篇在转来转去,而且仍然在java下的iBatis结合Spring共同促成的。楼猪不禁怀疑难道是祥和老眼昏花看错了,抑或是有人写好了隐形很深等待考古挖掘或是科学发现吗?

二、批量安插的变迁解决方案
鉴于普遍插入都是对准单表的,本文的言传身教延续使用“iBATIS.net获取运行时sql语句”中的Person表来讲解。
1、批量数据插入
  既然没看到iBatis.net有现成的类库可实现批量导入,再一次祭出杀手锏,查找ado.net下的批量数量操作。这三次资料就灰常的充分和百科了。在广泛的两种缓解方案中,楼猪选取了看上去特别简短而且效用也不低的SqlBulkCopy对象的贯彻格局,直接实现了iBatis.net的数额导入:
(1)、基类中三个虚方法

韦德国际app官方 1韦德国际app官方 2代码

        /// <summary>
        /// 批量插入(这些法子外部重写)
        /// </summary>
        /// <typeparam name=”M”></typeparam>
        /// <param name=”listModels”></param>
        /// <returns></returns>
        public virtual bool BatchInsert<M>(IList<M> listModels) where M : class
        {
            bool flag = false;
            return flag;
        }

        /// <summary>
        /// 执行插入命令
        /// </summary>
        /// <param name=”connStr”>sql连接字符串</param>
        /// <param name=”tableName”>表名称</param>
        /// <param name=”dt”>组装好的要批量导入的datatable</param>
        /// <returns></returns>
        protected virtual bool ExecuteInsertCommand(string connStr, string tableName, DataTable dt)
        {
            bool flag = false;
            //SqlTransaction transaction = null;
            //ISqlMapSession sesseion = this.SqlMapper.CreateSqlMapSession();
            try
            {
                using (System.Transactions.TransactionScope scope = new System.Transactions.TransactionScope())
                {
                    using (SqlConnection conn = new SqlConnection(connStr))
                    {
                        conn.Open();
                        using (SqlBulkCopy sbc = new SqlBulkCopy(conn))
                        {
                            //sesseion.BeginTransaction();
                            //transaction = conn.BeginTransaction();
                            //服务器上目标表的称呼
                            sbc.DestinationTableName = tableName;
                            sbc.BatchSize = 50000;
                            sbc.BulkCopyTimeout = 180;
                            for (int i = 0; i < dt.Columns.Count; i++)
                            {
                                //列映射定义数据源中的列和对象表中的列之间的关联
                                sbc.ColumnMappings.Add(dt.Columns[i].ColumnName, dt.Columns[i].ColumnName);
                            }
                            sbc.WriteToServer(dt);
                            flag = true;
                            //throw new Exception(“Test…”);
                            //transaction.Commit();//无效事务
                            //sesseion.Complete();  //无效事务
                            scope.Complete();//有效的政工
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                //if (transaction != null)
                //{
                //    transaction.Rollback();
                //}
                //if (sesseion != null)
                //{
                //    sesseion.RollBackTransaction();
                //}
                flag = false;
                string errMsg = ex.Message;
            }
            return flag;
        }

 说明:
a、从ExecuteInsertCommond的实现代码中,可以领悟地观察,其实大家就是间接选拔了ado.net的SqlBulkCopy对象,通过WriteToServer方法实现的。在演示代码中我们需要传递的多少个参数分别是数据库连接字符串,表名和一个datatable对象。其中datatable是在表面组装好传递进入给WriteToServer方法使用的,WriteToServer方法还有此外3个重载方法,您可以扩张实现任何花样的参数传递。
b、在批量安插的代码中,事务的拍卖楼猪拔取了传说已久的TransactionScope,记得刚毕业这会,没少这么写:

//.Net 2.0 可以这样子: 
using (TransactionScope trans = new TransactionScope()) 

   //执行你的事务,假设不成功自行回滚

 TransactionScope已经表明通过,而且方便的一塌糊涂。
有关iBatis自己的作业SqlMapperSession或者ado.net的SqlTransaction,楼猪在诠释中写的很精晓,二种工作无一例外地有非常。
(2)、dao重写批量导入方法

韦德国际app官方 3韦德国际app官方 4代码

    /// <summary>
        /// 批量插入
        /// </summary>
        /// <typeparam name=”M”></typeparam>
        /// <param name=”listModels”></param>
        /// <returns></returns>
        public override bool BatchInsert<M>(IList<M> listModels)
        {
            bool flag = false;
            try
            {
                string connStr = this.SqlMapper.DataSource.ConnectionString;
                string tbName = typeof(M).Name;
                DataTable dt = DataTableHelper.CreateTable<M>(listModels);
                flag = ExecuteInsertCommand(connStr, tbName, dt);
            }
            catch
            {
                flag = false;
            }
            return flag;
        }

 我们看来,在重写的法门里准备了特需传递的两个参数:数据库连接字符串,表名和一个datatable对象。在datatable组装的时候大家赖以了一个协助类:

韦德国际app官方 5韦德国际app官方 6代码

using System;
using System.Collections.Generic;
using System.Data;
using System.Reflection;

namespace IBatisNetApp.DAL.Common
{
    using IBatisNetApp.DAL.Model;

    public class DataTableHelper
    {
        private static IList<string> CreateModelProperty<T>(T obj) where T : class
        {
            IList<string> listColumns = new List<string>();
            BindingFlags bf = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static;
            Type objType = typeof(T);
            PropertyInfo[] propInfoArr = objType.GetProperties(bf);
            foreach (PropertyInfo item in propInfoArr)
            {
                object[] objAttrs = item.GetCustomAttributes(typeof(TableColumnAttribute), true);
                if (objAttrs != null && objAttrs.Length > 0)//取出实体对应表的莫过于列名
                {
                    listColumns.Add(item.Name);
                }
            }
            return listColumns;
        }

        private static DataTable CreateTable(IList<string> listColumns)
        {
            DataTable dt = new DataTable();
            for (int i = 0; i < listColumns.Count; i++)
            {
                dt.Columns.Add(new DataColumn(listColumns[i]));
            }
            return dt;
        }

        public static DataTable CreateTable<T>(IList<T> listModels) where T : class
韦德国际app官方,        {
            T model = default(T);
            IList<string> listProperties = CreateModelProperty<T>(model);
            DataTable dataTable = CreateTable(listProperties);
            BindingFlags bf = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static;
            Type objType = typeof(T);
            PropertyInfo[] propInfoArr = objType.GetProperties(bf);
            foreach (T itemModel in listModels)
            {
                DataRow dataRow = dataTable.NewRow();
                foreach (PropertyInfo item in propInfoArr)
                {
                    string propName = item.Name;
                    if (listProperties.Contains(propName))
                    {
                        object value = item.GetValue(itemModel, null);
                        dataRow[propName] = value;
                    }
                }
                dataTable.Rows.Add(dataRow);
            }
            return dataTable;
        }
    }
}

 必须小心,datatable里的列名顺序不限,不过列名必须相应实际表里的列(datatable里的列可以简单等于实际表里的列,可是无法凌驾)。示例中Person实体的TableColumn特性就是为着让属性和表的列名匹配,否则,实体继承的局部不是实际表的习性字段也会炫耀到表里,这样就会生出“给定的
ColumnMapping 与源或目标中的任意列均不配合”的特别。

2、批量数额插入并收获具有新插入的自增Id
  其实这一个是在题目1的基本功上延伸的一个问题。楼猪想了一个傻乎乎的解决方案,思路就是,将要插入的大数据量的泛型List(设有count项)第一项先单独插入,重临一个自增id设为firstId,然后按照1里的解决方案(有业务的那种,不带事务的话,结果就不是有过错那么简单了,很可能错的百般离谱),批量插入剩余的count-1项,插入成功后,我们可以看清批量安插的数码Id的限制为firstId<=Id<firstId+count。
ps,在实际的实践环境中,楼猪仍然认为这些非常不靠谱,一定要慎用。哎,懒得贴代码了。
最后,期待高手给出iBATIS.net的批量数量操作的宏观解决方案。

【补充】高手你在什么地方,顶上去啊?

demo下载:IBatisNetApp

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图