自己手动仿写MyBatis框架完整版

 

一、MyBatis简介

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

二、MyBatis特点

1、简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件和配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。

2、灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql基本上可以实现我们不使用数据访问框架可以实现的所有功能,或许更多。

3、解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。

4、提供映射标签,支持对象与数据库的ORM字段关系映射

5、提供对象关系映射标签,支持对象关系组建维护

6、提供xml标签,支持编写动态sql。

三、源码目录结构

1、源码目录内容

1.1、日志

org.apache.ibatis.logging

org.apache.ibatis.logging.commons

org.apache.ibatis.logging.jdbc

org.apache.ibatis.logging.jdk14

org.apache.ibatis.logging.log4j

org.apache.ibatis.logging.log4j2

org.apache.ibatis.logging.nologging

org.apache.ibatis.logging.slf4j

org.apache.ibatis.logging.stdout

1.2、异常

org.apache.ibatis.exceptions

1.3、缓存

org.apache.ibatis.cache

org.apache.ibatis.cache.decorators

org.apache.ibatis.cache.impl

1.4、解析

org.apache.ibatis.parsing

xml解析,${} 格式的字符串解析

1.5、类型处理器

org.apache.ibatis.type

实现java和jdbc中的类型之间转换

1.6、IO

org.apache.ibatis.io

通过类加载器在jar包中寻找一个package下满足条件(比如某个接口的子类)的所有类

1.7、反射

org.apache.ibatis.reflection

org.apache.ibatis.reflection.factory

org.apache.ibatis.reflection.invoker

org.apache.ibatis.reflection.property

org.apache.ibatis.reflection.wrapper

可以参考MetaObjectTest来跟踪调试,基本上用到了reflection包下所有的类

1.8、数据源

org.apache.ibatis.datasource

org.apache.ibatis.datasource.jndi

org.apache.ibatis.datasource.pooled

org.apache.ibatis.datasource.unpooled

1.9、事务

org.apache.ibatis.transaction

org.apache.ibatis.transaction.jdbc

org.apache.ibatis.transaction.managed

1.10、会话

org.apache.ibatis.session

org.apache.ibatis.session.defaults

1.11、JDBC单元测试工具

org.apache.ibatis.jdbc

1.12、构建

org.apache.ibatis.builder

org.apache.ibatis.builder.annotation

org.apache.ibatis.builder.xml

1.13、映射

org.apache.ibatis.mapping

1.14、脚本

org.apache.ibatis.scripting

org.apache.ibatis.scripting.defaults

org.apache.ibatis.scripting.xmltags

1.15、注解

org.apache.ibatis.annotations

1.16、绑定

org.apache.ibatis.binding

1.17、执行器

org.apache.ibatis.executor

org.apache.ibatis.executor.******

org.apache.ibatis.executor.loader

org.apache.ibatis.executor.loader.cglib

org.apache.ibatis.executor.loader.javassist

org.apache.ibatis.executor.parameter

org.apache.ibatis.executor.result

org.apache.ibatis.executor.resultset

org.apache.ibatis.executor.statement

1.18、插件

org.apache.ibatis.plugin

2、源码包对应架构

四、MyBatis层次结构

五、自己的MyBatis设计思路阐述

在了解了MyBatis的设计原理及架构之后,我的简化版MyBatis初现端倪。总结之后如下图:

1、读取xml文件,建立连接

MyConfiguration负责与人交互。当读取xml文件后,会将属性和连接数据库的操作封装在MyConfiguration对象中供后面的组件调用。我打算将使用dom4j来读取xml文件,因为它具有性能优异和非常方便使用的特点。

2、创建SqlSession,搭建Configuration和Executor之间的桥梁

一个Session仅拥有一个对应的数据库连接。类似于一个前段请求Request,它可以直接调用execute来执行SQL语句。从流程图中可以看出,MySqlSession的成员变量中必须得有MyExecutor和MyConfiguration来集中做调配。MySqlSession中会有一个getMapper方法,使用动态代理生成对象后,就可以进行对数据库的操作。

3、创建Executor,封装JDBC操作数据库

Executor是一个执行器,负责SQL语句的生成和查询缓存的维护,也就是JDBC的代码将在这里构建。

4、创建MapperProxy,使用动态代理生成Mapper对象

要保证只对指定的接口生成一个对象,执行它的时候运行sql,而接口无法直接调用方法,所以这里使用动态代理生成对象,在执行时回到MySqlSession中调用对应的方法,最终由MyExecutor做JDBC对对应的方法调用执行。这样设计是为了单一职责,可扩展性更强。

六、基础环境

JDK 8 + MySQL 8。

这里有个坑,当时装数据库时直接官网下的最新版,在JDBC连接数据库时配置的参数需要修改,不然会报各种奇奇怪怪的错误,还是推荐大家使用MySQL 5版本。

