iBatis.NET是我接触Nhibernate之后的第二个ORM框架,我觉得这个框架具有小巧玲珑的特点。如果你想自己开发一个ORM框架的话,我觉得iBatis.net肯定是你必须要熟读的,因为它很简洁。如果你深入了解它,你会觉得这种ORM框架的出现是必然的,因为它很接近人们在探索ORM框架的最基本想法。
我们在自学iBatis.net的时候,都会参考NPetShop这个经典的开源代码。里面有一些核心的类,如ServiceConfig和BaseSqlMapDao,理解这些类对理解iBatis.net的核心是非常重要的。
ibatis.net组件中的核心类:
IBatisNet.DataAccess组件中的DaoManager类:
using IBatisNet.Common; using IBatisNet.Common.Utilities; using IBatisNet.DataAccess.Interfaces; using IBatisNet.DataAccess.SessionStore; using System; using System.Collections.Specialized; using System.Data; using System.Reflection; namespace IBatisNet.DataAccess { public class DaoManager : IDaoManager { public const string DEFAULT_CONTEXT_NAME = "_DEFAULT_CONTEXT_NAME"; protected static HybridDictionary DaoContextMap; public string Id { get; set; } public IDalSession LocalDaoSession { get; } public IDataSource LocalDataSource { get; } public ISessionStore SessionStore { set; } public IDao this[Type daoInterface] { get; } public IDalSession BeginTransaction(); public IDalSession BeginTransaction(IsolationLevel isolationLevel); public void CloseConnection(); public void CommitTransaction(); [Obsolete("This method will be removed in a future version, use DomDaoManagerBuilder.Configure.", false)] public static void Configure(); [Obsolete("This method will be removed in a future version, use DomDaoManagerBuilder.Configure.", false)] public static void Configure(string resource); [Obsolete("This method will be removed in a future version, use DomDaoManagerBuilder.Configure.", false)] public static void ConfigureAndWatch(ConfigureHandler configureDelegate); [Obsolete("This method will be removed in a future version, use DomDaoManagerBuilder.Configure.", false)] public static void ConfigureAndWatch(string resource, ConfigureHandler configureDelegate); public IDao GetDao(Type daoInterface); public DaoSession GetDaoSession(); public static IDaoManager GetInstance(); public static IDaoManager GetInstance(IDao dao); public static IDaoManager GetInstance(string contextName); public bool IsDaoSessionStarted(); public IDalSession OpenConnection(); public IDalSession OpenConnection(string connectionString); public void RollBackTransaction(); } }
SqlMapDaoSession类:
using IBatisNet.DataAccess; using IBatisNet.DataMapper; using System; using System.Data; namespace IBatisNet.DataAccess.DaoSessionHandlers { public class SqlMapDaoSession : DaoSession { public SqlMapDaoSession(DaoManager daoManager, ISqlMapper sqlMap); public override IDbConnection Connection { get; } public override IDataSource DataSource { get; } public override bool IsTransactionStart { get; } public ISqlMapper SqlMap { get; } public override IDbTransaction Transaction { get; } public override void BeginTransaction(); public override void BeginTransaction(bool openConnection); public override void BeginTransaction(IsolationLevel isolationLevel); public override void BeginTransaction(string connectionString); public override void BeginTransaction(bool openConnection, IsolationLevel isolationLevel); public override void BeginTransaction(string connectionString, IsolationLevel isolationLevel); public override void BeginTransaction(string connectionString, bool openConnection, IsolationLevel isolationLevel); public override void CloseConnection(); public override void CommitTransaction(); public override void CommitTransaction(bool closeConnection); public override void Complete(); public override IDbCommand CreateCommand(CommandType commandType); public override IDbDataAdapter CreateDataAdapter(); public override IDbDataAdapter CreateDataAdapter(IDbCommand command); public override IDbDataParameter CreateDataParameter(); public override void Dispose(); public override void OpenConnection(); public override void OpenConnection(string connectionString); public override void RollBackTransaction(); public override void RollBackTransaction(bool closeConnection); } }
IBatisNet.DataMapper 组件中的DataMapper类:
using IBatisNet.Common; using IBatisNet.Common.Utilities.Objects; using IBatisNet.Common.Utilities.Objects.Members; using IBatisNet.DataMapper.Configuration.Cache; using IBatisNet.DataMapper.Configuration.ParameterMapping; using IBatisNet.DataMapper.Configuration.ResultMapping; using IBatisNet.DataMapper.DataExchange; using IBatisNet.DataMapper.MappedStatements; using IBatisNet.DataMapper.SessionStore; using IBatisNet.DataMapper.TypeHandlers; using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Data; namespace IBatisNet.DataMapper { public class SqlMapper : ISqlMapper { public SqlMapper(IObjectFactory objectFactory, AccessorFactory accessorFactory); public AccessorFactory AccessorFactory { get; } public DataExchangeFactory DataExchangeFactory { get; } public IDataSource DataSource { get; set; } public string Id { get; } public bool IsCacheModelsEnabled { get; set; } public bool IsSessionStarted { get; } public ISqlMapSession LocalSession { get; } public HybridDictionary MappedStatements { get; } public IObjectFactory ObjectFactory { get; } public HybridDictionary ParameterMaps { get; } public HybridDictionary ResultMaps { get; } public ISessionStore SessionStore { set; } public TypeHandlerFactory TypeHandlerFactory { get; } public void AddCache(CacheModel cache); public void AddMappedStatement(string key, IMappedStatement mappedStatement); public void AddParameterMap(ParameterMap parameterMap); public void AddResultMap(IResultMap resultMap); public ISqlMapSession BeginTransaction(); public ISqlMapSession BeginTransaction(bool openConnection); public ISqlMapSession BeginTransaction(IsolationLevel isolationLevel); public ISqlMapSession BeginTransaction(string connectionString); public ISqlMapSession BeginTransaction(bool openNewConnection, IsolationLevel isolationLevel); public ISqlMapSession BeginTransaction(string connectionString, IsolationLevel isolationLevel); public ISqlMapSession BeginTransaction(string connectionString, bool openNewConnection, IsolationLevel isolationLevel); public void CloseConnection(); public void CommitTransaction(); public void CommitTransaction(bool closeConnection); public ISqlMapSession CreateSqlMapSession(); public ISqlMapSession CreateSqlMapSession(string connectionString); public int Delete(string statementName, object parameterObject); public void FlushCaches(); public CacheModel GetCache(string name); public string GetDataCacheStats(); public IMappedStatement GetMappedStatement(string id); public ParameterMap GetParameterMap(string name); public IResultMap GetResultMap(string name); public object Insert(string statementName, object parameterObject); public ISqlMapSession OpenConnection(); public ISqlMapSession OpenConnection(string connectionString); public IDictionary QueryForDictionary(string statementName, object parameterObject, string keyProperty); public IDictionary<K, V> QueryForDictionary<K, V>(string statementName, object parameterObject, string keyProperty); public IDictionary QueryForDictionary(string statementName, object parameterObject, string keyProperty, string valueProperty); public IDictionary<K, V> QueryForDictionary<K, V>(string statementName, object parameterObject, string keyProperty, string valueProperty); public IDictionary<K, V> QueryForDictionary<K, V>(string statementName, object parameterObject, string keyProperty, string valueProperty, DictionaryRowDelegate<K, V> rowDelegate); public IList<T> QueryForList<T>(string statementName, object parameterObject); public IList QueryForList(string statementName, object parameterObject); public void QueryForList<T>(string statementName, object parameterObject, IList<T> resultObject); public void QueryForList(string statementName, object parameterObject, IList resultObject); public IList<T> QueryForList<T>(string statementName, object parameterObject, int skipResults, int maxResults); public IList QueryForList(string statementName, object parameterObject, int skipResults, int maxResults); public IDictionary QueryForMap(string statementName, object parameterObject, string keyProperty); public IDictionary QueryForMap(string statementName, object parameterObject, string keyProperty, string valueProperty); public IDictionary QueryForMapWithRowDelegate(string statementName, object parameterObject, string keyProperty, string valueProperty, DictionaryRowDelegate rowDelegate); public object QueryForObject(string statementName, object parameterObject); public T QueryForObject<T>(string statementName, object parameterObject); public object QueryForObject(string statementName, object parameterObject, object resultObject); public T QueryForObject<T>(string statementName, object parameterObject, T instanceObject); [Obsolete("This method will be remove in future version.", false)] public PaginatedList QueryForPaginatedList(string statementName, object parameterObject, int pageSize); public IList<T> QueryWithRowDelegate<T>(string statementName, object parameterObject, RowDelegate<T> rowDelegate); public IList QueryWithRowDelegate(string statementName, object parameterObject, RowDelegate rowDelegate); public void RollBackTransaction(); public void RollBackTransaction(bool closeConnection); public int Update(string statementName, object parameterObject); } }
IBatisNet.Common组件中的IBatisNetException类,处理异常信息的类。
using System; using System.Runtime.Serialization; namespace IBatisNet.Common.Exceptions { [Serializable] public class IBatisNetException : ApplicationException { public IBatisNetException(); public IBatisNetException(Exception ex); public IBatisNetException(string message); protected IBatisNetException(SerializationInfo info, StreamingContext context); public IBatisNetException(string message, Exception inner); } }
通过列举上面的组件的类,我们大概可以分析出ibatis.net三大组件的作用:
IBatisNet.DataAccess,是访问数据库和与数据库交互的核心组件。
IBatisNet.DataMapper,提供数据访问的接口。将SQL从原来硬编码中分离到.xml文件中去。
查询出来的结果通过Map, List, Object方式返回调用层。.NET 2.0推出之后,iBatisNet的DataMapper也支持了泛型,
具有了更强的类型支持。其简单易上手的动态SQL(通过xml标签来配置),使得iBatisNet更加灵活好用。
/// <summary>
/// iBatis.NET的核心类 ServiceConfig.
/// </summary>
public class ServiceConfig
{
static private object synRoot = new object(); //建立一个object对象,在處理多線程的同步時非常有用
//3:定义一个变量instance来存储创建好的类实例,因为这个变量要在静态方法中使用,所以需要加上static修饰
static private ServiceConfig instance;
private DaoManager daoManager = null; //定义一个DaoManager类型的daoManager字段
///<summary>
///1:私有化构造方法,好在内部控制创建实例的数目
///</summary>
private ServiceConfig() { }
//2:定义一个方法来为客户端提供类实例,这个方法需要定义成类方法(即静态方法),也就是要加上static
static public ServiceConfig GetInstance()
{
if (instance == null)
{
//在多线程的程序中,多个线程同时访问单例类,调用GetInstance()方法,会有可能造成创建多个实例在这种情况下,给进行加一把锁来处理
//lock是确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定代码,则它将一直等,直到对象被释放。
lock (synRoot)
{
//判断存储实例的变量是否有值
if (instance == null)
{
try
{
//定义ConfigureHander事件的一个实例handler,并调用Service.Reset作为参数,该事件的作用是
IBatisNet.Common.Utilities.ConfigureHandler handler = new ConfigureHandler(ServiceConfig.Reset);
IBatisNet.DataAccess.Configuration.DomDaoManagerBuilder builder = new DomDaoManagerBuilder();
builder.ConfigureAndWatch("dao.config", handler);
instance = new ServiceConfig(); //如果Instance为空,就创建一个类实例,并把值赋给存储类实例的变量instance.
//返回一个DaoManager实例,"SqlMapDao"为dao.config文件 context 节点id的值
instance.daoManager = (DaoManager)DaoManager.GetInstance("SqlMapDao");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
}
return instance; //如果Instance不为空,那就直接使用。
}
///<summary>
///Reset the singleton
///<summary>
///<remarks>
///Must verity ConfigureHandler signature.
///</remarks>
///<param name="obj">
///</param>
static public void Reset(object obj)
{
instance = null;
}
//定义一个属性DaoManager,它返回一个DaoManager类的一个实例
public DaoManager DaoManager
{
get
{
return daoManager;
}
}
}
现在让我们看看ServiceConfig类的具体作用。下面定义一个服务类NewsService,我们通过 IbatisNet.ServiceConfig.GetInstance().DaoManager返回一个DaoManager的一个实例。再通过这个实例进行对数据库的操作。
public class NewsService
{
#region Private Fields
private INewsDao newsDao;
private static NewsService instance = new NewsService();
private DaoManager daoManager = null;
#endregion
public static NewsService GetInstance()
{
return instance;
}
public NewsService()
{
daoManager = IbatisNet.ServiceConfig.GetInstance().DaoManager;
newsDao = daoManager.GetDao(typeof(INewsDao)) as INewsDao;
}
public object InsertNews(Domain.News news)
{
return this.newsDao.InsertNews(news);
}
public Domain.News GetNewsById(float? newsId)
{
return this.newsDao.GetNewsById(newsId);
}
public IList<Domain.News> GetNewsList()
{
return this.newsDao.GetNewsList();
}
public IList<Domain.News> GetNewsListByParameter(Parameter.NewsParameter newsParameter)
{
return this.newsDao.GetNewsListByParameter(newsParameter);
}
public IList<Domain.News> GetNewsListByAnyWhere(string strQueryString)
{
Parameter.NewsParameter newsParameter = new Parameter.NewsParameter();
newsParameter.UnlimitedQuery = strQueryString;
return this.newsDao.GetNewsListByAnyWhere(newsParameter);
}
public int UpdateNews(Domain.News news)
{
return this.newsDao.UpdateNews(news);
}
public int UpdateNewsByParameter(Parameter.NewsParameter newsParameter)
{
return this.newsDao.UpdateNewsByParameter(newsParameter);
}
public object DeleteNews(Domain.News news)
{
return this.newsDao.DeleteNews(news);
}
public object DeleteNewsById(float? newsId)
{
return this.newsDao.DeleteNewsById(newsId);
}
public object DeleteNewsByParameter(Parameter.NewsParameter newsParameter)
{
return this.newsDao.DeleteNewsByParameter(newsParameter);
}
public object DeleteNewsByAnyWhere(Parameter.NewsParameter newsParameter)
{
return this.newsDao.DeleteNewsByAnyWhere(newsParameter);
}
}
自定义的BaseSqlMapDao类,数据库操作类,类似于OracleHelper后SqlHelper。
using System; using System.Collections.Generic; using IBatisNet.DataMapper; using IBatisNet.DataAccess; using IBatisNet.DataAccess.DaoSessionHandlers; using IBatisNet.Common.Exceptions; namespace ESPS.IbatisNet { /// <summary> /// 基于IBatisNet的数据访问基类 /// </summary> public class BaseSqlMapDao : IBatisNet.DataAccess.Interfaces.IDao { protected const int PAGE_SIZE = 4; ///<summary> /// Looks up the parent DaoManager, gets the local transaction /// (which should be a SqlMapDaoTransaction) and returns the /// SqlMap associated with this DAO. ///</summary> ///<returns>The SqlMap instance for this DAO.</returns> protected IBatisNet.DataMapper.SqlMapper GetLocalSqlMap() { IBatisNet.DataAccess.DaoManager daoManager = (DaoManager)DaoManager.GetInstance(this);//this 向上转型为IDao类型的实例,通过GetInstance返回一个IDaoManager的实例,再向下转型为DaoManager的一个实例 IBatisNet.DataAccess.DaoSessionHandlers.SqlMapDaoSession sqlMapDaoSession = (SqlMapDaoSession)daoManager.LocalDaoSession; return (IBatisNet.DataMapper.SqlMapper)sqlMapDaoSession.SqlMap; //向下转型,强制执行 } ///<summary> ///Simple convenience method to wrap the SqlMap method of the same name. ///Warps the exception with a IBatisNetException to isolate the SqlMap framework. /// </summary> /// <typeparam name="T">实体类型</typeparam> /// <param name="statementName">操作名称,对应xml中的Statement的id</param> /// <param name="parameterObject">参数</param> /// <returns></returns> protected IList<T> ExecuteQueryForList<T>(string statementName, object parameterObject) { SqlMapper sqlMap = GetLocalSqlMap(); try { return sqlMap.QueryForList<T>(statementName, parameterObject); } catch (Exception e) { throw new IBatisNetException("Error executing query '" + statementName + "'for list. Cause: " + e.Message, e); } } /// <summary> /// Simple convenience method to wrap the SqlMap method of the same name. /// Wraps the exception with a IBatisNetException to isolate the SqlMap framework. /// 得到指定数量的记录数 /// </summary> /// <param name="statementName"></param> /// <param name="parameterObject">参数</param> /// <param name="skipResults">跳过的记录数</param> /// <param name="maxResults">最大返回的记录数</param> /// <returns></returns> protected IList<T> ExecuteQueryForList<T>(string statementName, object parameterObject, int skipResults, int maxResults) { SqlMapper sqlMap = GetLocalSqlMap(); try { return sqlMap.QueryForList<T>(statementName, parameterObject, skipResults, maxResults); } catch (Exception e) { throw new IBatisNetException("Error executing query '" + statementName + "' for list. Cause: " + e.Message, e); } } /// <summary> /// Simple convenience method to wrap the SqlMap method of the same name. /// Wraps the exception with a IBatisNetException to isolate the SqlMap framework. /// 查询得到对象的一个实例 /// </summary> /// <typeparam name="T">对象type</typeparam> /// <param name="statementName">操作名</param> /// <param name="parameterObject">参数</param> /// <returns></returns> protected T ExecuteQueryForObject<T>(string statementName, object parameterObject) { SqlMapper sqlMap = GetLocalSqlMap(); try { return sqlMap.QueryForObject<T>(statementName, parameterObject); } catch (Exception e) { throw new IBatisNetException("Error executing query '" + statementName + "' for object. Cause: " + e.Message, e); } } /// <summary> /// /// </summary> /// <param name="statementName"></param> /// <param name="parameterObject"></param> /// <param name="keyProperty"></param> /// <param name="valueProperty"></param> /// <returns></returns> protected object ExecuteQueryForMap(string statementName, string parameterObject, string keyProperty, string valueProperty) { SqlMapper sqlMap = GetLocalSqlMap(); try { return sqlMap.QueryForMap(statementName, parameterObject, keyProperty, valueProperty); } catch (Exception e) { throw new IBatisNetException("Error executing query '" + statementName + "' for object. Cause: " + e.Message, e); } } protected object ExecuteQueryForMap(string statementName, object parameterObject, string keyProperty) { SqlMapper sqlMap = GetLocalSqlMap(); try { return sqlMap.QueryForMap(statementName, parameterObject, keyProperty); } catch (Exception e) { throw new IBatisNetException("Error executing query '" + statementName + "' for object. Cause: " + e.Message, e); } } /// <summary> /// Simple convenience method to wrap the SqlMap method of the same name. /// Wraps the exception with a IBatisNetException to isolate the SqlMap framework. /// 执行修改 /// </summary> /// <param name="statementName">操作名</param> /// <param name="parameterObject">参数</param> /// <returns></returns> protected int ExecuteUpdate(string statementName, object parameterObject) { SqlMapper sqlMap = GetLocalSqlMap(); try { return sqlMap.Update(statementName, parameterObject); } catch (Exception e) { throw new IBatisNetException("Error executing query'" + statementName + "' for update. Cause: " + e.Message, e); } } /// <summary> /// Simple convenience method to wrap the SqlMap method of the same name. /// Wraps the exception with a IBatisNetException to isolate the SqlMap framework. /// 执行插入 /// </summary> /// <param name="statementName">操作名</param> /// <param name="parameterObject">参数</param> /// <returns></returns> protected object ExecuteInsert(string statementName, object parameterObject) { SqlMapper sqlMap = GetLocalSqlMap(); try { return sqlMap.Insert(statementName, parameterObject); } catch (Exception e) { throw new IBatisNetException("Error executing query '" + statementName + "' for insert. Cause: " + e.Message, e); } } /// <summary> /// 执行删除 /// </summary> /// <param name="statementName">操作名</param> /// <param name="parameterObject">参数</param> /// <returns>返回影响行数</returns> protected object ExecuteDelete(string statementName, Object parameterObject) { SqlMapper sqlMap = GetLocalSqlMap(); try { return sqlMap.Delete(statementName, parameterObject); } catch (Exception e) { throw new IBatisNetException("Error executing query '" + statementName + "' for delete. Cause: " + e.Message, e); } } } }