之前做了一个项目,项目中使用了IBatis for .net框架,感觉IBatis使用起来还是比较方便,且较之NHibernate配置比较简单,属于轻量级ORM框架。最近又有一个项目需要开动,我琢磨着还是使用IBatis吧,毕竟前面项目使用过,大家都比较熟悉,而且构建起来也比较方便。但是在构建的时候,感觉在做重复劳动,每个实体都有增删改查几个共用的操作,写完这些也需要不少时间,而且无意义,最多只能增加对IBatis使用熟练度,相对于花下去的成本还是不值得的。百度、google想找一些封装好的通用类,类似于NHibernate的IBaseDao,然而并没有找到合适的。那只能自己动手写了,参考了NHibernate的IBaseDao封装,主要封装了以下几个方法(添加一个实体,添加多个实体,删除一个实体,删除多个实体,修改一个实体,获取一个实体,获取所有实体,分页,分页排序查询)。
IBaseDao接口
/// <summary>
/// 基础操作
/// </summary>
/// <typeparam name="TEntity">实体对象</typeparam>
/// <typeparam name="TPk">主键类型</typeparam>
public interface IBaseDao<TEntity, TPk>
{
/// <summary>
/// 添加一个实体,返回主键
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
TPk Add(TEntity entity);
/// <summary>
/// 添加多个实体,返回主键列表
/// </summary>
/// <param name="entities"></param>
/// <returns></returns>
IList<TPk> Add(IEnumerable<TEntity> entities);
/// <summary>
/// 通过主键删除实体
/// </summary>
/// <param name="id"></param>
void Delete(TPk id);
/// <summary>
/// 通过id集合删除实体
/// </summary>
/// <param name="ids"></param>
void Delete(IEnumerable<TPk> ids);
/// <summary>
/// 更新实体,为null则不操作
/// </summary>
/// <param name="entity"></param>
void Update(TEntity entity);
/// <summary>
/// 通过主键获取实体
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
TEntity Get(TPk id);
/// <summary>
/// 通过主键获取实体
/// </summary>
/// <param name="ids"></param>
/// <returns></returns>
IList<TEntity> Get(IEnumerable<TPk> ids);
/// <summary>
/// 获取所有实体
/// </summary>
/// <returns></returns>
IList<TEntity> GetAll();
/// <summary>
/// 分页
/// </summary>
/// <typeparam name="TSearchSortCriteria"></typeparam>
/// <param name="currentPage"></param>
/// <param name="pageSize"></param>
/// <returns></returns>
PageData<TEntity> GetPageDate<TSearchSortCriteria>(int currentPage, int pageSize) where TSearchSortCriteria : SearchSortPageEntity, new();
/// <summary>
/// 分页查询排序
/// </summary>
/// <typeparam name="TSearchSortCriteria"></typeparam>
/// <param name="currentPage"></param>
/// <param name="pageSize"></param>
/// <param name="searchSortCriteria"></param>
/// <returns></returns>
PageData<TEntity> GetPageDate<TSearchSortCriteria>(int currentPage, int pageSize, TSearchSortCriteria searchSortCriteria) where TSearchSortCriteria : SearchSortPageEntity, new();
}
IBaseDao的实现
public class BaseDao<TEntity, TPk> : IBaseDao<TEntity, TPk>
{
public ISqlMapper RealMapper = Mapper.GetMaper;
private readonly string _className = typeof(TEntity).Name;
public TPk Add(TEntity entity)
{
string command = "Add" + _className;
var obj = RealMapper.Insert(command, entity);
return (TPk)obj;
}
public IList<TPk> Add(IEnumerable<TEntity> entities)
{
var tPks = new List<TPk>();
using (var tran = RealMapper.BeginTransaction())
{
tPks.AddRange(from entity in entities let command = "Add" + _className select RealMapper.Insert(command, entity) into obj select (TPk)obj);
tran.CommitTransaction();
}
return tPks;
}
public void Delete(TPk id)
{
string command = "Delete" + _className;
RealMapper.Delete(command, id);
}
public void Delete(IEnumerable<TPk> ids)
{
string command = "Delete" + _className + "s";
RealMapper.Delete(command, ids);
}
public void Update(TEntity entity)
{
string command = "Update" + _className;
RealMapper.Update(command, entity);
}
public TEntity Get(TPk id)
{
string command = "Get" + _className;
return RealMapper.QueryForObject<TEntity>(command, id);
}
public IList<TEntity> Get(IEnumerable<TPk> ids)
{
string command = "Get" + _className + "s";
return RealMapper.QueryForList<TEntity>(command, ids);
}
public IList<TEntity> GetAll()
{
string command = "GetAll" + _className + "s";
return RealMapper.QueryForList<TEntity>(command, null);
}
public PageData<TEntity> GetPageDate<TSearchSortCriteria>(int currentPage, int pageSize) where TSearchSortCriteria : SearchSortPageEntity, new()
{
return GetPageDate(currentPage, pageSize, new TSearchSortCriteria());
}
public PageData<TEntity> GetPageDate<TSearchSortCriteria>(int currentPage, int pageSize, TSearchSortCriteria searchSortCriteria) where TSearchSortCriteria : SearchSortPageEntity, new()
{
int startRowNumber = (currentPage - 1) * pageSize + 1;
int endRowNumber = currentPage * pageSize;
searchSortCriteria.StartRowNumber = startRowNumber;
searchSortCriteria.EndRowNumber = endRowNumber;
string command = "Get" + _className + "sWithPageAndSearch";
IList<TEntity> entities = RealMapper.QueryForList<TEntity>(command, searchSortCriteria);
command = "Get" + _className + "sWithPageAndSearchCount";
var totalCount = (int)RealMapper.QueryForObject(command, searchSortCriteria);
var pageDate = new PageData<TEntity>
{
Data = entities,
Pager = new Pager(totalCount, currentPage, pageSize)
};
return pageDate;
}
}
其中需要解释的操作是“分页查询排序”,TSearchSortCriteria是查询排序条件,每个实体的查询条件都不一样,且继承SearchSortCriteria类(包含公共条件)
SearchSortCriteria代码:
public abstract class SearchSortPageEntity
{
/// <summary>
/// StartRowNumber
/// </summary>
public int? StartRowNumber { get; set; }
/// <summary>
/// EndRowNumber
/// </summary>
public int? EndRowNumber { get; set; }
/// <summary>
/// SortField
/// </summary>
public string SortField { get; set; }
/// <summary>
/// Sort
/// </summary>
public string Sort { get; set; }
}
注意点:
1、这里我会为每个表字段都创建为一个查询条件(除了主键)
2、针对值类型的字段,如int,decimal使用 “=”条件
3、针对引用类型的字段,如nvarchar,varchar使用 like ‘%%’
4、针对时间类型,为其创建2个字段 BeginDatetime,EndDatetime,创建条件 >= ‘BeginDatetime’,<= 'EndDatetime'
示例代码如下:(AccountSearchCriteria.cs)
public class AccountSearchCriteria : SearchSortPageEntity
{
/// <summary>
/// UserName
/// </summary>
public string SearchUserName { get; set; }
/// <summary>
/// Password
/// </summary>
public string SearchPassword { get; set; }
/// <summary>
/// BeginRegisterDate
/// </summary>
public DateTime? BeginRegisterDate { get; set; }
/// <summary>
/// EndRegisterDate
/// </summary>
public DateTime? EndRegisterDate { get; set; }
}
Account的表结构(接下来代码全部以Account为例):
|
<?xml version="1.0" encoding="utf-8" ?>
<sqlMap namespace="Account" xmlns="http://ibatis.apache.org/mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<alias>
<!--alias:取别名
assembly:表示类所在的文件
type:表示该类的完整的名称
-->
<typeAlias alias="Account" assembly="ULH.Entity.dll" type="ULH.Entity.User.AccountPo" />
<typeAlias alias="AccountSearchCriteria" assembly="ULH.Service.dll" type="ULH.Service.User.SearchCriteria.AccountSearchCriteria" />
</alias>
<resultMaps>
<resultMap id="AccountResult" class="Account">
<result property="Id" column="Id"/>
<result property="UserName" column="UserName"/>
<result property="Password" column="Password"/>
<result property="RegisterDate" column="RegisterDate"/>
</resultMap>
<resultMap id="AccountSearchCriteriaResult" class="AccountSearchCriteria">
<result property="StartRowNumber" column="StartRowNumber"/>
<result property="EndRowNumber" column="EndRowNumber"/>
<result property="SortField" column="SortField"/>
<result property="Sort" column="Sort"/>
<result property="SearchUserName" column="SearchUserName"/>
<result property="SearchPassword" column="SearchPassword"/>
<result property="BeginRegisterDate" column="BeginRegisterDate"/>
<result property="EndRegisterDate" column="EndRegisterDate"/>
</resultMap>
</resultMaps>
<statements>
<insert id="AddAccountPo" parameterClass="Account">
INSERT INTO Account VALUES(#UserName# ,#Password# ,#RegisterDate#)
<selectKey resultClass="int" type="post" property="Id">
SELECT @@IDENTITY
</selectKey>
</insert>
<delete id="DeleteAccountPo" parameterClass="int" restultClass="int">
DELETE FROM Account WHERE Id = #Id#
</delete>
<delete id="DeleteAccountPos" parameterClass="List">
DELETE FROM Account
<iterate prepend="WHERE" open="(" close=")" conjunction="OR">
Id = #[]#
</iterate>
</delete>
<update id="UpdateAccountPo" parameterClass="Account" restltClass="int">
UPDATE Account
<dynamic prepend="SET">
<isNotEmpty prepend="," property="UserName">
UserName = #UserName#
</isNotEmpty>
<isNotEmpty prepend="," property="Password">
Password = #Password#
</isNotEmpty>
<isNotEmpty prepend="," property="RegisterDate">
RegisterDate = #RegisterDate#
</isNotEmpty>
</dynamic> WHERE Id = #Id#
</update>
<select id="GetAccountPo" parameterClass="int" resultClass="Account">
SELECT Id ,UserName ,Password ,RegisterDate FROM Account WHERE Id = #Id#
</select>
<select id="GetAccountPos" parameterClass="List" resultClass="Account">
SELECT Id ,UserName ,Password ,RegisterDate FROM Account
<iterate prepend="WHERE" open="(" close=")" conjunction="OR">
Id = #[]#
</iterate>
</select>
<select id="GetAllAccountPos" resultClass="Account">
SELECT Id ,UserName ,Password ,RegisterDate FROM Account
</select>
<select id="GetAccountPosWithPageAndSearch" parameterClass="AccountSearchCriteria" resultClass="Account">
WITH TempAccount
AS ( SELECT Id ,UserName ,Password ,RegisterDate ,
ROW_NUMBER() OVER ( ORDER BY <isEmpty property="SortField">Id</isEmpty>
<isNotEmpty property="SortField">#SortField#</isNotEmpty>
<isEmpty property="Sort">Asc</isEmpty>
<isNotEmpty property="Sort">#Sort#</isNotEmpty>) AS RowNumber
FROM Account
<dynamic prepend="WHERE">
<isNotEmpty prepend="AND" property="SearchUserName">
UserName LIKE '%$SearchUserName$%'
</isNotEmpty>
<isNotEmpty prepend="AND" property="SearchPassword">
Password LIKE '%$SearchPassword$%'
</isNotEmpty>
<isNotEmpty prepend="AND" property="BeginRegisterDate">
<![CDATA[RegisterDate >= #BeginRegisterDate#]]>
</isNotEmpty>
<isNotEmpty prepend="AND" property="EndRegisterDate">
<![CDATA[RegisterDate <= #EndRegisterDate#]]>
</isNotEmpty>
</dynamic>
)
<![CDATA[SELECT Id ,UserName ,Password ,RegisterDate
FROM TempAccount
WHERE RowNumber >= #StartRowNumber#
AND RowNumber <= #EndRowNumber#]]>
</select>
<select id="GetAccountPosWithPageAndSearchCount" parameterClass="AccountSearchCriteria" resultClass="int">
SELECT COUNT(1)
FROM Account
<dynamic prepend="WHERE">
<isNotEmpty prepend="AND" property="SearchUserName">
UserName LIKE '%$SearchUserName$%'
</isNotEmpty>
<isNotEmpty prepend="AND" property="SearchPassword">
Password LIKE '%$SearchPassword$%'
</isNotEmpty>
<isNotEmpty prepend="AND" property="BeginRegisterDate">
<![CDATA[RegisterDate >= #BeginRegisterDate#]]>
</isNotEmpty>
<isNotEmpty prepend="AND" property="EndRegisterDate">
<![CDATA[RegisterDate <= #EndRegisterDate#]]>
</isNotEmpty>
</dynamic>
</select>
</statements>
</sqlMap>
public interface IAccountDao : IBaseDao<AccountPo, int>
{
}
public class AccountDao : BaseDao<AccountPo, int>, IAccountDao
{
}
现在就可以直接使用基础操作了
public class UserService : IUserService
{
private readonly IAccountDao _accountDao = Container.CommonContainer.Resolve<IAccountDao>();
public int CreateAccount(Account account)
{
return _accountDao.Add(GetAccountPoFromVo(account));
}
public void DeleteAccount(int id)
{
_accountDao.Delete(id);
}
public void DeleteAccounts(List<int> ids)
{
_accountDao.Delete(ids);
}
public void UpdateAccount(Account account)
{
_accountDao.Update(GetAccountPoFromVo(account));
}
public void UpdateAccounts(List<Account> accounts)
{
foreach (var account in accounts)
{
UpdateAccount(account);
}
}
public Account GetAccount(int id)
{
return GetAccountVoFromPo(_accountDao.Get(id));
}
public List<Account> GetAllAccount()
{
return _accountDao.GetAll().Select(GetAccountVoFromPo).ToList();
}
public PageData<Account> GetAccountPageData(int currentPage, int pageSize)
{
return GetAccountPageData(currentPage, pageSize, new AccountSearchCriteria());
}
public PageData<Account> GetAccountPageData(int currentPage, int pageSize, AccountSearchCriteria accountSehSotPage)
{
var pageDataVo = new PageData<Account>();
var pageDataPo = _accountDao.GetPageDate(currentPage, pageSize, accountSehSotPage);
pageDataVo.Data = pageDataPo.Data.Select(GetAccountVoFromPo).ToList();
pageDataVo.Pager = pageDataPo.Pager;
return pageDataVo;
}
#region 辅助方法
internal static Account GetAccountVoFromPo(AccountPo accountPo)
{
Account account = null;
if (accountPo != null)
{
account = new Account
{
Id = accountPo.Id,
UserName = accountPo.UserName,
RegisterDate = accountPo.RegisterDate
};
}
return account;
}
internal static AccountPo GetAccountPoFromVo(Account account)
{
AccountPo accountPo = null;
if (account != null)
{
accountPo = new AccountPo
{
Id = account.Id,
Password = account.Password,
UserName = account.UserName,
RegisterDate = account.RegisterDate
};
}
return accountPo;
}
#endregion
}
最后共享一下代码模板,使用动软代码生成器生成