七、代码编写

思路捋完了,该上代码了…我将按照我编写时的思路顺序一一阐述。由于我在项目里集成了日志,便于我们直观的查看各个类之间的调用顺序。

1、项目文件及目录结构

2、在创建的maven项目中的pom.xml文件中导入依赖

<dependencies>

  <dependency>
    <groupId>
junit</groupId>
    <artifactId>
junit</artifactId>
    <version>
4.11</version>
    <scope>
test</scope>
  </dependency>

 
<!--读取xml文件-->
  <!-- https://mvnrepository.com/artifact/dom4j/dom4j -->
 
<dependency>
    <groupId>
dom4j</groupId>
    <artifactId>
dom4j</artifactId>
    <version>
1.6.1</version>
  </dependency>

 
<!--MySQL-->
  <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
 
<dependency>
    <groupId>
mysql</groupId>
    <artifactId>
mysql-connector-java</artifactId>
    <version>
8.0.11</version>
  </dependency>

 
<!--日志-->
 
<dependency>
    <groupId>
org.slf4j</groupId>
    <artifactId>
slf4j-api</artifactId>
    <version>
1.7.25</version>
  </dependency>
  <dependency>
    <groupId>
org.slf4j</groupId>
    <artifactId>
slf4j-log4j12</artifactId>
    <version>
1.7.25</version>
    <scope>
runtime</scope>
  </dependency>
 
<!-- common-logging 实际调用slf4j -->
 
<dependency>
    <groupId>
org.slf4j</groupId>
    <artifactId>
jcl-over-slf4j</artifactId>
    <version>
1.7.25</version>
    <scope>
runtime</scope>
  </dependency>
 
<!-- java.util.logging 实际调用slf4j -->
 
<dependency>
    <groupId>
org.slf4j</groupId>
    <artifactId>
jul-to-slf4j</artifactId>
    <version>
1.7.25</version>
    <scope>
runtime</scope>
  </dependency>
  <dependency>
    <groupId>
log4j</groupId>
    <artifactId>
log4j</artifactId>
    <version>
1.2.17</version>
  </dependency>
  <dependency>
  <groupId>
org.lazyluke</groupId>
  <artifactId>
log4jdbc-remix</artifactId>
  <version>
0.2.7</version>
  <scope>
runtime</scope>
  </dependency>

</dependencies>

 

3、log4j的日志配置log4j.properties

# Output pattern : date [thread] priority category - message

log4j.rootLogger=INFO, Console, RollingFile



##Appenders##



#Console Appender

log4j.appender.Console=org.apache.log4j.ConsoleAppender

log4j.appender.Console.layout=org.apache.log4j.PatternLayout

log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p %-40.40c -%m%n



#RollingFile Appender(Store application message, hourly rolling, threshold is INFO)

log4j.appender.RollingFile=org.apache.log4j.DailyRollingFileAppender

log4j.appender.RollingFile.File=${user.home}/logs/xweb.log

log4j.appender.RollingFile.Threshold=INFO

log4j.appender.RollingFile.File.DatePattern=.yyyy-MM-dd-HH-mm-ss

log4j.appender.RollingFile.layout=org.apache.log4j.PatternLayout

log4j.appender.RollingFile.layout.ConversionPattern=%d [%t] %-5p %-40.40c -%m%n





##Loggers##



#Project defalult level

log4j.logger.org.springside.examples.showcase=INFO

log4j.logger.com.ucredit=DEBUG,RollingFile



#log4jdbc

log4j.logger.jdbc.sqltiming=INFO

log4j.logger.java.sql.Connection=DEBUG

log4j.logger.java.sql.Statement=DEBUG

log4j.logger.java.sql.PreparedStatement=DEBUG

4、创建连接数据库的参数配置文件config.xml

<?xml version="1.0" encoding="UTF-8"?>

<database>

   <!--MySQL-5版本的是com.mysql.jdbc.Driver , MySQL-8版本的是     com.mysql.cj.jdbc.Driver-->

   <property name="driverClassName">
       com.mysql.cj.jdbc.Driver
</property>

   <property name="url">jdbc:mysql://localhost/test
       ?useUnicode=true
       &amp;characterEncoding=UTF-8
       &amp;serverTimezone=UTC
       &amp;useSSL=false
   </property>

   <property name="username">root</property>

   <property name="password">888888</property>
</database>

5、创建数据库test,创建t_user表

6、根据t_user创建实User实体类

package com.ucredit.bean;



import java.io.Serializable;



