iBatis --> MyBatis
从 Clinton Begin 到 Google(从 iBatis 到 MyBatis,从 Apache Software Foundation 到 Google Code),Apache 开源代码项目,O/R Mapping 解决方案,基于 SQL 映射(将SQL语句映射为Java/.Net对象)的支持 Java 和 .Net 的数据访问工具和持久层框架。
iBatis 作为一个映射层,在对象(类-字段)和数据库(数据表-列)之间传递数据,并保持两者与映射层的相互独立,低耦合。
- 小巧简单、轻量级,快速开发,提供满足要求、灵活简单的解决方案
- iBATIS 提供独立于数据库的接口和 API
- 支持存储过程、内嵌的 SQL、动态 SQL
- SQL语句在单独的 XML 文件中,与程序代码分离,可移植性
- SQL 语句需要手动灵活配置,半自动化(相比全自动化的Hibernate)
iBatis 提供的持久层框架包括 SQL Maps 和 Data Access Objects(DAO),同时提供一个利用该框架开发的 JPetStore 实例。
- SQL Maps:整个iBatis Database Layer的核心价值,通过XML文件实现从实体到SQL语句的映射,显著节约数据库操作的代码量
- DAO:提供接口操作数据,隐藏实现细节
iBatis 运行方式类似 ADO.Net,连接数据库、设置参数、执行语句、获取并返回结果、关闭并释放资源。
关于 ADO.Net
改进的ADO数据访问模型,Microsoft 提供的一组用于和数据源进行交互的面向对象类库,.NET 编程环境中优先使用的数据访问接口。
- 内存表示:DataSet 对象
- 数据访问:DataTable 的 Rows 集合 索引访问
相关信息可参见:ADO.Net 入门学习;
下面直接给出 iBatis 的框架结构
关于 iBatis 的具体信息参见官网:iBatis --> MyBatis;MyBatis for .Net; MyBatis for Java;
SqlMapXxxx.config
iBatis SqlMap 配置文件(DataMapper 配置文件),类似项目的 web.config 或 app.config
- 指定 MyXxxx.xml 和 providers.config 文件的位置
- 定义 DataMapper 的其他配置选项
SQL Map 将对象持久化至关系型数据库,方便维护(无需修改代码,只需动态维护XML文件即可)
在项目中,推荐一个数据源(数据库)对应一个 SqlMapXxxx.config 文件 和 一个 providers.config 文件
- DB2:SqlMapDB2.config、providersDB2.config
- SQLServer:SqlMapSQLServer.config、providersSQLServer.config
SqlMapXxxx.config 用于将 MyXxxx.xml 文件载入到 iBatis 框架。
<?xml version="1.0" encoding="utf-8"?> <sqlMapConfig
xmlns="http://ibatis.apache.org/dataMapper"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> // 自增长属性等配置信息
<propertys>
<property />
</propertys> // 配置信息
<settings>
<setting />
</settings> // 数据库相关信息
<providers resource="bin/DBConfig/iBatis/providersDB2.config"/>
<database>
<provider name="iDb2.9"/>
<dataSource name="SourceName" connectionString="Database=xxx;User ID=xxx; Password = xxx;"/>
</database> // SQL映射信息文件
<sqlMaps>
<sqlMap resource="bin/DBConfig/SqlMap/MyXxxx.xml" />
</sqlMaps> </sqlMapConfig>
首先在该配置中,凡是引用外部文件,均可通过
- resource:推荐,从项目的根目录加载
- url:从文件的绝对路径加载
- embedded:作为程序集的资源文件加载
配置信息 <setting /> 有关参数解释如下:
maxRequests:同时执行SQL语句的最大线程数
maxSessions:同一时间内活动的最大session数
maxTransactions:同时进入SqlMapClient.startTransaction()的最大线程数
cacheModelsEnabled:全局性启用/禁用SqlMapClient的所有缓存model
lazyLoadingEnabled:全局性启用/禁用SqlMapClient的所有延迟加载
enhancementEnabled:全局性启用/禁用运行时字节码增强,优化访问JavaBean属性的性能,同时优化延迟加载性能
useStatementNamespaces:如果启用本属性,必须使用全限定名来引用mapped statement。
Mapped statement的全限定名由sql-map的名称和mapped-statement的名称合成
配置信息 <sqlMaps> + <sqlMap> 指向 SQL 映射文件。
此外,上述信息引用 DB2 数据库,如果是引用 SQLServer 数据库,配置信息如下
<providers resource="bin/DBConfig/IBatis/providersSQLServer.config"/>
<database>
<provider name="sqlServer2.0"/>
<dataSource name="SourceName" connectionString="Data Source=IP;Initial Catalog=dbName;User ID=xxx;Password=xxx;connection reset=false;"/>
</database>
当向存在自增长列的表插入数据时,需要配置如下属性
// for SQLServer
<properties>
<property key="selectKeyForXxxx" value="select @@IDENTITY as value" />
</properties>
// for SQLServer (推荐)
<properties>
<property key="selectKeyForXxxx" value="select SCOPE_IDENTITY() as value" />
</properties> // for DB2
<properties>
<property key="selectKeyForXxxx" value="VALUES IDENTITY_VAL_LOCAL()" />
</properties>
关于 SCOPE_IDENTITY() 和 @@IDENTITY 的区别,参见:http://www.cnblogs.com/MingDe/archive/2011/10/12/2208749.html
在 MyXxxx.xml 文件中使用形式如下
<insert id="insertUser" parameterClass="User">
insert into Users values(null,#userName# ,#password#,#userType#);
<selectKey resultClass="int" keyProperty="uid">
${selectKeyForXxxx}
</selectKey>
</insert>
该配置也可以直接在 MyXxxx.xml 文件的 <insert> 标签中配置。
<insert id="insertUser" parameterClass="User">
insert into Users values(null,#userName# ,#password#,#userType#);
<selectKey resultClass="int" keyProperty="uid">
SELECT @@IDENTITY AS uid // uid是数据库表列名
</selectKey>
</insert>
MyXxxx.xml
SQL 语句映射文件,iBatis 通过该 XML 文件来映射 SQL 语句的输入和输出以及语句本身。
<?xml version="1.0" encoding="utf-8" ?> <sqlMap namespace = "与文件同名即可"
xmlns="http://ibatis.apache.org/mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" > <!--别名设置-->
<alias>
<typeAlias alias="别名" type="命名空间.类名, 命名空间"/>
</alias> <!--返回值配置-->
<resultMaps>
<resultMap id="resultMapID" class="别名">
<result property="Param1" column="数据库列1" />
<result property="Param2" column="数据库列2" />
</resultMap>
</resultMaps> <!--动态SQL配置-->
<statements>
<!-- SQL公用片段, 用于组装SQL语句 -->
<sql id="sql_select_count">
select count(*)
</sql>
<sql id="sql_select_all">
select *
</sql>
<sql id ="sql_operation_where">
from ( tableName or (select语句)
where
<dynamic>
动态组装 where 条件
</dynamic>
</sql> // 增删改查(Insert/Delete/Update/Select), 根据需要自行组装SQL语句
<!-- Select -->
<select id="selectID" parameterClass="别名" resultClass="resultMapID or 特定返回值类型" remapResults="true">
<include refid="sql_select_all"/>
<include refid="sql_operation_where"/>
)temp
where 条件
</select>
<!-- Insert -->
<insert id="insertID" parameterClass="别名">
SQL语句 or 存储过程
</insert>
<!-- Update -->
<update id="updateID" parameterClass="别名">
SQL语句 or 存储过程
</update>
<!-- Delete -->
<delete id="deleteID" parameterClass="别名">
SQL语句 or 存储过程
</delete>
</statements>
</sqlMap>
下面对映射文件中的几个重要标签作解释
[0]. sqlMap
SQL 映射文件的根结点。因为 iBatis 运行时会把所有映射文件一次性加载,所以属性 namespace 命名空间必须唯一标识映射文件 。
同一命名空间下,标签的 id 属性不能重复。若不同命名空间下存在相同的标签 id,需要使用 命名空间.标签id 访问。
[1]. resultMaps + resultMap
返回值配置,iBatis 映射文件中最重要最强大的元素。
[2]. alias + typeAlias
别名配置,化繁为简,简短的别名代替完全限定的类名。
[3]. statements
动态SQL配置。动态SQL作为iBatis的强大功能,灵活的动态SQL标签、提高SQL语句的重用性。
- <sql>:SQL公用片段,可以用于组装SQL语句
- <select> <insert> <update> <delete>:增删改查标签,可以利用 <sql> 自行组装SQL语句
[4]. <![CDATA[ xxxxxx ]]>
通过 <![CDATA[……]]> 节点,可以避免SQL中与XML规范相冲突的字符对XML映射文件的合法性造成影响。
The most common conflict between SQL and XML is the greater-than and less-than symbols (><).
关于 <![CDATA[ ... ]]> 的使用,对于非数据库字段,应使用该标签包装。
[5]. parameterClass + resultClass
parameterClass 表示输入参数类型完整类名,resultClass 表示返回结果类型完整类名,两者均可通过 alias 简化。
存储过程使用 parameterMap,除存储过程外的其他 <statement> 内的标签均采用 parameterClass 和 resultClass 配置方式。
动态SQL
在利用 <sql> 标签组装增删改查标签时,动态条件写在 <dynamic>、一元标签、二元标签 和 <iterator> 中,该标签可以放在
- select 列表
- where 过滤条件
中,下面详细介绍如何拼接动态 SQL 语句以及应该注意的问题。
(a)关于直接执行 SQL 语句,务必注意以下几点
1). 属性 remapResults="true" 配置
2). 推荐使用 $sql$ or <![CDATA[ $sql$ ]]> ,但是不要用 #sql#
(b)插入类型的存储过程,推荐在存储过程的最后添加
// 返回最新主键
SELECT @@IDENTITY AS value
(C)关于验证 where 和 AND/OR 在动态SQL中的位置问题
下面给出 IBatis 调用 SQL 语句的几个简单例子
[1]. 根据 ID 查询 Name
<select id="selectNameByID" parameterClass="int" resultClass="string">
select name from dbo.StudentInfo where id=#id#
</select>
然后给出调用方式
int id = 101;
string statement = "MyXxxx.selectNameByID";
string stuName = DataOperationHelper.CallSql<string>(statement, id);
下面的代码,封装了2个方法,分别用于执行SQL语句和存储过程
/// <summary>
/// 调用SQL
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="statement"></param>
/// <param name="inParam"></param>
/// <returns></returns>
public static T CallSql<T>(string statement, object inParam)
{
T outParam = default(T); try {
outParam = iSqlMapper.QueryForObject<T>(statement, inParam);
}
catch(Exception ex) {
Logger.Error("Sql: " + ex.Message + ex.StackTrace);
throw (new Exception(ex.Message));
} Logger.Info("exec Sql " + statement + " success");
return outParam;
} /// <summary>
/// 调用存储过程
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="statement"></param>
/// <param name="inParam"></param>
/// <returns></returns>
public static T CallProc<T>(string statement, object inParam)
{
T outParam = default(T); try
{
outParam = iSqlMapper.QueryForObject<T>(statement, inParam);
}
catch (Exception ex)
{
Logger.Error("Proc: " + ex.Message + ex.StackTrace);
throw (new Exception(ex.Message));
} Logger.Info("exec Proc " + statement + " success");
return outParam;
}
执行SQL或存储过程
其实,两者没有太大区别,只是为了分开而分开。
注意,上述只是调用 QueryForObject 获取一个对象,如果是获取列表,需要调用 QueryForList。
存储过程
如果是利用 iBatis 调用存储过程,需要添加如下标签
[1]. parameterMaps + parameterMap
存储过程参数配置,标签地位等同 resultMaps + resultMap 标签。
<!-- 存储过程输入参数配置 -->
<parameterMaps>
<parameterMap id="parameterMapID" class="参数类型(Hashtable or map)">
<parameter property="Param1" column="数据库列1" direction="Input" />
<parameter property="Param2" column="数据库列2" type="string" dbType="binary" direction="Input" />
<parameter ... ... />
<parameter property="Param_x" column="数据库列x" direction="Output" />
<parameter property="Param_y" column="数据库列y" direction="Output" />
</parameterMap>
</parameterMaps>
[2]. procedure
存储过程设置,在 <statements> 标签中配置。
<statements>
<procedure id="procedureID" parameterMap="parameterMapID" resultMap="resultMapID" remapResults="true">
<![CDATA[
DatabaseName.ProcedureName (eg: RVC.Pr_QueryUserInfo)
]]>
</procedure>
</statements>
注意,如果是返回特定返回值类型,应该采用:resultClass="特定返回值类型"
[3]. remapResults
在使用 iBatis 中,务必设置该属性为 true,用于重置查询结果,否则,由于 iBatis 缓存机制的原因,会得到重复的查询结果。
除此之外,注意存储过程的 return 只能返回 int 类型,若要返回其它类型,请移步 output
OGNL 表达式
iBatis 提供强大的 OGNL 表达式来消除其他元素。
- if 语句:<if>,最常见的场景是在动态SQL中有条件地包括where子句的一部分
- choose, when, otherwise 语句:类似switch语句
- where 语句:<where>,避免出现 SELECT * FROM TabName WHERE 的情况
- foreach 语句:<foreach>,允许指定使用集合
providers.config
提供常用数据库驱动程序支持信息清单,DataMapper 在该文件中查找选定数据库的 provider 的定义。
// provider.config 配置文件结构
<?xml version="1.0" encoding="utf-8"?> <providers
xmlns="http://ibatis.apache.org/providers"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <clear/> <provider
name="DBName"
description="The description of the DB"
enabled="true or false"
default="true or false"
parameterPrefix="@"
... ...
/>
<provider
/> ... ... </providers>
注意其中的几个参数,如果多个数据库驱动 enabled="true",可以设置其中一个的 default="true" 默认启动,parameterPrefix 表示参数化SQL语句中参数的前缀。
下面分别给出一个 SQLServer 和 DB2 的示例
<provider
name="sqlServer2.0"
enabled="true"
description="Microsoft SQL Server, provider V2.0.0.0 in framework .NET V2.0"
assemblyName="System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
connectionClass="System.Data.SqlClient.SqlConnection"
commandClass="System.Data.SqlClient.SqlCommand"
parameterClass="System.Data.SqlClient.SqlParameter"
parameterDbTypeClass="System.Data.SqlDbType"
parameterDbTypeProperty="SqlDbType"
dataAdapterClass="System.Data.SqlClient.SqlDataAdapter"
commandBuilderClass=" System.Data.SqlClient.SqlCommandBuilder"
usePositionalParameters = "false"
useParameterPrefixInSql = "true"
useParameterPrefixInParameter = "true"
parameterPrefix="@"
allowMARS="false"
allowMultiQueries="true"
/>
SQLServer
<provider
name="iDb2.9"
description="IBM DB2 Provider, V 10.0"
enabled="true"
default="true"
assemblyName="IBM.Data.DB2, Culture=neutral, PublicKeyToken=7c307b91aa13d208, Custom=null"
connectionClass="IBM.Data.DB2.DB2Connection"
commandClass="IBM.Data.DB2.DB2Command"
parameterClass="IBM.Data.DB2.DB2Parameter"
parameterDbTypeClass="IBM.Data.DB2.DB2Type"
parameterDbTypeProperty="DB2Type"
dataAdapterClass="IBM.Data.DB2.DB2DataAdapter"
commandBuilderClass="IBM.Data.DB2.DB2CommandBuilder"
usePositionalParameters="true"
useParameterPrefixInSql="false"
useParameterPrefixInParameter="false"
parameterPrefix=""
allowMARS="false"
/>
DB2
基本使用
iBatis 最重要的三个文件
SqlMapXxxx.config、MyXxxx.xml、providersXxxx.config
注意,SqlMapXxxx.config、providersXxxx.config 文件应放在 DataMapper 运行时可以找到的地方。
首先,要下载 iBatis .Net 使用的 .dll 文件,可自行百度 iBatis.Net 哈
- IBatis.DataMapper.1.6.2.bin.zip
- IBatis.DataAccess.1.9.2.bin.zip
涉及的主要 .dll 文件如下
IBatisNet.Common.dll: 由DataAccess和DataMapper组成的共享程序集
IBatisNet.DataMapper.dll: DataMapper框架
IBatisNet.DataAccess.dll: DataAccess框架
IBatisNet.Common.Logging.Log4Net.dll: Log4Net集成记录器, 和Log4Net.dll配合使用
此外,下载的 ibatis.net 在解压目录下有几个 .xsd 文件,该文件是 .xml 文件的验证文件
SqlMapConfig.xsd, SqlMap.xsd, provider.xsd, DaoConfig.xsd
将其拷贝到 VS 安装目录,用于在 VS 中支持 XML 自动提示
%VsInstallDir%\xml\Schemas
在解压目录下还有几个 .xml 文件
IBatisNet.Common.xml、IBatisNet.DataMapper.xml、IBatisNet.DataAccess.xml、IBatisNet.Common.Logging.Log4Net.xml、log4net.xml
将其拷贝到 VS .NET Framework 的安装目录,用于编写代码时获得程序的API说明
C:/WINDOWS/Microsoft.NET/Framework/v4.0.30319/zh-CN
[1]. ISqlMapper
该 API 提供数据访问层涉及到的方法
// 查询方法
[1]. QueryForObject()
[2]. QueryForList()
[3]. QueryForMap()
[4]. QueryForDictionary() // 增、删、更新方法
object Insert(string statementName, object parameterObject);
int Update(string statementName, object parameterObject);
int Delete(string statementName, object parameterObject);
注意,”Map” 这个名称是 Java 里的,按 .NET 的精神应该是 Dictionary,所以 iBatis.NET同时提供了 "Dictionary" 对应的方法。
[2]. 自动结果映射
该特性可以通过三种方式使用
- 单列查询
- 固定类列表查询
- 动态列表查询
映射参数用于定义参数的有序列表,与查询语句的占位符相匹配。
[3]. iBatis初始化
private static ISqlMapper iSqlMapper = null; /// filePath: 配置文件 SqlMapXxxx.config (相对)路径
public static void StartIBatisMapXxxx(string filePath)
{
DomSqlMapBuilder builder = new DomSqlMapBuilder();
iSqlMapper = builder.Configure(filePath); // 非Web请求线程需要加上该行
iSqlMapper.SessionStore = new IBatisNet.DataMapper.SessionStore.HybridWebThreadSessionStore(iSqlMapper.Id); Logger = LogManager.GetLogger("iBatis");
}
在 iBatis 中,SqlMapper 对象默认是单例模式实现。除上述方法外,也可以通过 Mapper 类的静态 Instance() 属性实例化。
Demo 测试
下面给出一个使用 iBatis 的简单 Demo,注意一点,新建项目时务必确认 .Net 框架的版本
默认是 .NET Framework 4 Client Profile,替换为 .NET Framework 4
首先给出配置文件(包含日志记录配置信息),在 app.config或web.config 中配置
(1)直接在控制台显示
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
<sectionGroup name="iBATIS">
<section name="logging" type="IBatisNet.Common.Logging.ConfigurationSectionHandler, IBatisNet.Common" />
</sectionGroup>
</configSections> <iBATIS>
<logging>
<logFactoryAdapter type="IBatisNet.Common.Logging.Impl.ConsoleOutLoggerFA, IBatisNet.Common">
<arg key="showLogName" value="true" />
<arg key="showDataTime" value="true" />
<arg key="level" value="ALL" />
<arg key="dateTimeFormat" value="[MM-dd HH:mm:ss]" />
</logFactoryAdapter>
</logging>
</iBATIS>
</configuration>
iBATIS info in Console
务必注意,标签 <iBATIS> 万万不能写成 <iBatis>,否则无法输出
此时,配置文件中没有使用 log4net 配置,要想在程序中执行 Logger.Info/Warn 记录日志,必须初始化
IBatisNet.Common.Logging.ILog Logger =
IBatisNet.Common.Logging.LogManager.GetLogger("iBatis");
采用 IBatisNet.Common.Logging 组件初始化 Logger。
()日志输出到文件
<configuration>
<configSections>
<sectionGroup name="iBATIS">
<section name="logging" type="IBatisNet.Common.Logging.ConfigurationSectionHandler, IBatisNet.Common"/>
</sectionGroup>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
</configSections> <!-- (2). 利用log4net输出日志到文件 -->
<iBATIS>
<logging>
<logFactoryAdapter type="IBatisNet.Common.Logging.Impl.Log4NetLoggerFA, IBatisNet.Common.Logging.Log4Net">
<arg key="configType" value="inline"/>
</logFactoryAdapter>
</logging>
</iBATIS> <log4net>
<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="Log\" />
<appendToFile value= "true" />
<datePattern value= "yyyyMMdd".log"" />
<rollingStyle value= "Date" />
<staticLogFileName value="false" />
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<layout type="log4net.Layout.PatternLayout, log4net">
<param name="ConversionPattern" value="[%d{HH:mm:ss,fff}] [%t] %-5p %c-(%line) %m%n" />
</layout>
</appender> <appender name="RollingConsoleAppender" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="[%d{HH:mm:ss,fff}] [%t] %-5p %c-(%line) %m%n"/>
</layout>
</appender> <root name="iBatis">
<level value="ALL"/>
<appender-ref ref="RollingLogFileAppender"/>
<appender-ref ref="RollingConsoleAppender"/>
</root> <!-- 打印错误信息的级别
<logger name="IBatisNet.DataMapper.Configuration.Cache.CacheModel">
<level value="DEBUG"/>
</logger>
<logger name="IBatisNet.DataMapper.Configuration.Statements.PreparedStatementFactory">
<level value="DEBUG"/>
</logger>
<logger name="IBatisNet.DataMapper.LazyLoadList">
<level value="DEBUG"/>
</logger>
<logger name="IBatisNet.DataAccess.DaoSession">
<level value="DEBUG"/>
</logger>
<logger name="IBatisNet.DataMapper.SqlMapSession">
<level value="DEBUG"/>
</logger>
<logger name="IBatisNet.Common.Transaction.TransactionScope">
<level value="DEBUG"/>
</logger>
<logger name="IBatisNet.DataAccess.Configuration.DaoProxy">
<level value="DEBUG"/>
</logger>
-->
</log4net> <startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup>
</configuration>
iBatis log into file by log4net
其中,<iBATIS> 标签的 configType 属性可设置为 inline、external、file、file-watch,推荐 inline:可以直接在 App.config 或 Web.Config 中配置 log4net 节点。
务必注意,log4net 的版本,应该采用下载包中的 1.2.10.0 版本,不要用最新的 2.0.8.0 版本。
下面给出在主函数中的初始化方法
string configFilePath = "app.config";
FileInfo fileinfo = new FileInfo(configFilePath);
log4net.Config.XmlConfigurator.ConfigureAndWatch(fileinfo);
ILog Logger = LogManager.GetLogger("iBatis");
因为此处采用 log4net 记录日志,可以直接使用 log4net 组件初始化 Logger。
()iBatis 调用存储过程
利用 iBatis 调用SQL语句,在前面的 动态SQL 部分学习过,此处不再重复,下面重点学习下调用存储过程。
相关信息可以参见:iBatis 调用存储过程
首先定义一个带输出参数的存储过程,根据 ID 查询姓名
USE [Stu_sqh]
GO SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO IF exists(select from sysobjects where id=object_id('PR_QueryNameByID') and xtype='P')
DROP PROC PR_QueryNameByID
GO CREATE PROC PR_QueryNameByID(
@ID INT,
@O_RTCD INT OUT,
@O_NAME VARCHAR() OUT
)
as SET @O_RTCD = ;
SET @O_Name = ''; BEGIN
select @O_Name = NAME
FROM [Stu_sqh].[dbo].[StudentInfo]
WHERE ID = @ID;
END; RETURN ;
PR_QueryNameByID
然后,在 MyXxxx.xml 中配置
<parameterMaps>
<parameterMap id="QueryNameByIDParamMap" class="Hashtable">
<parameter property="ID" column="ID" direction="Input" />
<parameter property="O_RTCD" column="O_RTCD" direction="Output" />
<parameter property="O_NAME" column="O_NAME" direction="Output" />
</parameterMap>
</parameterMaps> <procedure id="QueryNameByID" parameterMap="QueryNameByIDParamMap" resultClass="string" remapResults="true">
<![CDATA[ PR_QueryNameByID ]]>
</procedure>
最后,调用该存储过程
Hashtable ht = new Hashtable();
ht.Add("ID", 101);
ht.Add("O_RTCD", 0);
ht.Add("O_NAME", "");
strig statement = "MyXxxx.QueryNameByID";
DataOperationHelper.CallProc<string>(statement, ht);
string stuName = (string)ht["O_NAME"];
扩展使用
(1)关于数据库查询性能提升方法
[a]. 分页查询
最实际有效。
[b]. 延迟加载
Lazy Loading,需要时再加载。在 SqlMapXxxx.config 文件中添加如下配置
// 延迟加载机制
<setting lazyLoadingEnabled="true"/>
// 字节码强制机制
<setting enhancementEnabled = "true" />
其中,字节码强制机制有利于 Lazy Loading 性能改进。
[c]. Cache机制
利用 Cache 查询,对于更新次数较少的数据比较有效,但是必须谨慎使用Cache机制,避免出现第三方对数据的更新导致脏数据。
在 SqlMapXxxx.config 文件中添加如下配置
// Cache机制
<setting cacheModelsEnabled="true"/>
结合 CacheModel,在配置文件中,主要是 type、readOnly、serialize 三个属性
<cacheModel id="product-cache" type ="LRU" readOnly="true" serialize="false" />
其中,readOnly 表示缓存数据只读,读性能好,但数据更新会降低效率;serialize = true 表示全局数据缓存,反之表示局部缓存、仅对当前 Session 有效。
对于 type,表示缓存实现机制,有 3 种类型
- FIFO:先进先出型缓存,最先放入 Cache 中的数据将被最先清除
- LRU:最近最少使用型缓存,当 Cache 达到预先设定的最大容量时,iBatis 会按照 “最少使用” 原则将使用频率最少的对象从缓冲中清除
- Memory:通常采用 WEAK 的 MEMORY 型 Cache 配置
此处给出一个 CacheModel 的配置结点示例
<cacheModels>
<cacheModel id = "person-cache" implementation = "MEMORY" >
<flushInterval hours = "24"/>
<flushOnExecute statement = "UpdateAccountViaInlineParameters"/>
<flushOnExecute statement = "UpdateAccountViaParameterMap"/>
<property name = "Type" value = "Weak"/>
</cacheModel>
</cacheModels>
对 CacheModel 的几个重要元素说明如下
flushInterval:设定缓存有效期
CacheSize:当前Cachemodel中最大的数据对象数量
flushOnExecute:指定执行特定的Statement时,将缓存清空
特别注意 flushOnExecute,如 Update 操作将更新数据库信息,会导致缓存中的数据信息与数据库中的实际数据信息发生偏差,因此必须将缓存清空避免脏数据出现。
然后,在 <select> 标签中,对查询到的数据采用 cache 机制
<statements>
<select id="SelectAllPerson" resultMap="PersonInfoResult" cacheModel="person-cache">
select ID, NAME, BIRTH_DATE, WEIGHT_KG, HEIGHT_M from PERSON
</select>
</statements>
通过该 id 获取到数据后,使用 CacheModel 的 “person-cache” 进行缓存,当再次调用该 id 数据查询时,直接从缓存中取数据,不用再去查询数据库。
(2)关于避免 N+1查询 问题
首先了解什么是 N+1 查询问题,主要考虑在大数据集的情况,特别是对于主从表(父子表)的查询,容易产生N+1查询问题,由于试图加载父记录的多个子记录引起的。在查询父记录时,只需要1条语句,若返回N条记录,那么就需要再执行N条语句来查询子记录,引发"N+1"问题。
如何避免,提供 2 种方式:
- 延迟加载:配置属性 lazyLoad="true",并没完全解决数据库I/O问题,最坏情况下,对数据库的访问次数与非延迟加载是一样的。
- 连接语句(join)方式来完全避免N+1查询问题:在 iBatis 中使用 groupBy 特性:方法快,但是内存占用大
- 自定义组件 RowHandler
相关信息参见:iBatis系列-数据库查询;
(3)获取特定类型的数据
[a]. 获取 SQL 语句
在某些场景下,需要获取执行的动态SQL语句,如,SQL语句执行失败、将该语句打印到日志
public static string GetRuntimeSQL(ISqlMapper iSqlMapper, string selectFun, object inParam)
{
string sql = string.Empty;
try
{
IMappedStatement iMappedStatement = iSqlMapper.GetMappedStatement(selectFun);
ISqlMapSession iSession = iSqlMapper.CreateSqlMapSession();
RequestScope requestScope = iMappedStatement.Statement.Sql.GetRequestScope(iMappedStatement, inParam, iSession);
iMappedStatement.PreparedCommand.Create(requestScope, iSession, iMappedStatement.Statement, inParam); sql = requestScope.PreparedStatement.PreparedSql;
IDbCommand cmd = requestScope.IDbCommand;
foreach (IDbDataParameter it in cmd.Parameters) {
sql += "; [" + it.ParameterName + " = " + it.Value + "] ";
}
iSession.CloseConnection();
}
catch(Exception ex)
{
Logger.Info("GetRuntimeSQL exec failed: " + ex.Message + ex.StackTrace);
return null;
}
return sql;
}
[b]. 获取存储过程
获取方法类似获取 SQL 语句,额外维护一个参数方向的字典集,输出信息会更加明确
proc = requestScope.PreparedStatement.PreparedSql; // 存储过程名字
IDbCommand cmd = requestScope.IDbCommand;
foreach (IDbDataParameter it in cmd.Parameters) {
proc += "; [" + it.ParameterName + " = " + it.Value + "] ";
}
iSession.CloseConnection();
具体方法可参见:iBatis.Net实现返回DataTable和DataSet对象
[c]. 返回 DataSet 类型数据
在某些情况下,需要返回 DataSet 格式的数据,如果返回 DataTable,则 dataSet.Table[];
public static DataSet QueryForDataSet(ISqlMapper iSqlMapper, string selectFun, object inParam)
{
DataSet dataSet = new DataSet();
try
{
IMappedStatement iMappedStatement = iSqlMapper.GetMappedStatement(selectFun);
ISqlMapSession iSession = iSqlMapper.CreateSqlMapSession();
RequestScope requestScope = iMappedStatement.Statement.Sql.GetRequestScope(iMappedStatement, inParam, iSession);
iMappedStatement.PreparedCommand.Create(requestScope, iSession, iMappedStatement.Statement, inParam); FieldInfo info = requestScope.IDbCommand.GetType().GetField("_innerDbCommand", BindingFlags.NonPublic | BindingFlags.Instance);
IDbCommand cmd = (IDbCommand)info.GetValue(requestScope.IDbCommand);
IDbDataAdapter adapter = iSession.CreateDataAdapter(cmd);
adapter.Fill(dataSet);
}
catch (Exception ex)
{
Logger.Info("QueryForDataSet exec failed: " + ex.Message + ex.StackTrace);
return null;
}
return dataSet;
}
注意,在 iBatis 1.6 版本之后,用 DbCommandDecorator 包装了 DbCommand,直接
iSession.CreateDataAdapter(requestScope.IDbCommand).Fill(ds)
会提示
无法将类型为“IBatisNet.DataMapper.Commands.DbCommandDecorator”的对象强制转换为类型“System.Data.SqlClient.SqlCommand”
可以采用反射的方法,从 DbCommandDecorator 中取出 innerDbCommand 字段即可。
下面给出调用方式和相应的配置
<select id="selectInfoByID" parameterClass="int" resultClass="System.Data.DataSet">
select * from dbo.StudentInfo where id=#id#
</select> int id = 101;
string statement = "MyXxxx.selectInfoByID";
ISqlMapper iSqlMapper = DataOperationHelper.iSqlMapper;
DataSet ds = DataOperationHelper.QueryForDataSet(iSqlMapper, statement, id);
Console.WriteLine(ds.GetXml());
注意,务必写成 System.Data.DataSet,不能简写。
此处,返回 DataSet/DataTable,并没有用到 iBatis 的内部查询方法,区别与 CallSql 和 CallProc。
调用存储过程返回 DataSet/DataTable 可能会遇到的问题,参见:http://blog.csdn.net/netjxz/article/details/1675430
使用 iBatis 应注意的问题
[1]. # 与 $
- # 是内联参数,可以进行编译、类型匹配,安全性能好,$ 是文本替换,不支持数据类型匹配;
- # 可用于变量替换,$ 仅仅是字符串拼接、存在 SQL 注入风险;
- 对于可能为整型变量可能为字符串变量的地方,务必使用 #,对于一定是整型变量的地方,可以使用 $;
对于变量,推荐使用 #,可以有效防止 SQL 注入。
update #tableName# set STATUS = #status# where ID = #id#
注意注意再注意,重要的事情说3遍,谨慎使用替换($)语法。
[2]. iBatis 的 .dll 文件与 VS .NET FrameWork 4.0 版本不匹配的问题
问题:项目中引用了 .dll 文件,但是编译运行报错:不存在相关的 .dll 文件
解决方法:将 VS 的.NET FrameWork 4.0 框架改为 .NET FrameWork 3.5
[3]. 提示数据库驱动程序不对
问题:引用的 System.Data.SqlClient.dll 版本和 providers.config 中记录的版本不一致
解决方法:通过 Assembly 读取 System.Data.SqlClient.dll 文件的 FullName,填入providers.config 文件相应驱动的 assemblyName 参数
System.Reflection.Assembly.LoadFile(@"...\System.Data.OracleClient.dll").FullName
关于在 Java 中使用 iBatis 可参见上述在 .Net 中的用法,有待于进一步学习。
参考
iBatis .Net - 博客园;框架:Ibatis.Net学习;
iBATIS.NET - DataMapper Application Framework - DataMapper Developer Guide;
深入分析 iBATIS Java 框架之系统架构与映射原理;
iBatis --> MyBatis的更多相关文章
- ibatis mybatis sql语句配置 符号不兼容 大于号 小于号<!CDATA[ ]>
ibatis mybatis sql语句配置 符号不兼容 大于号 小于号<!CDATA[ ]> 因为这个是xml格式的,所以不允许出现类似">"这样的字符,但是都 ...
- paip.环境配置整合 ibatis mybatis proxool
paip.环境配置整合 ibatis mybatis proxool 索引: ///////////1.调用 ///////////////2. ibatis 主设置文件 com/mijie/ho ...
- iBatis & myBatis & Hibernate 要点记录
iBatis & myBatis & Hibernate 要点记录 这三个是当前常用三大持久层框架,对其各自要点简要记录,并对其异同点进行简单比较. 1. iBatis iBatis主 ...
- Ibatis/Mybatis模糊查询
Ibatis/Mybatis模糊查询 根据网络内容整理 Ibatis中 使用$代替#.此种方法就是去掉了类型检查,使用字符串连接,不过可能会有sql注入风险. Sql代码 select * from ...
- ibatis/mybatis显示sql语句 log4j.properties配置文件
将ibatis/mybatis log4j运行级别调到DEBUG可以在控制台打印出ibatis运行的sql语句,方便调试: ### 设置Logger输出级别和输出目的地 ### log4j.rootL ...
- JDBC、ibatis(mybatis)、Hibernate有什么不同?
①JDBC编程流程固定,同时将sql语句和java代码混在了一起,经常需要拼凑sql语句,细节很繁琐: ②ibatis(mybatis)它不完全是一个ORM框架,因为MyBatis需要程序员自己编写S ...
- MySQL 存储过程实例 与 ibatis/mybatis/hibernate/jdbc 如何调用存储过程
虽然MySQL的存储过程,一般情况下,是不会使用到的,但是在一些特殊场景中,还是有需求的.最近遇到一个sql server向mysql迁移的项目,有一些sql server的存储过程需要向mysql迁 ...
- SqlMapConfig.xml中的setting属性 Ibatis mybatis
<settingscacheModelsEnabled="true"lazyLoadingEnabled="false"enhancementEnable ...
- [Done]ibatis/mybatis: java.lang.NoSuchMethodException
异常描述: Caused by: org.apache.ibatis.reflection.ReflectionException: Error instantiating interface cn. ...
随机推荐
- 【2018沈阳赛区网络预选赛J题】Ka Chang【分块+DFS序+线段树】
题意 给出一个有根树(根是1),有n个结点.初始的时候每个结点的值都是0.下面有q个操作,操作有两种,操作1.将深度为L的点的值全部增加X.操作2.查询以x为根的子树的结点值得和. 其中N,Q< ...
- 解决"authentication token manipulation error"
昨天安装是Ubuntu Server. 配置好了几个软件后就忘记继续了...今天打开,居然忘记了密码...真是的.. 后来还是要改了. 不想重新弄什么的了..百度了下怎么改密码...然后就有一篇 ...
- ubuntu18 tensorflow cpu fast_rcnn
(flappbird) luo@luo-All-Series:~/MyFile/TensorflowProject/tf-faster-rcnn/lib$ makepython setup.py bu ...
- 29-中国剩余定理CRT
https://blog.csdn.net/u010468553/article/details/38346195 中国剩余定理[数论] 2014年08月02日 12:55:59 阅读数:2351 中 ...
- 两个线程并发执行以下代码,假设a是全局变量,那么以下输出______是不可能的?
3.两个线程并发执行以下代码,假设a是全局变量,那么以下输出______是不可能的? void foo(){ ++a; printf("%d ",a);}A.3 2 ...
- C#中利用LINQ to XML与反射把任意类型的泛型集合转换成XML格式字符串
在工作中,如果需要跟XML打交道,难免会遇到需要把一个类型集合转换成XML格式的情况.之前的方法比较笨拙,需要给不同的类型,各自写一个转换的函数.但是后来接触反射后,就知道可以利用反射去读取一个类型的 ...
- 白盒测试实践项目(day5)
在这几天的工作下,小组成员都基本完成了各自所负责的内容. 李建文同学完成提交了代码复审相关文档后,也经过小组的补充,彻底完成. 汪鸿同学使用FIndBugs工具完成了静态代码的测试,并且也完成了静态代 ...
- myisam innodb memory 区别(2)
1.区别:1) MyISAM管理非事务表.提供高速存储和检索,以及全文搜索能力.MyISAM在所有MySQL配置里被支持,是默认的存储引擎,除非配置MySQL默认使用另外一个引擎.2)MEMORY存储 ...
- css3之transform-origin
transform-origin属性平时似乎用得很少,它决定了变换时依赖的原点.基本的属性特性可以参考CSS手册. 如果在H5动画项目中,用到旋转的话,它还是不能小觑的. 假如我们做一个秋千效果 其实 ...
- 关于利用word发布文章到博客
目前大部分的博客作者在写博客这件事情上都会遇到以下3个痛点:1.所有博客平台关闭了文档发布接口,用户无法使用Word,Windows Live Writer等工具来发布博客.2.发布到博客或公众号平台 ...