攻占SSM之手写Mybatis的xml实现Dao层接口

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

反正我是这么实现的,至于Mybatis是怎么实现的,我还没看源码。

  有不了解的可以点下面链接

手把手封装数据层之DButil数据库连接的封装

手把手封装数据层之DataUtil数据库操作的封装


下面是我的目录结构

因为要解析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=2, name=王八蛋, age=23]
[User [id=1, name=蛋蛋, age=200], User [id=2, name=王八蛋, age=23], User [id=4, name=李四, age=66], User [id=5, name=小明, age=66], User [id=6, name=小红, age=66], User [id=7, name=小张, age=66], User [id=8, name=李三, age=66], User [id=9, name=大牛, age=88], User [id=10, name=二牛, age=77], User [id=11, name=三牛, age=66], User [id=12, name=四牛, age=55]]

数据库最后结果如下

 
时间: 2024-10-27 08:23:28

攻占SSM之手写Mybatis的xml实现Dao层接口的相关文章

MyBatis xml和dao层接口组合使用

在这里,不再介绍Mybatis的配置. mybatis可以用xml进行数据操作,也可以在dao层用注解的方式,也可以采取xml和dao层接口组合使用的方法.显然 ,后者更加简单. 实体类Student package com.zhao.entity; /** * * @author: zhao * @time: 2016年5月31日 * * @description:学生 */ public class Student { private int stuId; private String st

了解mybatis源码手写mybatis

一:mybatis概述 MyBatis 是一款优秀的持久层框架,它支持定制化 SQL.存储过程以及高级映射.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录. 二:手写mybatis 实现思路:: 1创建SqlSessionFactory实例 2:实例化过程中,加载配置

手写MyBatis ORM框架实践

一.实现手写Mybatis三个难点 1.接口既然不能被实例化?那么我们是怎么实现能够调用的? 2.参数如何和sql绑定 3.返回结果 下面是Mybatis接口 二.Demo实现 1.创建Maven工程(开发工具Eclipse) 下一步 下一步 然后点击“完成” 2.配置pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema

手写MyBatis,纯手工打造开源框架(第四篇:决胜千里)- 第272篇

说明 MyBatis版本:3.5.1 相关历史文章(阅读本文之前,您可能需要先看下之前的系列) Spring Boot MyBatis最全教程:你值得拥有MyBatis能脱离Spring吗一图纵览MyBatis的工作原理从源码看MyBatis,竟如此简单MyBatis的Mapper是什么`垃圾` 手写MyBatis,纯手工打造开源框架(第一篇:风云再起) 手写MyBatis,纯手工打造开源框架(第二篇:君临天下) 手写MyBatis,纯手工打造开源框架(第三篇:运筹帷幄) 前言        运

手写Mybatis,彻底搞懂框架原理

mybatis的前身是iBatis,其源于"Internet"和"abatis"的组合,是一款优秀的持久层框架,它支持定制化SQL.存储过程以及高级映射.mybatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集,它可以使用简单的XML或注解来配置和映射原生信息,将接口和Java的POJO映射成数据库中的记录. Mybatis的架构设计Mybatis的功能架构分为三层: 接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库.接口层接

IDEA中写MyBatis的xml配置文件编译报错的坑

IDEA中写MyBatis的xml配置文件编译报错的坑 说明:用IDEA编译工具在项目中使用Mybatis框架,编写mybatis-config.xml和Mapper.xml配置文件时,编译项目出现错误,错误提示为: xml中1字节的UTF-8序列的字节1无效 The cause of this is a file that is not UTF-8 is being parsed as UTF-8. It is likely that the parser is encountering a

手写数字识别——基于全连接层和MNIST数据集

本文使用TensorFlow2.0手动搭建简单的全连接网络进行MNIST手写数据集的分类识别,逐步讲述实现过程,穿插TensorFlow2.0语法,文末给出完整的代码.废话少说,开始动手吧! 一.量子波动撸代码 该节先给出各代码片段,第二节将这些片段汇总成程序,这些代码片段故意包含了一些错误之处,在第二节中会进行一一修正.如要正确的代码,请直接参考第三节.如若读者有一番闲情逸致,可跟随笔者脚步,看看自己是否可以事先发现这些错误. 首先,导入依赖的两个模块,一个是tensorflow,另一个是te

Mybatis框架三:DAO层开发、Mapper动态代理开发

这里是最基本的搭建:http://www.cnblogs.com/xuyiqing/p/8600888.html 接下来做到了简单的增删改查:http://www.cnblogs.com/xuyiqing/p/8601506.html 但是发现代码重复过多等问题 接下来整合并实现DAO开发: 一:原始DAO开发: package dao; import pojo.User; public interface UserDao { public User selectUserById(Integer

详解intellij idea搭建SSM框架(spring+maven+mybatis+mysql+junit)(上)

SSM(Spring+SpringMVC+MyBatis)框架集由Spring.SpringMVC.MyBatis三个开源框架整合而成,常作为数据源较简单的web项目的框架. 其中spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架. SpringMVC分离了控制器.模型对象.分派器以及处理程序对象的角色,这种分离让它们更容易进行定制. MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架. 本文旨在快速且详细的介绍intellij idea 搭建SS