/**  * @ClassName User  * @Author zhangxianqing  * @Date 2018/8/1 15:21  * @Version 1.0  **/ public class User implements Serializable {



    private static final long serialVersionUID = -277386013177856279L;



    private int id;

    private String username;

    private String password;

    private String email;

    private String address;

    private String phone;

    private String sex;

    private String age;



    public int getId() {

        return id;

    }



    public void setId(int id) {

        this.id = id;

    }



    public String getUsername() {

        return username;

    }



    public void setUsername(String username) {

        this.username = username;

    }



    public String getPassword() {

        return password;

    }



    public void setPassword(String password) {

        this.password = password;

    }



    public String getEmail() {

        return email;

    }



    public void setEmail(String email) {

        this.email = email;

    }



    public String getAddress() {

        return address;

    }



    public void setAddress(String address) {

        this.address = address;

    }



    public String getPhone() {

        return phone;

    }



    public void setPhone(String phone) {

        this.phone = phone;

    }



    public String getSex() {

        return sex;

    }



    public void setSex(String sex) {

        this.sex = sex;

    }



    public String getAge() {

        return age;

    }



    public void setAge(String age) {

        this.age = age;

    }



    public User() {



    }



    public User(int id, String username, String password, String email, String address, String phone, String sex, String age) {

        this.id = id;

        this.username = username;

        this.password = password;

        this.email = email;

        this.address = address;

        this.phone = phone;

        this.sex = sex;

        this.age = age;

    }



    @Override

    public String toString() {

        return id + "," + username + "," + password + "," + email + "," + address + "," + phone + "," + sex + "," + age;

    }



}

 

7、创建UserMapper接口

package com.ucredit.mapper;



import com.ucredit.bean.User;



/**  * @ClassName UserMapper  * @Author zhangxianqing  * @Date 2018/8/1 15:21  * @Version 1.0  **/ public interface UserMapper {

    User selectById(int id);



    void insertRow(User user);



    void updateById(User user);



    void deleteById(int id);

}

8、对应的UserMapper.xml

<?xml version="1.0" encoding="UTF-8"?>

<mapper namespace="com.ucredit.mapper.UserMapper">

    <select id="selectById" resultType="com.ucredit.bean.User">

        select id,username,password,email,address,phone,sex,age from t_user where id = ?

    </select>

    <insert id="insertRow">

        insert into t_user (id,username,password,email,address,phone,sex,age) values (?,?,?,?,?,?,?,?)

    </insert>

    <update id="updateById">

        update t_user set username = ? , password= ? , email = ? , address = ? , phone = ? , sex = ? , age = ? where id=?

    </update>

    <delete id="deleteById">

        delete from t_user where id = ?

    </delete>

</mapper>

9、基本配置到此结束,接下来实现MyConfiguration

package com.ucredit.sqlsession;



import com.ucredit.config.Function;

import com.ucredit.config.MapperBean;

import org.dom4j.Document;

import org.dom4j.DocumentException;

import org.dom4j.Element;

import org.dom4j.io.SAXReader;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;



import java.io.InputStream;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.SQLException;

import java.util.ArrayList;

import java.util.Iterator;



