mapper.xml是怎样实现Dao层接口
上午写了一个简单的 从xml读取信息实例化一个Bean对象。下午就开始想mybatis是怎么通过xml文件来实现dao层接口的,一开始想直接用Class.forName(String name)然后调用getMethods的办法来得到Dao接口所有的方法Method,结果证明这对接口实没有用的,会报一个错误。于是想百度,但是百度的结果全是怎么配置mapper的。。然后我又四处翻资料,终于找到一些办法。最后我还是用到了我自己封装的DButil 和 DataUtil两个类。
反正我是这么实现的,至于Mybatis是怎么实现的,我还没看源码。
有不了解的可以点下面链接
下面是我的目录结构

因为要解析xml文件,我这里引入了一个dom4j的jar包
从根源开始,下面先贴上User类和IUserdao接口
package com.yck.bean;
public class User
{
private Integer id;
private String name;
private Integer age;
//getter和setter方法均省略
}
package com.yck.dao;
import com.yck.bean.User;
public interface IUserDao
{
User selectById(Integer id);
}
然后是我准备好的userdao的xml文件
<?xml version="1.0" encoding="UTF-8"?>
<dao id="com.yck.dao.IUserDao">
<select id="selectById" resultType ="com.yck.bean.User">
select * from t_user where id = ?
</select>
</dao>
为了习惯,我没怎么改动这个配置文件的东西,比较符合mybatis的风格
既然我们的目的是用xml配置文件来实现dao层接口。那我们第一件事就是读取xml文件的信息。
我写了两个类来存取我需要的东西 getter和setter方法我全部省略不贴了,太难看
package com.yck.bean;
import java.util.List;
/**
* 用来存储一个dao接口的信息
* @author Administrator
*
*/
public class MapperBean
{
private String interfaceName; //接口名
private List<Function> list; //接口下所有方法
}
package com.yck.bean;
/*
* 用来存储dao接口一条方法的信息
*/
public class Function
{
private String sqltype; //sql的类型 其实用我封装的DataUtil是只有两种类型的,但是我计划在xml读取仍有四种情况
private String funcName; // 方法名
private String sql; //执行的sql语句
private String resultType; // 返回类型
private String parameterType; //参数类型
}
然后是读取xml文件和通过反射实现dao接口所有函数的代码
public class DaoProxy
{
/**
* 通过读取配置文件实现dao接口
* @param path
* @return
*/
public static Object implDao(String path)
{
MapperBean mapper = readMapper(path);
ClassLoader classLoader = DaoProxy.class.getClassLoader();
Class interfaze = null;
try
{
interfaze = classLoader.loadClass(mapper.getInterfaceName()); //加载一个接口类
} catch (ClassNotFoundException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
/**
* 下面这几句相当重要了,是用xml文件实现dao接口的核心代码,因为数据库相关的大量工作我之前都写过了,所以这个看起来代码量很少
* 我也不太懂下面这个东西,我查API查了相当久,一开始写总是错,因为我理解错了InvocationHandler接口下那个方法的Object数组参数
* 它应该理解为一个可变长数组,而不是必须为数组
*/
Object instanze = Proxy.newProxyInstance(classLoader, new Class[]{interfaze}, new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
List<Function> list = mapper.getList();
Object obj = null;
for(Function f:list)
{
if(f.getFuncName().equals(method.getName()))
{
/**
* 判断是不是select语句,是则调用DateUtil的select方法
* 否则调用update的方法
*/
if(f.getSqltype().equals("select"))
obj = DataUtil.selectForBean(Class.forName(f.getResultType()), f.getSql(), args);
else obj = DataUtil.updata(f.getSql(), args);
}
}
return obj;
}
});
return instanze; //返回这个接口,即mapper.getInterfaceName()这个接口
}
/**
* 读取xml文件的信息
* @param path
* @return
*/
@SuppressWarnings("rawtypes")
private static MapperBean readMapper(String path)
{
File file = new File(path);
SAXReader reader = new SAXReader();
MapperBean mapper = new MapperBean();
try
{
Document doc = reader.read(file);
Element root = doc.getRootElement(); //读取根节点 即dao节点
mapper.setInterfaceName(root.attributeValue("id").trim()); //把dao节点的id值存为接口名
List<Function> list = new ArrayList<Function>(); //用来存储方法的List
for(Iterator rootIter = root.elementIterator();rootIter.hasNext();) //遍历根节点下所有子节点
{
Function fun = new Function(); //用来存储一条方法的信息
Element e = (Element) rootIter.next();
String sqltype = e.getName().trim();
String funcName = e.attributeValue("id").trim();
String sql = e.getText().trim();
String resultType = e.attributeValue("resultType").trim();
fun.setSqltype(sqltype);
fun.setFuncName(funcName);
fun.setResultType(resultType);
fun.setSql(sql);
list.add(fun);
}
mapper.setList(list);
} catch (DocumentException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
return mapper;
}
}
下面写一个Test类测试一下
package com.yck.test;
import com.yck.bean.User;
import com.yck.dao.IUserDao;
import com.yck.yebatis.DaoProxy;
public class Test2
{
public static void main(String[] args)
{
IUserDao userdao = (IUserDao) DaoProxy.implDao("src/userdao.xml");
User user= userdao.selectById(2);
System.out.println(user);
}
}
数据库原来有几条数据是这样的

