• 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

    欢迎拍砖:)

     

     

     

  • 相关阅读:
    【javaFX学习】(二) 面板手册
    Android开发从系统图库中选择一张图片的方法
    用Android studio进行 OpenCV 开发的第一个项目
    【Android学习入门】Android中activity的启动模式
    Android Studio快捷键【Android学习入门】
    【Android学习入门】Android studio基本设置
    安装Android模拟器Genymotion【Android学习入门】
    Android studio 安装与配置【Android学习入门】
    【知乎】日常生活中有哪些十分钟就能学会并可以终生受用的技能?(一)
    C++学生信息处理
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3941020.html
Copyright © 2020-2023  润新知