/**  * @ClassName MyComfiguration  * @Author zhangxianqing  * @Date 2018/8/1 15:22  * @Version 1.0  **/ public class MyConfiguration {



    private ClassLoader loader = ClassLoader.getSystemClassLoader();



    private final Logger logger = LoggerFactory.getLogger(MyConfiguration.class);



    /**      * 读取xml信息并处理      */     public Connection build() {



        try {

            InputStream stream = loader.getResourceAsStream("config.xml");

            SAXReader reader = new SAXReader();

            Document document = reader.read(stream);

            Element element = document.getRootElement();

            logger.info("加载数据库comfig.xml成功!");

            return evalDataSource(element);

        } catch (DocumentException e) {

            logger.error("读取数据库comfig.xml失败!"+e.getMessage());

            throw new RuntimeException("读取xml信息失败!");

        }



    }



    private Connection evalDataSource(Element element) {



        if (!element.getName().equals("database")) {

            throw new RuntimeException("读取到了错误的数据库配置信息");

        }



        String driverClassName = null;

        String url = null;

        String username = null;

        String password = null;



        //获取属性节点

        for (Object item : element.elements("property")) {



            Element e = (Element) item;

            String value = getValue(e);

            String name = e.attributeValue("name");



            if (name == null || value == null) {

                throw new RuntimeException("读取property信息失败");

            }



            //赋值

            switch (name) {

                case "driverClassName":

                    driverClassName = value;

                    break;

                case "url":

                    url = value;

                    break;

                case "username":

                    username = value;

                    break;

                case "password":

                    password = value;

                    break;

                default:

                    throw new RuntimeException("读取数据库配置文件给4种属性赋值时失败!");

            }



        }



        //连接数据库

        try {

            Class.forName(driverClassName);

            logger.info("连接数据库时获取DriverClassName成功!");

        } catch (ClassNotFoundException e) {

            throw new RuntimeException("连接数据库时获取DriverClassName失败:" + e.getMessage());

        }



        Connection connection = null;



        try {

            //建立数据库连接

            connection = DriverManager.getConnection(url, username, password);

            logger.info("连接数据库时校验urlusernamepassword成功!");

        } catch (SQLException e) {

            throw new RuntimeException("连接数据库时获取url,username,password失败:" + e.getMessage());

        }



        return connection;

    }



    //获取property属性的值,如果有value值,则读取,没有设置value,则读取内容

    private String getValue(Element element) {

        /*等效下面的代码

        if(element.hasContent()){

            return element.getText();

        }else{{

            return element.attributeValue("value");

        }*/

        return element.hasContent() ? element.getText() : element.attributeValue("value");

    }



    public MapperBean readMapper(String path) {



        MapperBean mapperBean = new MapperBean();



        try {

            InputStream stream = loader.getResourceAsStream(path);

            SAXReader reader = new SAXReader();

            Document document = reader.read(stream);

            Element root = document.getRootElement();



            //存储mapper节点的namespace的值

            mapperBean.setInterfaceName(root.attributeValue("namespace").trim());

            //存储方法

            ArrayList<Function> list = new ArrayList<>();

            //遍历根节点下的子节点

            for (Iterator iterator = root.elementIterator(); iterator.hasNext(); ) {



                Function function = new Function();



                Element element = (Element) iterator.next();



                String sqlType = element.getName().trim();

                String functionName = element.attributeValue("id").trim();

                String sql = element.getText().trim();

                String resultType = null;

                Object newInstance = null;

                if (null != element.attributeValue("resultType")) {

                    resultType = element.attributeValue("resultType").trim();



                    try {

                        newInstance = Class.forName(resultType).newInstance();

                    } catch (InstantiationException e) {

                        e.printStackTrace();

                    } catch (IllegalAccessException e) {

                        e.printStackTrace();

                    } catch (ClassNotFoundException e) {

                        e.printStackTrace();

                    }

                    function.setResultType(newInstance);

                }

                function.setSqlType(sqlType);

                function.setFunctionName(functionName);

                function.setSql(sql);



                list.add(function);



            }



            //存储方法

            mapperBean.setList(list);



        } catch (DocumentException e) {

            e.printStackTrace();

        }



        return mapperBean;



    }



}

10、使用面向对象的思想编写MapperBean包括接口名、接口下的方法

package com.ucredit.config;



import java.util.List;



/**  * @ClassName MapperBean  * @Author zhangxianqing  * @Date 2018/8/1 15:19  * @Version 1.0  **/ public class MapperBean {

    //接口名

    private String interfaceName;

    //接口下所有方法

    private List<Function> list;



    public String getInterfaceName() {

        return interfaceName;

    }



    public void setInterfaceName(String interfaceName) {

        this.interfaceName = interfaceName;

    }



    public List<Function> getList() {

        return list;

    }



    public void setList(List<Function> list) {

        this.list = list;

    }



    public MapperBean() {

    }



    public MapperBean(String interfaceName, List<Function> list) {



        this.interfaceName = interfaceName;

        this.list = list;

    }



    @Override

    public String toString() {

        return "MapperBean{" +

                "interfaceName='" + interfaceName + '\'' +

                ", list=" + list +

                '}';

    }



}

11、Function对象包括sql的类型、方法名、sql语句、返回类型和参数类型

package com.ucredit.config;



/**  * @ClassName Function  * @Author zhangxianqing  * @Date 2018/8/1 15:19  * @Version 1.0  **/ public class Function {

    private String sqlType;

    private String functionName;

    private String sql;

    private Object resultType;

    private String parameterType;



    public String getSqlType() {

        return sqlType;

    }



    public void setSqlType(String sqlType) {

        this.sqlType = sqlType;

    }



    public String getFunctionName() {

        return functionName;

    }



    public void setFunctionName(String functionName) {

        this.functionName = functionName;

    }



    public String getSql() {

        return sql;

    }



    public void setSql(String sql) {

        this.sql = sql;

    }



    public Object getResultType() {

        return resultType;

    }



    public void setResultType(Object resultType) {

        this.resultType = resultType;

    }



    public String getParameterType() {

        return parameterType;

    }



    public void setParameterType(String parameterType) {

        this.parameterType = parameterType;

    }



    public Function() {

    }



    public Function(String sqlType, String functionName, String sql, Object resultType, String parameterType) {

        this.sqlType = sqlType;

        this.functionName = functionName;

        this.sql = sql;

        this.resultType = resultType;

        this.parameterType = parameterType;

    }



    @Override

    public String toString() {

        return "Function{" +

                "sqlType='" + sqlType + '\'' +

                ", functionName='" + functionName + '\'' +

                ", sql='" + sql + '\'' +

                ", resultType=" + resultType +

                ", parameterType='" + parameterType + '\'' +

                '}';

    }



}