控制台输出结果

package com.yck.dao;
import java.util.List;
import com.yck.bean.User;
public interface IUserDao
{
User selectById(Integer id);
int updateName(String name,Integer id);
int deleteById(Integer id);
int insert(String name,int age);
List<User> getAll();
}
<?xml version="1.0" encoding="UTF-8"?>
<dao id="com.yck.dao.IUserDao">
<select id="selectById" resultType ="com.yck.bean.User">
select * from t_user where id = ?
</select>
<update id="updateName" resultType = "java.lang.Integer">
update t_user set name=? where id=?
</update>
<delete id="deleteById" resultType = "java.lang.Integer">
delete from t_user where id=?
</delete>
<insert id="insert" resultType = "java.lang.Integer">
insert into t_user values(null,?,?)
</insert>
<select id="getAll" resultType = "java.util.List">
select * from t_user;
</select>
</dao>
import com.yck.bean.User;
import com.yck.dao.IUserDao;
import com.yck.yebatis.DaoProxy;
public class Test2
{
public static void main(String[] args)
{
IUserDao userdao = (IUserDao) DaoProxy.implDao("src/userdao.xml");//通过xml文件生成一个IUserDao实例
User user= userdao.selectById(2);
System.out.println(user);
userdao.insert("李四", 66);
userdao.insert("小明", 66);
userdao.insert("小红", 66);
userdao.insert("小张", 66);
//List<User> list = userdao.getAll();
userdao.updateName("蛋蛋", 1);
userdao.deleteById(3);
}
}

