寻找 IBatisNet 批量插入(批量复制) 的心路历程
1、IBatisNet本身就是一个比较早的ORM框架,再加上是从java iBatis移值过来的,其流行程度不是特别高资料也不是很多(一年前),不过最近好像搜索比较多了一些(可能是训练的结果吧)
2、iBatis是有批量插入功能可Net就没有(光这个答案,都搜了好久。MyBatis是可以批量插入的,可公司框架是用的1.5版本的嘞)
3、无赖只好继续找,大多都说用用 iterate
- <!--批量导入数据-->
- <insert id="AddTmpResource" parameterClass="ArrayList">
- <iterate conjunction=" " open="" close="" >
- <![CDATA[
- insert into $[].TableName$ ([PhoneNum], [Name], [Resource]) values
- (#[].PhoneNum#, #[].Name#, #[].Resource#)
- ]]>
- </iterate>
- </insert>
- 当然要比foreach一条条插快,但这个有个限制2000个参数,所有(插入的行数=(2000/列数)的限制,而且与后面的批量复制差远了
- 4、最后还是找到了一个
- 下面就是复制的了
二、批量插入的变通解决方案
鉴于常见插入都是针对单表的,本文的示例延续使用“iBATIS.net获取运行时sql语句”中的Person表来讲解。
1、批量数据插入
既然没看到iBatis.net有现成的类库可实现批量导入,再次祭出杀手锏,查找ado.net下的批量数据操作。这一次资料就灰常的丰富和全面了。在常见的几种解决方案中,楼猪选择了看上去非常简洁而且效率也不低的SqlBulkCopy对象的实现方式,间接实现了iBatis.net的数据导入:
(1)、基类中两个虚方法
- /// <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;
- }
- /// <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重写批量导入方法
- /// <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;
- }
- /// <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组装的时候我们借助了一个辅助类:
- 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
- {
- 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;
- }
- }
- }
- 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
- {
- 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
寻找 IBatisNet 批量插入(批量复制) 的心路历程的更多相关文章
- springMVC 接收数组参数,mybatis 接收数组参数,mybatis批量插入/批量删除案例
案例是给一个用户赋予多个权限,多个权限用其对应的主键 id 为参数,组成了 一个id数组,传给springMVC,然后springMVC传给mybatis,然后mybatis批量插入.其实类似的场景还 ...
- Dapper, 批量插入,批量更新, 以及in, like
1. 批量插入 public async Task CreateBusinessItemAsync(IEnumerable<BusinessItemsEntity> businessIte ...
- 【mybatis】mybatis中批量插入 批量更新 batch 进行insert 和 update,或者切割LIst进行批量操作
================================================================== 分别展示 mybatis 批量新增 和 批量更新 的操作: ...
- mongodb 批量改变某一列类型 比如 String改为double,insert into select 批量插入 批量修改
//type:2代表String 1.String变Double db.集合.find({"列":{$type:2}}).forEach(function(x){ x.列=pars ...
- mybatis中批量插入以及更新
1:批量插入 批量插入就是在预编译的时候,将代码进行拼接,然后在数据库执行 <insert id="batchInsert" parameterType="java ...
- sql server中的大数据的批量操作(批量插入,批量删除)
首先我们建立一个测试用员工表 ---创建一个测试的员工表--- create table Employee( EmployeeNo int primary key, --员工编号 EmployeeNa ...
- mybatis的插入与批量插入的返回ID的原理
目录 背景 底层调用方法 单个对象插入 列表批量插入 完成 背景 最近正在整理之前基于mybatis的半ORM框架.原本的框架底层类ORM操作是通过StringBuilder的append拼接的,这次 ...
- mybatis+mysql批量插入和批量更新
一.批量插入 批量插入数据使用的sql语句是: insert into table (字段一,字段二,字段三) values(xx,xx,xx),(oo,oo,oo) mybatis中mapper.x ...
- mybatis+mysql批量插入和批量更新、存在及更新
mybatis+mysql批量插入和批量更新 一.批量插入 批量插入数据使用的sql语句是: insert into table (字段一,字段二,字段三) values(xx,xx,xx),(oo, ...
- 【JDBC核心】批量插入
批量插入 批量执行 SQL 语句 当需要成批插入或者更新记录时,可以采用 Java 的批量更新机制,这一机制允许多条语句一次性提交给数据库批量处理.通常情况下比单独提交处理更有效率. JDBC 的批量 ...
随机推荐
- servlet3.0 新特性和springboot Listener和filter案例
1.filter package com.newtouch.zxf.filter; import java.io.IOException; import javax.servlet.Filter; i ...
- pycahrm使用docstrings来指定变量类型、返回值类型、函数参数类型
py里面不需要显示声明类型,这和java c这些静态语言不同,虽然python这样做少了一些代码和写代码的困难度,但还是非常多的弊端的,运行速度 代码安全, 这些都是语言本身带来的本的弊端,这些没办法 ...
- C# 调用dephi dll 实例
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Runti ...
- AngularJS------报错"The selector "app-user-item" did not match any elements"
原因:新建的组件没有在任何界面使用到 解决方法:在界面使用该组件
- 【能力提升】SQL Server常见问题介绍及高速解决建议
前言 本文旨在帮助SQL Server数据库的使用人员了解常见的问题.及高速解决这些问题.这些问题是数据库的常规管理问题,对于非常多对数据库没有深入了解的朋友提供一个大概的常见问题框架. 以下一些问题 ...
- Ubuntu 16.04服务器 配置
1. 修改用户名称:切换到root打开如下两个配置文件 sudo vim /etc/passwd 把我想改的"xxx"这个用户名改为"way"了,保存并退出 s ...
- Jackson Gson Json.simple 比较
为公司做了小任务,需要用到Java Json库,Json库我几个月之前就用过,不过那时候是跟着项目来的,延续了项目的使用习惯直接用了jackson Json,而这次我觉得好好比较一下几个常见的Json ...
- Linux od命令(以指定进制显示文件)
从“读取二进制文件”出发,到od命令的使用 在桃村实习期间,一直努力做毕业设计,我的毕业设计中有一个内容就是读取SEGY文件.在读取文件时,经常遇到的问题时你要读取浮点型数据,这时你就必须考虑你所使用 ...
- SELECT中常用的子查询操作
MySQL中的子查询 是在MySQL中经常使用到的一个操作,不仅仅是用在DQL语句中,在DDL语句.DML语句中也都会常用到子查询. 子查询的定义: 子查询是将一个查询语句嵌套在另一个查询语句中: 在 ...
- 【GIS】使用GDAL为Leaflet切图
一.参考资料 https://commenthol.github.io/leaflet-rastercoords/ https://github.com/commenthol/gdal2tiles-l ...