12、接下来实现MySqlSession,成员变量里得有Excutor和MyConfiguration,代码的精髓就在getMapper(重要)的方法里

package com.ucredit.sqlsession;



import org.slf4j.Logger;

import org.slf4j.LoggerFactory;



import java.lang.reflect.Proxy;



/**  * @ClassName MySqlsession  * @Author zhangxianqing  * @Date 2018/8/1 15:22  * @Version 1.0  **/ public class MySqlsession {



    private Executor excutor = new MyExecutor();



    private MyConfiguration myConfiguration = new MyConfiguration();



    private final Logger logger = LoggerFactory.getLogger(MySqlsession.class);



    public <T> T selectOne(String statement, Object parameter) {

        logger.info("我是查询的Sqlsession!");

        return excutor.query(statement, parameter);

    }



    public void insertOne(String statement, Object parameter) {

        logger.info("我是添加的Sqlsession!");

        excutor.save(statement, parameter);

    }



    public void updateOne(String statement, Object parameter) {

        logger.info("我是修改的Sqlsession!");

        excutor.modify(statement, parameter);

    }



    public void deleteOne(String statement, Object parameter) {

        logger.info("我是删除的Sqlsession!");

        excutor.remove(statement, parameter);

    }



    public <T> T getMapper(Class<T> tClass) {

        logger.info("已通过代理获取对象!");

        //动态代理调用

        return (T) Proxy.newProxyInstance(

                tClass.getClassLoader(),

                new Class[]{tClass},

                new MyMapperProxy(this, myConfiguration)

        );

    }



}

13、创建Executor接口解耦合

package com.ucredit.sqlsession;



/**  * @ClassName Excutor  * @Author zhangxianqing  * @Date 2018/8/1 15:21  * @Version 1.0  **/ public interface Executor {

    <T> T query(String statement, Object parameter);



    void save(String statement, Object parameter);



    void modify(String statement,Object parameter);



    void remove(String statement, Object parameter);
}

14、创建Executor实现类MyExecutor,在MyExecutor中封装了JDBC的操作

package com.ucredit.sqlsession;



import com.ucredit.bean.User;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;



import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;