单条的增删改都没问题;问题出在返回没有参数的时候,Object[] 和Object...objects的区别。今天太晚了,先去睡一觉,白天再改善一下吧
<**********************************************************************************************此处是分割线**********************************************************************************************************>
昨晚实在太晚了,为了区别Object[] 和 Object...objects,其实我不明白为什么 Invacation 这个接口里的方法
Object invoke(Object proxy, Method method, Object[] args) throws Throwable 为什么不用可变长数组Object...objects 而且在调用函数的时候它确实有Object...objects的特性
比如我们的dao接口里面有像 selectById(Integer id)这样的 单个参数的方法,也有像updateName(String name,Integer id)这样的双参数方法。但是在调用没有参数的getAll()方法时,它报错了
,报了空指针错误。
这一点我是比较纳闷的,为了解决这个问题我稍微修改了一下用 resultType做判断,如果resultType是java.util.ArrayList类(原谅我实在水平有限,目前只能设计出返回这一种集合。。。),我们再分别判断
它有没有参数,为此我在sql语句的标签上加了一个parameter属性,我一开始意思是默认只能为“yes”和“no”两种属性,但我写到这里的时候觉得特别蠢,所以我刚刚默默地去改成了“true”和“false”;
最后的最后,修改了以上代码的几句就行了
在mapper.xml中增加了一点标签属性
<?xml version="1.0" encoding="UTF-8"?>
<dao id="com.yck.dao.IUserDao">
<select id="selectById" resultType ="com.yck.bean.User" parameter="true">
select * from t_user where id = ?
</select>
<update id="updateName" resultType = "java.lang.Integer" parameter="true">
update t_user set name=? where id=?
</update>
<delete id="deleteById" resultType = "java.lang.Integer" parameter="true">
delete from t_user where id=?
</delete>
<insert id="insert" resultType = "java.lang.Integer" parameter="true">
insert into t_user values(null,?,?)
</insert>
<select id="getAll" resultType = "java.util.ArrayList" resultOf="com.yck.bean.User" parameter="false">
select * from t_user;
</select>
</dao>
在DaoProxy类改了读取mapper.xml的代码,如下
/**
* 读取xml文件的信息
* @param path
* @return
*/
@SuppressWarnings("rawtypes")
private static MapperBean readMapper(String path)
{
File file = new File(path);
SAXReader reader = new SAXReader();
MapperBean mapper = new MapperBean();
try
{
Document doc = reader.read(file);
Element root = doc.getRootElement(); //读取根节点 即dao节点
mapper.setInterfaceName(root.attributeValue("id").trim()); //把dao节点的id值存为接口名
List<Function> list = new ArrayList<Function>(); //用来存储方法的List
for(Iterator rootIter = root.elementIterator();rootIter.hasNext();) //遍历根节点下所有子节点
{
Function fun = new Function(); //用来存储一条方法的信息
Element e = (Element) rootIter.next();
String sqltype = e.getName().trim();
String funcName = e.attributeValue("id").trim();
String sql = e.getText().trim();
String resultType = e.attributeValue("resultType").trim();
String resultOf = "";
if("java.util.ArrayList".equals(resultType))
resultOf = e.attributeValue("resultOf").trim();
String parameter = e.attributeValue("parameter");
fun.setSqltype(sqltype);
fun.setFuncName(funcName);
fun.setResultType(resultType);
fun.setSql(sql);
fun.setResultOf(resultOf);
fun.setParameter("true".equals(parameter));
list.add(fun);
}
mapper.setList(list);
} catch (DocumentException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
return mapper;
}
修改了实现接口的方法如下
/**
* 通过读取配置文件实现dao接口
* @param path
* @return
*/
public static Object implDao(String path)
{
MapperBean mapper = readMapper(path);
ClassLoader classLoader = DaoProxy.class.getClassLoader();
Class interfaze = null;
try
{
interfaze = classLoader.loadClass(mapper.getInterfaceName()); //加载一个接口类
} catch (ClassNotFoundException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
/**
* 下面这几句相当重要了,是用xml文件实现dao接口的核心代码,因为数据库相关的大量工作我之前都写过了,所以这个看起来代码量很少
* 我也不太懂下面这个东西,我查API查了相当久,一开始写总是错,因为我理解错了InvocationHandler接口下那个方法的Object数组参数
* 它应该理解为一个可变长数组,而不是必须为数组
*/
Object instanze = Proxy.newProxyInstance(classLoader, new Class[]{interfaze}, new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
List<Function> list = mapper.getList();
Object obj = null;
for(Function f:list)
{
if(f.getFuncName().equals(method.getName()))
{
/**
* 判断是不是select语句,是则调用DateUtil的select方法
* 否则调用update的方法
*/
if(f.getSqltype().equals("select"))
{
if("java.util.ArrayList".equals(f.getResultType()))
{
if(f.isParameter())
obj = DataUtil.selectForBeanList(Class.forName(f.getResultOf()), f.getSql(), args);
else obj = DataUtil.selectForBeanList(Class.forName(f.getResultOf()), f.getSql());
}
else
{
if(f.isParameter())
obj = DataUtil.selectForBean(Class.forName(f.getResultType()), f.getSql(), args);
else obj = DataUtil.selectForBean(Class.forName(f.getResultType()), f.getSql());
}
}
else
{
if(f.isParameter())
obj = DataUtil.updata(f.getSql(), args);
else obj = DataUtil.updata(f.getSql());
}
}
}
return obj;
}
});
return instanze; //返回这个接口,即mapper.getInterfaceName()这个接口
}
最后我们做一下测试,数据库原有的信息如下

