在 IBatisNet 中实现批处理

   在 IBatisNet 没有 IBatis4Java 的 startBatch() 函数,这让我们批量插入大量数据时很受困扰。本文介绍了如何在 IBatisNet 和 ADO.Net 中批量插入数据。
  说到批量插入,我们有以下解决方案:
   1) 直接执行用 SqlCommand  执行 INSERT 语句,一条一条的插入。无疑,这样效率最低。
   2) 把拼接INSERT 语句,一次插入多条;这样性能不好说,最大的问题就是 SqlCommand  有参数限制(2100个)。
   3) 利用 SqlBulkCopy 或SqlDataAdapter 的 Update 方法,可以实现批量的更新/插入,但是数据是基于  DataRow 的,并且最终 DataRow 还是要转换为 SqlCommand,对于 IBatisNet Mapping,显然我们不想这样做。
  最好能怎么样呢?
  我的思路就是批量执行 SqlCommand,因为 IBatisNet 最终也需要转化 statement 为 DbCommand,因此只要得到把 statement 和 parameters 混合以后的 SqlCommand,就可以了。
   
  首先,我们要能够批量执行  SqlCommand,这里是直接反射调用 SqlDataAdapter 的批处理函数实现的,这些方法受保护,所以只能反射调用。
  代码如下:


ExpandedBlockStart.gif ContractedBlock.gif /**/ /**
InBlock.gif<code>
InBlock.gif  <revsion>$Rev: 34 $</revision>
InBlock.gif  <owner name="Zealic" mail="rszealic@gmail.com" />
InBlock.gif</code>
ExpandedBlockEnd.gif*
*/

None.gif
using  System;
None.gif
using  System.Collections.Generic;
None.gif
using  System.Data;
None.gif
using  System.Data.SqlClient;
None.gif
using  System.Reflection;
None.gif
None.gif
None.gif
namespace  YourApplicationOrLibrary.Data
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
ExpandedSubBlockStart.gifContractedSubBlock.gif    
/**//// <summary>
InBlock.gif    
/// 为 Sql Server 提供批量处理操作。
ExpandedSubBlockEnd.gif    
/// </summary>

InBlock.gif    public class SqlBatcher
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
private MethodInfo m_AddToBatch;
InBlock.gif        
private MethodInfo m_ClearBatch;
InBlock.gif        
private MethodInfo m_InitializeBatching;
InBlock.gif        
private MethodInfo m_ExecuteBatch;
InBlock.gif        
private SqlDataAdapter m_Adapter;
InBlock.gif        
private bool _Started;
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//// <summary>
InBlock.gif        
/// 构造一个新的 SqlBatcher。
ExpandedSubBlockEnd.gif        
/// </summary>

InBlock.gif        public SqlBatcher()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            Type type 
= typeof(SqlDataAdapter);
InBlock.gif            m_AddToBatch 
= type.GetMethod("AddToBatch", BindingFlags.NonPublic | BindingFlags.Instance);
InBlock.gif            m_ClearBatch 
= type.GetMethod("ClearBatch", BindingFlags.NonPublic | BindingFlags.Instance);
InBlock.gif            m_InitializeBatching 
= type.GetMethod("InitializeBatching", BindingFlags.NonPublic | BindingFlags.Instance);
InBlock.gif            m_ExecuteBatch 
= type.GetMethod("ExecuteBatch", BindingFlags.NonPublic | BindingFlags.Instance);
ExpandedSubBlockEnd.gif        }

InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//// <summary>
InBlock.gif        
/// 获得批处理是否正在批处理状态。
ExpandedSubBlockEnd.gif        
/// </summary>

InBlock.gif        public bool Started
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif            
get dot.gifreturn _Started; }
ExpandedSubBlockEnd.gif        }

InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//// <summary>
InBlock.gif        
/// 开始批处理。
InBlock.gif        
/// </summary>
ExpandedSubBlockEnd.gif        
/// <param name="connection">连接。</param>

InBlock.gif        public void StartBatch(SqlConnection connection)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
if (_Started) return;
InBlock.gif            SqlCommand command 
= new SqlCommand();
InBlock.gif            command.Connection 
= connection;
InBlock.gif            m_Adapter 
= new SqlDataAdapter();
InBlock.gif            m_Adapter.InsertCommand 
= command;
InBlock.gif            m_InitializeBatching.Invoke(m_Adapter, 
null);
InBlock.gif            _Started 
= true;
ExpandedSubBlockEnd.gif        }

InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//// <summary>
InBlock.gif        
/// 添加批命令。
InBlock.gif        
/// </summary>
ExpandedSubBlockEnd.gif        
/// <param name="command">命令</param>