/**  * @ClassName MyExcutor  * @Author zhangxianqing  * @Date 2018/8/1 15:22  * @Version 1.0  **/ public class MyExecutor implements Executor {



    private MyConfiguration myConfiguration = new MyConfiguration();



    private final Logger logger = LoggerFactory.getLogger(MyExecutor.class);



    /**      * 查询方法      *      * @param statement      * @param parameter      * @param <T>

     * @return      */     @Override

    public <T> T query(String statement, Object parameter) {



        Connection connection = getConnection();



        ResultSet resultSet = null;

        PreparedStatement pst = null;



        try {

            pst = connection.prepareStatement(statement);

            //设置参数

            pst.setString(1, parameter.toString());

            resultSet = pst.executeQuery();

            User user = new User();

            //遍历结果集

            while (resultSet.next()) {

                user.setId(resultSet.getInt(1));

                user.setUsername(resultSet.getString(2));

                user.setPassword(resultSet.getString(3));

                user.setEmail(resultSet.getString(4));

                user.setAddress(resultSet.getString(5));

                user.setPhone(resultSet.getString(6));

                user.setSex(resultSet.getString(7));

                user.setAge(resultSet.getString(8));

            }

            logger.info("query,遍历结果集成功!");

            return (T) user;

        } catch (SQLException e) {

            logger.error("query,遍历结果集失败!" + e.getMessage());

            e.printStackTrace();

        } finally {

            close(resultSet, pst, connection);

        }



        return null;



    }



    /**      * 插入方法      *      * @param statement      * @param parameter      */     @Override

    public void save(String statement, Object parameter) {



        Connection connection = getConnection();



        PreparedStatement pst = null;



        try {

            pst = connection.prepareStatement(statement);

            //设置参数

            String[] strings = parameter.toString().split(",");

            pst.setInt(1, Integer.parseInt(strings[0]));

            pst.setString(2, strings[1]);

            pst.setString(3, strings[2]);

            pst.setString(4, strings[3]);

            pst.setString(5, strings[4]);

            pst.setString(6, strings[5]);

            pst.setString(7, strings[6]);

            pst.setString(8, strings[7]);

            pst.execute();

            commit(connection);

            logger.info("save,设置参数成功!");

        } catch (SQLException e) {

            rollback(connection);

            logger.error("save,设置参数失败!" + e.getMessage());

            e.printStackTrace();

        } finally {

            close(null, pst, connection);

        }



    }



    /**      * 修改方法      *      * @param statement      * @param parameter      */     @Override

    public void modify(String statement, Object parameter) {



        Connection connection = getConnection();



        PreparedStatement pst = null;



        try {

            pst = connection.prepareStatement(statement);

            //设置参数

            String[] strings = parameter.toString().split(",");

            pst.setString(1, strings[1]);

            pst.setString(2, strings[2]);

            pst.setString(3, strings[3]);

            pst.setString(4, strings[4]);

            pst.setString(5, strings[5]);

            pst.setString(6, strings[6]);

            pst.setString(7, strings[7]);

            pst.setInt(8, Integer.parseInt(strings[0]));

            pst.execute();

            commit(connection);

            logger.info("modify,设置参数成功!");

        } catch (SQLException e) {

            rollback(connection);

            logger.error("modify,设置参数失败!" + e.getMessage());

            e.printStackTrace();

        } finally {

            close(null, pst, connection);

        }



    }



    /**      * 删除方法      *      * @param statement      * @param parameter      */     @Override

    public void remove(String statement, Object parameter) {



        Connection connection = getConnection();



        PreparedStatement pst = null;



        try {

            pst = connection.prepareStatement(statement);

            //设置参数

            pst.setString(1, parameter.toString());

            pst.execute();

            commit(connection);

            logger.info("remove,设置参数成功!");

        } catch (SQLException e) {

            rollback(connection);

            logger.error("remove,设置参数失败!" + e.getMessage());

            e.printStackTrace();

        } finally {

            close(null, pst, connection);

        }



    }



    /**      * 加载数据库配置文件,连接数据库      *      * @return      */     private Connection getConnection() {

        try {

            Connection connection = myConfiguration.build();

            connection.setAutoCommit(false);

            logger.info("连接数据库成功!");

            return connection;

        } catch (Exception e) {

            logger.error("连接数据库失败!" + e.getMessage());

            e.printStackTrace();

        }

        return null;

    }



    /**      * 关闭资源      *      * @param pst      * @param connection      */     private void close(ResultSet resultSet, PreparedStatement pst, Connection connection) {

        try {

            if (pst != null) {

                pst.close();

                logger.info("PreparedStatement已关闭!");

            }

            if (connection != null) {

                connection.close();

                logger.info("Connection已关闭!");

            }

        } catch (SQLException e) {

            logger.error("关闭资源失败!" + e.getMessage());

            e.printStackTrace();

        }

    }
    /**      * 事物提交      *      * @param connection      */     private void commit(Connection connection) {

        try {

            connection.commit();

        } catch (SQLException e) {

            e.printStackTrace();

        }

    }



    /**      * 事物回滚      *      * @param connection      */     private void rollback(Connection connection) {

        try {

            connection.rollback();

        } catch (SQLException e) {

            e.printStackTrace();

        }

    }



}

15、MyMapperProxy代理类完成读取的UserMapper.xml中方法与真实方法校验,并调用执行对应的方法

package com.ucredit.sqlsession;



import com.ucredit.config.Function;

import com.ucredit.config.MapperBean;



import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.util.List;



/**  * @ClassName MyMapperProxy  * @Author zhangxianqing  * @Date 2018/8/1 15:22  * @Version 1.0  **/ public class MyMapperProxy implements InvocationHandler {



    private MySqlsession mySqlsession;



    private MyConfiguration myConfiguration;



    public MyMapperProxy(MySqlsession mySqlsession, MyConfiguration myConfiguration) {

        this.mySqlsession = mySqlsession;

        this.myConfiguration = myConfiguration;

    }



    @Override

    public Object invoke(Object proxy, Method method, Object[] args) {

        MapperBean mapperBean = myConfiguration.readMapper("UserMapper.xml");

        //判断是否是xml文件对应的接口

        if (!method.getDeclaringClass().getName().equals(mapperBean.getInterfaceName())) {

            return null;

        }

        List<Function> list = mapperBean.getList();

        if (null != list || 0 != list.size()) {

            for (Function function : list) {

                //判断id是否和接口方法一致

                if (judge(method, function, "selectById")) {

                    return mySqlsession.selectOne(function.getSql(), String.valueOf(args[0]));

                }

                if (judge(method, function, "deleteById")) {

                    mySqlsession.deleteOne(function.getSql(), String.valueOf(args[0]));

                }

                if (judge(method, function, "insertRow")) {

                    mySqlsession.insertOne(function.getSql(), String.valueOf(args[0]));

                }

                if (judge(method, function, "updateById")) {

                    mySqlsession.updateOne(function.getSql(), String.valueOf(args[0]));

                }

            }

        }

        return null;

    }



    /**      * 判断mapper文件中id是否一致      *      * @param method      * @param function      * @param methodId      * @return      */     private boolean judge(Method method, Function function, String methodId) {

        if (method.getName().equals(function.getFunctionName()) && function.getFunctionName().equals(methodId)) {

            return true;

        } else {

            return false;

        }

    }



}

