Castle扩展Ibatis.Net
使用Castle扩展Ibatis.Net,面向接口编程-更优雅的代码
使用Ibatis.Net做项目半年了,甚是喜欢,感觉确实是个简单、轻巧的O/R Mapping框架,特别是将Sql配置在Xml文件中,相当于直接将Dao层抽离了出来。
本文假定读者对Ibatis.Net有一定的了解。
最近试用了一下Ibatis.Net的亲兄弟--Java的Mybatis,一对比发现:
执行一个查询,Ibatis.Net是这么写的:IList<UserEntity> list = SqlMapper.QueryForList<UserEntity>(prefix+ ".GetByFilter", parameters);
而Java的Mybatis是这么写的:List<UserEntity> list = dao.GetByFilter(parameters);
发现了没,后者的显然更优雅。
Mybatis之所以能这么调用,是因为Mybatis提供了一种面向接口编程的方法,只要写好接口,接口的方法名与map文件中sql片段的id相同,我们就能够直接通过接口调用。
我想了又想...C#也能够实现这样优雅的调用啊,可是为啥Ibatis.Net不提供呢,想到这,我开始明白Ibatis.Net是后妈生的。。。
说到这,进入主题吧,既然Ibatis.Net先天不够强大,那我们后天弥补吧,这里主要使用Castle这个组件来动态实现接口。
接下来我们做个Demo
1.搭建Ibatis.Net环境,这里就不说啦(最新版Ibatis.Net下载地址:http://download.csdn.net/detail/tzjzcy/7829759 )
2.引用Castle.Core.dll,这个dll实际上最新版的Ibatis.Net本身就有用到
3.创建一个测试表,录入数据,本文以mysql为例,代码如下:
1 CREATE TABLE `user`(
2 `Userid` INT NOT NULL AUTO_INCREMENT,
3 `Username` VARCHAR(100),
4 `Age` INT,
5 `City` VARCHAR(100), PRIMARY KEY (`Userid`)
6 );
7
8 INSERT INTO `testex`.`user` (`Username`, `Age`, `City`) VALUES ('羊望', '26', '厦门');
9 INSERT INTO `testex`.`user` (`Userid`, `Username`, `Age`, `City`) VALUES ('2', '测试', '18', '福州');
4.编写对应实体类
1 public class UserEntity
2 {
3 public int? Userid { get; set; }
4
5 public string Username { get; set; }
6
7 public int? Age { get; set; }
8
9 public string City { get; set; }
10 }
5.写个简单的map文件:UserMap.xml
1 <?xml version="1.0" encoding="utf-8" ?>
2 <!--这里的namespace必须对应Dao接口的完整类名-->
3 <sqlMap namespace="IbatisExTest.Daos.IUserDao"
4 xmlns="http://ibatis.apache.org/mapping"
5 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
6 <alias>
7 <typeAlias alias="UserEntity" type="IbatisExTest.Entities.UserEntity,IbatisExTest" />
8 </alias>
9
10 <statements>
11
12 <select id="GetByUserid" parameterClass="String" resultClass="UserEntity">
13 SELECT *
14 FROM user
15 <dynamic prepend="WHERE">
16 Userid =#value#
17 </dynamic>
18 </select>
19
20 <select id="GetByFilter" parameterClass="Hashtable" resultClass="UserEntity">
21 SELECT *
22 From user
23 <dynamic prepend="WHERE">
24 <isNotEmpty prepend="AND" property="Userid">
25 Userid =#Userid#
26 </isNotEmpty>
27 <isNotEmpty prepend="AND" property="Username">
28 Username =#Username#
29 </isNotEmpty>
30 </dynamic>
31 </select>
32
33 <insert id="InsertUser" parameterClass="UserEntity">
34 INSERT INTO user
35 ( Username
36 , Age
37 , City)
38 VALUES (
39 #Username#
40 , #Age#
41 , #City#);
42 </insert>
43
44 </statements>
45 </sqlMap>
6.写一个接口,接口的全名(命名空间+接口名)必须与map文件的namespace相同,接口的方法与map文件中的sql片段id对应
1 public interface IUserDao
2 {
3
4 UserEntity GetByUserid(string userid);
5
6 IList<UserEntity> GetByFilter(Hashtable ht);
7
8 object InsertUser(UserEntity user);
9 }
7.写一个BaseDao,作为动态创建的Dao实现类的基类,定义一个属性,传入SqlMapper用
public class BaseDao
{
public BaseDao(ISqlMapper sqlMapper)
{
this.SqlMapper = sqlMapper;
}
public ISqlMapper SqlMapper { get; private set; }
}
8.重点来了,编写Dao实现类的具体方法实现,通过Castle组件实现的,作用是:在调用接口的方法时,执行map中对应的sql片段
1 /// <summary>
2 /// Dao接口的方法实现
3 /// </summary>
4 public class DaoInterceptor : IInterceptor
5 {
6 public void Intercept(IInvocation invocation)
7 {
8 BaseDao baseDao = (BaseDao)invocation.Proxy;
9 //从基类BaseDao获取sqlMapper实例
10 ISqlMapper sqlMapper = baseDao.SqlMapper;
11 MethodInfo method = invocation.Method;
12 if (method.DeclaringType == null) return;
13 //获取接口的全名,即map文件的Namespace
14 string mapNamespace = method.DeclaringType.FullName;
15 //得到要执行的sql的完整id
16 string statementId = mapNamespace + "." + method.Name;
17 IMappedStatement ms = sqlMapper.GetMappedStatement(statementId);
18 if (ms is SelectMappedStatement)
19 {
20 ProcessSelectStatement(invocation, sqlMapper, statementId);
21 }
22 else if (ms is InsertMappedStatement)
23 {
24 ProcessInsertStatement(invocation, sqlMapper, statementId);
25 }
26 else if (ms is UpdateMappedStatement)
27 {
28 ProcessUpdateStatement(invocation, sqlMapper, statementId);
29 }
30 else if (ms is DeleteMappedStatement)
31 {
32 ProcessDeleteStatement(invocation, sqlMapper, statementId);
33 }
34 }
35
36 private static void ProcessSelectStatement(IInvocation invocation, ISqlMapper sqlMapper, string statementId)
37 {
38 MethodInfo method = invocation.Method;
39 if (method.ReturnType.IsGenericType) //判断方法的返回值,如果是泛型,表示返回值是泛型集合
40 {
41 //通过反射调用sqlMapper.QueryForList方法
42 Type t = typeof(List<>).MakeGenericType(method.ReturnType.GetGenericArguments());
43 var list = Activator.CreateInstance(t);
44 MethodInfo miQueryForList = typeof(ISqlMapper).GetMethod("QueryForList",
45 new Type[] { typeof(string), typeof(object), typeof(List<>) });
46 miQueryForList.Invoke(sqlMapper, new object[] { statementId, invocation.Arguments[0], list });
47 invocation.ReturnValue = list;
48 }
49 else //返回单个对象,或int等基本类型
50 {
51 //直接调用sqlMapper.QueryForObject方法
52 invocation.ReturnValue = sqlMapper.QueryForObject(statementId, invocation.Arguments[0]);
53 }
54 }
55
56 private static void ProcessInsertStatement(IInvocation invocation, ISqlMapper sqlMapper, string statementId)
57 {
58 invocation.ReturnValue = sqlMapper.Insert(statementId, invocation.Arguments[0]);
59 }
60
61 private static void ProcessUpdateStatement(IInvocation invocation, ISqlMapper sqlMapper, string statementId)
62 {
63 invocation.ReturnValue = sqlMapper.Update(statementId, invocation.Arguments[0]);
64 }
65
66 private static void ProcessDeleteStatement(IInvocation invocation, ISqlMapper sqlMapper, string statementId)
67 {
68 invocation.ReturnValue = sqlMapper.Delete(statementId, invocation.Arguments[0]);
69 }
9.编写SqlMapper的扩展方法,动态实现Dao接口
1 public static class SqlMapExtensionMethods
2 {
3 /// <summary>
4 /// 获取Dao的实现
5 /// </summary>
6 /// <typeparam name="T">Dao接口</typeparam>
7 /// <param name="sqlMapper">sqlMapper</param>
8 /// <returns>返回Dao的实现</returns>
9 public static T GetDao<T>(this ISqlMapper sqlMapper)
10 {
11 ProxyGenerator generator = new ProxyGenerator();
12 DaoInterceptor daoInterceptor = new DaoInterceptor();
13 //创建一个BaseDao的代理类,并实现指定Dao接口
14 object proxy = generator.CreateClassProxy(typeof(BaseDao), new Type[] { typeof(T) }, ProxyGenerationOptions.Default, new object[] { sqlMapper }, daoInterceptor);
15 return (T)proxy;
16 }
17 }
10.这样就完成了扩展,让我们看看调用实例吧
class Program
{
private const string mapperNamespace = "IbatisExTest.Daos.IUserDao"; private static ISqlMapper SqlMapper
{
get { return Mapper.Get(); }
} private static IUserDao UserDao
{
get { return SqlMapper.GetDao<IUserDao>(); }
} static void Main()
{
Hashtable ht = new Hashtable();
ht["Username"] = "羊望";
//传统用法
IList<UserEntity> list1 = SqlMapper.QueryForList<UserEntity>(mapperNamespace + ".GetByFilter", ht); //新用法(代码更优雅了吧)
IList<UserEntity> list2 = UserDao.GetByFilter(ht); //测试新增
//UserEntity user = new UserEntity { Username = "新用户", Age = 11, City = "新城市" };
//UserDao.InsertUser(user);
}
}
最后,我们看到,扩展后我们只需要调用Dao接口的方法,代码更简洁了。
至于要比传统用法多写个Dao接口,这个工作或许我们可以通过代码生成工具来做吧。
源代码下载:http://files.cnblogs.com/lookup/Castle%E6%89%A9%E5%B1%95IbatisNet%E4%BE%8B%E5%AD%90.zip
欢迎拍砖:)
Castle扩展Ibatis.Net的更多相关文章
- 使用Castle扩展Ibatis.Net,面向接口编程-更优雅的代码
使用Ibatis.Net做项目半年了,甚是喜欢,感觉确实是个简单.轻巧的O/R Mapping框架,特别是将Sql配置在Xml文件中,相当于直接将Dao层抽离了出来. 本文假定读者对Ibatis.Ne ...
- 关于ibatis进行物理游标分页
http://www.iteye.com/topic/136712 详细demo:参照http://www.kusoft.net 我的数据库是采用mssql2000 采用分页必定数据量比较大: 按照i ...
- 读书笔记--iBATIS in Action 目录
1.iBATIS的理念 2.iBATIS是什么 3.安装和配置iBATIS 4.使用以映射语句 5.执行非查询语句 6.使用高级查询技术 7.事务 8.使用动态SQL 9.使用高速缓存提高性能 10. ...
- ORM 框架简介
对象-关系映射(Object/Relation Mapping,简称ORM),是随着面向对象的软件开发方法发展而产生的.面向对象的开发方法是当今企业级应用开发环境中的主流开发方法,关系数据库是企业级应 ...
- ORM(Object/Relation Mapping)框架简介
ORM 框架简介 对象-关系映射(Object/Relation Mapping,简称ORM),是随着面向对象的软件开发方法发展而产生的.面向对象的开发方法是当今企业级应用开发环境中的主流开发方法,关 ...
- Ibatis学习总结4--SQL Map XML 映射文件扩展
SQL Map XML 映射文件除了上文提到的属性还有一些其他重要的属性,下文将详细介绍这些属性. 缓存 Mapped Statement 结果集 通过在查询 statement 中指定 cacheM ...
- Castle Core 4.0.0 alpha001发布
时隔一年多以后Castle 项目又开始活跃,最近刚发布了Castle Core 4.0.0 的alpha版本, https://github.com/castleproject/Core/releas ...
- ASP.NET Core 整合Autofac和Castle实现自动AOP拦截
前言: 除了ASP.NETCore自带的IOC容器外,我们还可以使用其他成熟的DI框架,如Autofac,StructureMap等(笔者只用过Unity,Ninject和Castle). 1.ASP ...
- Hibernate和IBatis对比
[转自]http://blog.csdn.net/ya2dan/article/details/7396598 项目也做过几个, 使用IBatis就做一个项目, 基本上都是使用Hibernate, 也 ...
随机推荐
- 《神秘的程序员们》漫画26~28:《万年坑系列》 I、II、III(转)
26 <万年坑系列> I:那些令你憎恶的系统从何而来? 世界上总有一些令人憎恶的系统,而你却天天非用不可.这些系统的提供方们既不缺钱也不缺人,有的还很热衷于改版升级. 但为何升级完后,它们 ...
- 安装Docker
安装Docker 1. 增加Repository配置文件 cat >/etc/yum.repos.d/docker.repo <<-EOF [dockerrepo]name=Dock ...
- Git是个好工具(转)
Git是分布式版本控制系统,我们常用的版本控制工具还有SVN.这里就得区分下什么是分布式版本控制系统,什么是集中化的版本控制系统. 集中化的版本控制系统 集中化的版本控制系统( Centralized ...
- JS匿名函数&闭包
<html> <head> <title> test </title> </head> <body> <script ty ...
- thinkphp学习笔记9—自动加载
原文:thinkphp学习笔记9-自动加载 1.命名空间自动加载 在3.2版本中不需要手动加载类库文件,可以很方便的完成自动加载. 系统可以根据类的命名空间自动定位到类库文件,例如定义了一个类Org\ ...
- 十天学Linux内核之第三天---内存管理方式
原文:十天学Linux内核之第三天---内存管理方式 昨天分析的进程的代码让自己还在头昏目眩,脑子中这几天都是关于Linux内核的,对于自己出现的一些问题我会继续改正,希望和大家好好分享,共同进步.今 ...
- 关于通过id查询记录的一些总结
最近在做一个oa系统,简化了账号的设置,列名均为id,类型均为varchar:有的表将id设置成了主键,有的表没有设置成主键. 通过举例说明通过id查询的一些问题. 之前登陆的时候,账号001-007 ...
- SlidingMenu 左侧滑动菜单
1.MainActivity package loveworld.slidingmenu; import java.util.ArrayList; import android.app.Activit ...
- IE打印的参数配置说明
IE自动给我们在页眉和页脚处加上了这些不必要的打印信息.如果我们不想要任何页眉和页脚的话,直接删除它们就行了.:-)大部分时候我都是这样做的.但如果你想自定义页眉和页脚的时候,该怎么做呢?上面的那些“ ...
- 建立Cent OS7server有些问题需要注意
1.与网络有关的问题 (1)网络配置问题 CentOS 7 与曾经的版本号项目在网络配置等许多的地方都做了比較大的改动,比如你会发现ifconfig命令没有了... 这是由于,centos7以下使用: ...