InBlock.gif        public void AddToBatch(IDbCommand command)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
if (!_Started) throw new InvalidOperationException();
ExpandedSubBlockStart.gifContractedSubBlock.gif            m_AddToBatch.Invoke(m_Adapter, 
new object[1dot.gif{ command });
ExpandedSubBlockEnd.gif        }

InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//// <summary>
InBlock.gif        
/// 执行批处理。
InBlock.gif        
/// </summary>
ExpandedSubBlockEnd.gif        
/// <returns>影响的数据行数。</returns>

InBlock.gif        public int ExecuteBatch()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
if (!_Started) throw new InvalidOperationException();
InBlock.gif            
return (int)m_ExecuteBatch.Invoke(m_Adapter, null);
ExpandedSubBlockEnd.gif        }

InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//// <summary>
InBlock.gif        
/// 结束批处理。
ExpandedSubBlockEnd.gif        
/// </summary>

InBlock.gif        public void EndBatch()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
if (_Started)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                ClearBatch();
InBlock.gif                m_Adapter.Dispose();
InBlock.gif                m_Adapter 
= null;
InBlock.gif                _Started 
= false;
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**//// <summary>
InBlock.gif        
/// 清空保存的批命令。
ExpandedSubBlockEnd.gif        
/// </summary>

InBlock.gif        public void ClearBatch()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
if (!_Started) throw new InvalidOperationException();
InBlock.gif            m_ClearBatch.Invoke(m_Adapter, 
null);
ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

None.gif


然后是我们需要一个批量插入的模板 statement
None.gif < insert  id ="SaveArray"  parameterClass ="TelnetRecord" >
None.gif  INSERT INTO [TelnetRecord]([state],[file_name]) VALUES(#State#,#FileName#)
None.gif
</ insert >

最后是重点,转化 statement 和 parameterObject 为 SqlCommand 并加入批处理操作执行。
None.gif public   class  TelnetRecordDao
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif  
//由于IBatisNet 用 DbCommandDecorator 包装了 DbCommand,因此必须使用反射取得该字段
InBlock.gif
  private static readonly FieldInfo m_InnerCommandField = typeof(IBatisNet.DataMapper.Commands.DbCommandDecorator).GetField("_innerDbCommand", BindingFlags.Instance | BindingFlags.NonPublic);
InBlock.gif  private SqlBatcher m_Batcher = new SqlBatcher();
InBlock.gif
InBlock.gif  
// 转化 IBatis 包装后的 DbCommand 为 原始的 DbCommand
InBlock.gif
  private IDbCommand GetCommand(IDbCommand ibatCommand)
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif      
return (IDbCommand)m_InnerCommandField.GetValue(ibatCommand);
ExpandedSubBlockEnd.gif  }

InBlock.gif
InBlock.gif  
// 批量保存
InBlock.gif
  public int Save(TelnetRecord[] recordArray)
ExpandedSubBlockStart.gifContractedSubBlock.gif  
dot.gif{
InBlock.gif      
if (recordArray == null)
InBlock.gif          
throw new ArgumentNullException("recordArray");
InBlock.gif      
if (recordArray.Length < 1)
InBlock.gif          
throw new ArgumentException("recordArray");
InBlock.gif      ISqlMapper mapper 
= Mapper.Instance();
InBlock.gif      ISqlMapSession session 
= mapper.LocalSession;
InBlock.gif      IMappedStatement mappedStatment 
= mapper.GetMappedStatement("SaveArray");
InBlock.gif      IStatement st 
= mappedStatment.Statement;
InBlock.gif      IPreparedCommand pc 
= mappedStatment.PreparedCommand;
InBlock.gif
InBlock.gif      
// 执行批处理命令
InBlock.gif
      m_Batcher.StartBatch(session.Connection as SqlConnection);
InBlock.gif      RequestScope request 
= st.Sql.GetRequestScope(mappedStatment, recordArray[0], session);
InBlock.gif      
foreach (TelnetRecord record in recordArray)
ExpandedSubBlockStart.gifContractedSubBlock.gif      
dot.gif{
InBlock.gif          pc.Create(request, session, st, record);
InBlock.gif          m_Batcher.AddToBatch(GetCommand(request.IDbCommand));
ExpandedSubBlockEnd.gif      }

InBlock.gif      session.OpenConnection();
InBlock.gif      
int ret = m_Batcher.ExecuteBatch();
InBlock.gif      m_Batcher.EndBatch();
InBlock.gif      
return ret;
ExpandedSubBlockEnd.gif  }

ExpandedBlockEnd.gif}

None.gif


经测试,在我的老牛车上速度良好,插入三万条14个字段的记录只需要几秒。
即使不需要使用 IBatisNet,你也也可以使用 SqlBatcher 完成常规的批量的命令执行。
搜了下 cnblogs 和 Google ,没有发现 IBatisNet 对于这个问题的解决方法,于是就自己解决并共享,希望对各位有用。



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值