16、至此,自写的简版MyBatis框架已经完成,再写下测试代码RunOwnMybatis

package com.ucredit.run;



import com.ucredit.bean.User;

import com.ucredit.mapper.UserMapper;

import com.ucredit.sqlsession.MySqlsession;



/**  * @ClassName runOwnMybatis  * @Author zhangxianqing  * @Date 2018/8/2 10:13  * @Version 1.0  **/ public class RunOwnMybatis {

    public static void main(String[] args) {



        MySqlsession mySqlsession = new MySqlsession();

        UserMapper mapper = mySqlsession.getMapper(UserMapper.class);



        //查询

        try {

            User user = mapper.selectById(1);

            System.out.println("查询成功!");

            System.out.println("查询内容:"+user);

        } catch (Exception e) {

            e.printStackTrace();

        }



        //插入

        try {

            User user = new User();

            user.setId(2);

            user.setUsername("要修改的人");

            user.setPassword("111111");

            user.setEmail("[email protected]");

            user.setAddress("昌平区");

            user.setPhone("99999999999");

            user.setSex("");

            user.setAge("28");

            mapper.insertRow(user);

            System.out.println("插入成功!");

            System.out.println("插入内容为:"+mapper.selectById(2));

        } catch (Exception e) {

            throw new RuntimeException("插入失败!" + e.getMessage());

        }



        //修改

        try {

            User user = new User();

            user.setId(2);

            user.setUsername("要删除的人");

            user.setPassword("222222");

            user.setEmail("[email protected]");

            user.setAddress("朝阳区");

            user.setPhone("66666666666");

            user.setSex("");

            user.setAge("21");

            mapper.updateById(user);

            System.out.println("修改成功!");

            System.out.println("修改之后内容为:"+mapper.selectById(2));

        } catch (Exception e) {

            throw new RuntimeException("修改失败!"+e.getMessage());

        }



        //删除

        try {

            mapper.deleteById(2);

            System.out.println("删除成功!");

        } catch (Exception e) {

            throw new RuntimeException("删除失败!" + e.getMessage());

        }



    }

}

17、这是执行结果

2018-08-08 17:47:24,432 [main] INFO  com.ucredit.sqlsession.MySqlsession      -已通过代理获取对象!

2018-08-08 17:47:24,494 [main] INFO  com.ucredit.sqlsession.MySqlsession      -我是查询的Sqlsession!

2018-08-08 17:47:24,498 [main] INFO  com.ucredit.sqlsession.MyConfiguration   -加载数据库comfig.xml成功!

2018-08-08 17:47:24,504 [main] INFO  com.ucredit.sqlsession.MyConfiguration   -连接数据库时获取DriverClassName成功!

2018-08-08 17:47:24,726 [main] INFO  com.ucredit.sqlsession.MyConfiguration   -连接数据库时校验url、username、password成功!

2018-08-08 17:47:24,727 [main] INFO  com.ucredit.sqlsession.MyExecutor        -连接数据库成功!

2018-08-08 17:47:24,745 [main] INFO  com.ucredit.sqlsession.MyExecutor        -query,遍历结果集成功!

2018-08-08 17:47:24,745 [main] INFO  com.ucredit.sqlsession.MyExecutor        -PreparedStatement已关闭!

2018-08-08 17:47:24,746 [main] INFO  com.ucredit.sqlsession.MyExecutor        -Connection已关闭!

 

查询成功!

查询内容:1,被查询的人,888888,[email protected],海淀区,88888888888,男,24

 

2018-08-08 17:47:24,747 [main] INFO  com.ucredit.sqlsession.MySqlsession      -我是添加的Sqlsession!

2018-08-08 17:47:24,748 [main] INFO  com.ucredit.sqlsession.MyConfiguration   -加载数据库comfig.xml成功!

2018-08-08 17:47:24,748 [main] INFO  com.ucredit.sqlsession.MyConfiguration   -连接数据库时获取DriverClassName成功!

2018-08-08 17:47:24,754 [main] INFO  com.ucredit.sqlsession.MyConfiguration   -连接数据库时校验url、username、password成功!

2018-08-08 17:47:24,754 [main] INFO  com.ucredit.sqlsession.MyExecutor        -连接数据库成功!

2018-08-08 17:47:24,779 [main] INFO  com.ucredit.sqlsession.MyExecutor        -save,设置参数成功!

2018-08-08 17:47:24,779 [main] INFO  com.ucredit.sqlsession.MyExecutor        -PreparedStatement已关闭!

2018-08-08 17:47:24,779 [main] INFO  com.ucredit.sqlsession.MyExecutor        -Connection已关闭!

 

插入成功!

 