测试方法如下
package com.yck.test;
import java.util.List;
import com.yck.bean.User;
import com.yck.dao.IUserDao;
import com.yck.yebatis.DaoProxy;
public class Test2
{
public static void main(String[] args)
{
IUserDao userdao = (IUserDao) DaoProxy.implDao("src/userdao.xml");//通过xml文件生成一个IUserDao实例
User user= userdao.selectById(2);
System.out.println(user);
userdao.insert("大牛", 88);
userdao.insert("二牛", 77);
userdao.insert("三牛", 66);
userdao.insert("四牛", 55);
List<User> list = userdao.getAll();
System.out.println(list);
userdao.updateName("二蛋", 1);
userdao.deleteById(3);
}
}
控制台输出
由于记录数比较多控制台输出太长了,直接贴结果吧
User [id=, name=王八蛋, age=] [User [id=, name=蛋蛋, age=], User [id=, name=王八蛋, age=], User [id=, name=李四, age=], User [id=, name=小明, age=], User [id=, name=小红, age=], User [id=, name=小张, age=], User [id=, name=李三, age=], User [id=, name=大牛, age=], User [id=, name=二牛, age=], User [id=, name=三牛, age=], User [id=, name=四牛, age=]]
数据库最后结果如下

总的来说,功能我们还是实现了,虽然不够完美,这也让我感觉到框架并不是什么高深的东西,
九层之台,起于累土,千里之行,始于足下。
上面这句我们的老话和大家共勉,不要害怕,再厉害的攻城狮,也是用一行一行代码堆砌实现他们想要的功能,有想法就去尝试
以上内容都是原创,如果转载请标注并标明来源于
大王让我写代码:http://www.cnblogs.com/yeyeck/p/7468644.html
mapper.xml是怎样实现Dao层接口的更多相关文章
- MyBatis框架的XML数据访问Dao层接口的组合使用
MyBatis 的前生为Apache的开源项目iBatis.其优势在于灵活,几乎可以替代JDBC,同时提供了编程接口.目前MyBatis的数据访问Dao层不需要实现类,也不需要像JDBC那样拼接Hql ...
- iBatis——自动生成DAO层接口提供操作函数(详解)
iBatis——自动生成DAO层接口提供操作函数(详解) 在使用iBatis进行持久层管理时,发现在使用DAO层的updateByPrimaryKey.updateByPrimaryKeySelect ...
- Mybatis中DAO层接口没有写实现类,Mapper中的方法和DAO接口方法是怎么绑定到一起的
参考mybatis入门基础(二)----原始dao的开发和mapper代理开发 其实也就是通过接口名与mapper的id绑定在一起,通过SQL去写实现类,返回数据.
- Java SE 之 DAO层接口设计思想
用图说话 好处 1.只需要定义好IBaseDao的接口方法,并只需要实现BaseDaoImpl的方法,而具体的业务类和业务类/接口的方法的基本方法(IBaseDao已定义的)并不需要再考虑实现. 2. ...
- Mybatis中DAO层接口没有写实现类,Mapper中的方法和DAO接口方法是怎么绑定到一起的,其内部是怎么实现的
其实也就是通过接口名与mapper的id绑定在一起(即相同),通过SQL去写实现类,返回数据.
- 瞎j8封装第二版之用xml文件来代理dao接口
也是重新整理了之前的那篇 模仿Mybatis用map per.xml实现Dao层接口的功能 话不多说直接上代码 首先是结构 依赖pom.xml <?xml version="1.0&q ...
- Mybatis Dao层注解及XML组合Dao的开发方式
mybatis可以用xml进行数据操作,也可以在dao层用注解的方式,也可以采取xml和dao层接口组合使用的方法.显然 ,后者更加简单. 实体类Student package com.zhao. ...
- MyBatis开发Dao层的两种方式(Mapper动态代理方式)
MyBatis开发原始Dao层请阅读我的上一篇博客:MyBatis开发Dao层的两种方式(原始Dao层开发) 接上一篇博客继续介绍MyBatis开发Dao层的第二种方式:Mapper动态代理方式 Ma ...
- 一个简单的Java代码生成工具—根据数据源自动生成bean、dao、mapper.xml、service、serviceImpl
目录结构 核心思想 通过properties文件获取数据源—>获取数据表的字段名称.字段类型等—>生成相应的bean实体类(po.model).dao接口(基本的增删改查).mapper. ...
随机推荐
- C3制作导航栏分割线及立体风格
//首先写一个导航栏样式 .nav{ width:560px; height: 50px; font:bold 0/50px Arial; text-align:center; ...
- GetStdHandle 函数--获取标准设备的句柄
#GetStdHandle函数 来源:https://msdn.microsoft.com/en-us/library/ms683231.aspx 作用 该函数用于取得指定的标准设备的句柄(标准输入, ...
- 关于frameset的一些小总结
如果我们在一个页面需要由多个子页面一同组成,那么我们可以通过frameset标签来嵌套别的页面 例如 这样的页面布局的思路是: 1,先将页面分为上下两部分也就是 A B 2,再将B部分分为左右两部分 ...
- (转)JAVA新特征
Java线程:新特征-有返回值的线程 在Java5之前,线程是没有返回值的,常常为了“有”返回值,破费周折,而且代码很不好写.或者干脆绕过这道坎,走别的路了. 现在Java终于有可返回值的任务 ...
- Android学习笔记-EditText(输入框)(二)
6.控制EditText四周的间隔距离与内部文字与边框间的距离 我们使用margin相关属性增加组件相对其他控件的距离,比如android:marginTop = "5dp" 使用 ...
- Eclipse 改变字体大小,设置背景色
Eclipse背景颜色修改: 操作界面默认颜色为白色.对于我们长期使用电脑编程的人来说,白色很刺激我们的眼睛,所以我经常会改变workspace的背景色,使眼睛舒服一些.设置方法如下: 1.打开win ...
- Scrapy爬取西刺代理ip流程
西刺代理爬虫 1. 新建项目和爬虫 scrapy startproject daili_ips ...... cd daili_ips/ #爬虫名称和domains scrapy genspider ...
- 一个想法照进现实-《IT连》创业项目:聊聊IT连App是如何思考解决IT人员单身问题的
前言: 根据最早我编写的IT联盟社区众筹计划书的思路方向:社交->资讯=>评级=>培训. 现在在实现第一个阶段中,而且这个阶段可能会走很久. 今天开文,主要是讲述一下,现在的版本为什 ...
- 最新 Zookeeper + Flume + Kafka 简易整合教程
在大数据领域有很多耳熟能详的框架,今天要介绍的就是 zookeeper.flume.kafka.因为平时是做数据接入的,所以对这些实时的数据处理系统不是很熟悉.通过官网的简要介绍,搭建了一套简要的平台 ...
- 浅谈odoo 后台与前端文件(附件)的存储与下载
odoo 后台与前端文件(附件)存储与下载实现 笔记太多了很乱,想想还是写博客的好,慢慢更 当然了,前提是你已经配好了odoo开发环境 一.odoo后台界面实现附件的上传和下载 1).在应用中搜索下图 ...