2018-08-08 17:47:24,781 [main] INFO  com.ucredit.sqlsession.MySqlsession      -我是查询的Sqlsession!

2018-08-08 17:47:24,782 [main] INFO  com.ucredit.sqlsession.MyConfiguration   -加载数据库comfig.xml成功!

2018-08-08 17:47:24,782 [main] INFO  com.ucredit.sqlsession.MyConfiguration   -连接数据库时获取DriverClassName成功!

2018-08-08 17:47:24,785 [main] INFO  com.ucredit.sqlsession.MyConfiguration   -连接数据库时校验url、username、password成功!

2018-08-08 17:47:24,786 [main] INFO  com.ucredit.sqlsession.MyExecutor        -连接数据库成功!

2018-08-08 17:47:24,786 [main] INFO  com.ucredit.sqlsession.MyExecutor        -query,遍历结果集成功!

2018-08-08 17:47:24,787 [main] INFO  com.ucredit.sqlsession.MyExecutor        -PreparedStatement已关闭!

2018-08-08 17:47:24,787 [main] INFO  com.ucredit.sqlsession.MyExecutor        -Connection已关闭!

 

插入内容为:2,要修改的人,111111,[email protected],昌平区,99999999999,男,28

 

2018-08-08 17:47:24,789 [main] INFO  com.ucredit.sqlsession.MySqlsession      -我是修改的Sqlsession!

2018-08-08 17:47:24,790 [main] INFO  com.ucredit.sqlsession.MyConfiguration   -加载数据库comfig.xml成功!

2018-08-08 17:47:24,790 [main] INFO  com.ucredit.sqlsession.MyConfiguration   -连接数据库时获取DriverClassName成功!

2018-08-08 17:47:24,794 [main] INFO  com.ucredit.sqlsession.MyConfiguration   -连接数据库时校验url、username、password成功!

2018-08-08 17:47:24,794 [main] INFO  com.ucredit.sqlsession.MyExecutor        -连接数据库成功!

2018-08-08 17:47:24,799 [main] INFO  com.ucredit.sqlsession.MyExecutor        -modify,设置参数成功!

2018-08-08 17:47:24,799 [main] INFO  com.ucredit.sqlsession.MyExecutor        -PreparedStatement已关闭!

2018-08-08 17:47:24,800 [main] INFO  com.ucredit.sqlsession.MyExecutor        -Connection已关闭!

 

修改成功!

 

2018-08-08 17:47:24,802 [main] INFO  com.ucredit.sqlsession.MySqlsession      -我是查询的Sqlsession!

2018-08-08 17:47:24,802 [main] INFO  com.ucredit.sqlsession.MyConfiguration   -加载数据库comfig.xml成功!

2018-08-08 17:47:24,802 [main] INFO  com.ucredit.sqlsession.MyConfiguration   -连接数据库时获取DriverClassName成功!

2018-08-08 17:47:24,805 [main] INFO  com.ucredit.sqlsession.MyConfiguration   -连接数据库时校验url、username、password成功!

2018-08-08 17:47:24,805 [main] INFO  com.ucredit.sqlsession.MyExecutor        -连接数据库成功!

2018-08-08 17:47:24,805 [main] INFO  com.ucredit.sqlsession.MyExecutor        -query,遍历结果集成功!

2018-08-08 17:47:24,806 [main] INFO  com.ucredit.sqlsession.MyExecutor        -PreparedStatement已关闭!

2018-08-08 17:47:24,806 [main] INFO  com.ucredit.sqlsession.MyExecutor        -Connection已关闭!

 

修改之后内容为:2,要删除的人,222222,[email protected],朝阳区,66666666666,女,21

 

2018-08-08 17:47:24,807 [main] INFO  com.ucredit.sqlsession.MySqlsession      -我是删除的Sqlsession!

2018-08-08 17:47:24,808 [main] INFO  com.ucredit.sqlsession.MyConfiguration   -加载数据库comfig.xml成功!

2018-08-08 17:47:24,808 [main] INFO  com.ucredit.sqlsession.MyConfiguration   -连接数据库时获取DriverClassName成功!

2018-08-08 17:47:24,811 [main] INFO  com.ucredit.sqlsession.MyConfiguration   -连接数据库时校验url、username、password成功!

2018-08-08 17:47:24,811 [main] INFO  com.ucredit.sqlsession.MyExecutor        -连接数据库成功!

2018-08-08 17:47:24,817 [main] INFO  com.ucredit.sqlsession.MyExecutor        -remove,设置参数成功!

2018-08-08 17:47:24,817 [main] INFO  com.ucredit.sqlsession.MyExecutor        -PreparedStatement已关闭!

2018-08-08 17:47:24,818 [main] INFO  com.ucredit.sqlsession.MyExecutor        -Connection已关闭!

 

删除成功!

 

Process finished with exit code 0