手写MyBatis框架

Mybaits和数据库交互的方式

  1. 使用传统的 MyBatis 提供的 API
  2. 使用 Mapper 接口

使用 Mapper 接口

MyBatis 将配置文件中的每一个 节点抽象为一个 Mapper 接口
这个接口中声明的方法和 节点中的 节点项对应,即 节点的id值为Mapper 接口中的方法名称,parameterType 值表示Mapper 对应方法的入参类型,而resultMap 值则对应了Mapper 接口表示的返回值类型或者返回结果集的元素类型。

根据MyBatis 的配置规范配置好后,通过SqlSession.getMapper(XXXMapper.class)方法,MyBatis 会根据相应的接口声明的方法信息,通过动态代理机制生成一个Mapper 实例,我们使用Mapper接口的某一个方法时,MyBatis会根据这个方法的方法名和参数类型,确定Statement Id,底层还是通过SqlSession.select(“statementId”,parameterObject);或者SqlSession.update(“statementId”,parameterObject); 等等来实现对数据库的操作,MyBatis引用Mapper 接口这种调用方式,纯粹是为了满足面向接口编程的需要。(其实还有一个原因是在于,面向接口的编程,使得用户在接口上可以使用注解来配置SQL语句,这样就可以脱离XML配置文件,实现“0配置”)。

数据处理层

数据处理层可以说是 MyBatis 的核心,从大的方面上讲,它主要完成两个功能

  1. 通过传入参数构建动态 SQL 语句
  2. SQL 语句的执行以及封装查询结果集成 List

框架支撑层

事务管理机制

事务管理机制对于 ORM 框架而言是不可缺少的一部分,事务管理机制的质量也是考量一个 ORM 框架是否优秀的一个标准。

连接池管理机制

由于创建一个数据库连接所占用的资源比较大,对数据吞吐量大和访问量非常大的应用而言,连接池的设计就显得非常重要。

缓存机制

为了提高数据利用率和减小服务器和数据库的压力,MyBatis 会对于一些查询提供会话级别的数据缓存,会将对某一次查询,放置到SqlSession 中,在允许的时间间隔内,对于完全相同的查询,MyBatis会直接将缓存结果返回给用户,而不用再到数据库中查找。

SQL语句的配置方式

传统的MyBatis 配置SQL语句方式就是使用XML文件进行配置的,但是这种方式不能很好地支持面向接口编程的理念,为了支持面向接口的编程,MyBatis 引入了Mapper接口的概念,面向接口的引入,对使用注解来配置SQL语句成为可能,用户只需要在接口上添加必要的注解即可,不用再去配置XML文件了,但是,目前的MyBatis 只是对注解配置SQL语句提供了有限的支持,某些高级功能还是要依赖XML配置文件配置SQL 语句。

引导层

引导层是配置和启动MyBatis配置信息的方式。MyBatis 提供两种方式来引导MyBatis :基于XML配置文件的方式和基于Java API 的方式。

主要构件及其相互关系

从MyBatis代码实现的角度来看,MyBatis的主要的核心部件有以下几个:

  • SqlSession:作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增删改查功能;
  • Executor:MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护;
  • StatementHandler:封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数、将Statement结果集转换成List集合。
  • ParameterHandler:负责对用户传递的参数转换成JDBC Statement 所需要的参数;
  • ResultSetHandler:负责将JDBC返回的ResultSet结果集对象转换成List类型的集合;
  • TypeHandler:负责java数据类型和jdbc数据类型之间的映射和转换;
  • MappedStatement:MappedStatement维护了一条节点的封装;
  • SqlSource:负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回;
  • BoundSql:表示动态生成的SQL语句以及相应的参数信息;
  • Configuration:MyBatis所有的配置信息都维持在Configuration对象之中;

手写MyBatis框架

定义JDBCUtils

package com.xiaoming.day21.orm.utils;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public final class JDBCUtils {

   private static String connect;
   private static String driverClassName;
   private static String URL;
   private static String username;
   private static String password;
   private static boolean autoCommit;

   /** 声明一个 Connection类型的静态属性,用来缓存一个已经存在的连接对象 */
   private static Connection conn;

   static {
      config();
   }

   /**
    * 开头配置自己的数据库信息
    */
   private static void config() {
      /*
       * 获取驱动
       */
      driverClassName = "com.mysql.jdbc.Driver";
      /*
       * 获取URL
       */
      URL = "jdbc:mysql://47.102.136.26:3306/test?useUnicode=true&characterEncoding=utf8";
      /*
       * 获取用户名
       */
      username = "root";
      /*
       * 获取密码
       */
      password = "123456";
      /*
       * 设置是否自动提交,一般为false不用改
       */
      autoCommit = false;

   }

   /**
    * 载入数据库驱动类
    */
   private static boolean load() {
      try {
         Class.forName(driverClassName);
         return true;
      } catch (ClassNotFoundException e) {
         System.out.println("驱动类 " + driverClassName + " 加载失败");
      }

      return false;
   }

   /**
    * 专门检查缓存的连接是否不可以被使用 ,不可以被使用的话,就返回 true
    */
   private static boolean invalid() {
      if (conn != null) {
         try {
            if (conn.isClosed() || !conn.isValid(3)) {
               return true;
               /*
                * isValid方法是判断Connection是否有效,如果连接尚未关闭并且仍然有效,则返回 true
                */
            }
         } catch (SQLException e) {
            e.printStackTrace();
         }
         /*
          * conn 既不是 null 且也没有关闭 ,且 isValid 返回 true,说明是可以使用的 ( 返回 false )
          */
         return false;
      } else {
         return true;
      }
   }

   /**
    * 建立数据库连接
    */
   public static Connection connect() {
      if (invalid()) { /* invalid为true时,说明连接是失败的 */
         /* 加载驱动 */
         load();
         try {
            /* 建立连接 */
            conn = DriverManager.getConnection(URL, username, password);
         } catch (SQLException e) {
            System.out.println("建立 " + connect + " 数据库连接失败 , " + e.getMessage());
         }
      }
      return conn;
   }

   /**
    * 设置是否自动提交事务
    **/
   public static void transaction() {

      try {
         conn.setAutoCommit(autoCommit);
      } catch (SQLException e) {
         System.out.println("设置事务的提交方式为 : " + (autoCommit ? "自动提交" : "手动提交") + " 时失败: " + e.getMessage());
      }

   }

   /**
    * 创建 Statement 对象
    */
   public static Statement statement() {
      Statement st = null;
      connect();
      /* 如果连接是无效的就重新连接 */
      transaction();
      /* 设置事务的提交方式 */
      try {
         st = conn.createStatement();
      } catch (SQLException e) {
         System.out.println("创建 Statement 对象失败: " + e.getMessage());
      }

      return st;
   }

   /**
    * 根据给定的带参数占位符的SQL语句,创建 PreparedStatement 对象
    * 
    * @param SQL
    *            带参数占位符的SQL语句
    * @return 返回相应的 PreparedStatement 对象
    */
   private static PreparedStatement prepare(String SQL, boolean autoGeneratedKeys) {

      PreparedStatement ps = null;
      connect();
      /* 如果连接是无效的就重新连接 */
      transaction();
      /* 设置事务的提交方式 */
      try {
         if (autoGeneratedKeys) {
            ps = conn.prepareStatement(SQL, Statement.RETURN_GENERATED_KEYS);
         } else {
            ps = conn.prepareStatement(SQL);
         }
      } catch (SQLException e) {
         System.out.println("创建 PreparedStatement 对象失败: " + e.getMessage());
      }

      return ps;

   }

   public static ResultSet query(String SQL, List params) {

      if (SQL == null || SQL.trim().isEmpty() || !SQL.trim().toLowerCase().startsWith("select")) {
         throw new RuntimeException("你的SQL语句为空或不是查询语句");
      }
      ResultSet rs = null;
      if (params.size() > 0) {
         /* 说明 有参数 传入,就需要处理参数 */
         PreparedStatement ps = prepare(SQL, false);
         try {
            for (int i = 0; i < params.size(); i++) {
               ps.setObject(i + 1, params.get(i));
            }
            rs = ps.executeQuery();
         } catch (SQLException e) {
            System.out.println("执行SQL失败: " + e.getMessage());
         }
      } else {
         /* 说明没有传入任何参数 */
         Statement st = statement();
         try {
            rs = st.executeQuery(SQL); // 直接执行不带参数的 SQL 语句
         } catch (SQLException e) {
            System.out.println("执行SQL失败: " + e.getMessage());
         }
      }

      return rs;

   }

   private static Object typeof(Object o) {
      Object r = o;

      if (o instanceof java.sql.Timestamp) {
         return r;
      }
      // 将 java.util.Date 转成 java.sql.Date
      if (o instanceof java.util.Date) {
         java.util.Date d = (java.util.Date) o;
         r = new java.sql.Date(d.getTime());
         return r;
      }
      // 将 Character 或 char 变成 String
      if (o instanceof Character || o.getClass() == char.class) {
         r = String.valueOf(o);
         return r;
      }
      return r;
   }

   public static boolean execute(String SQL, Object... params) {
      if (SQL == null || SQL.trim().isEmpty() || SQL.trim().toLowerCase().startsWith("select")) {
         throw new RuntimeException("你的SQL语句为空或有错");
      }
      boolean r = false;
      /* 表示 执行 DDL 或 DML 操作是否成功的一个标识变量 */

      /* 获得 被执行的 SQL 语句的 前缀 */
      SQL = SQL.trim();
      SQL = SQL.toLowerCase();
      String prefix = SQL.substring(0, SQL.indexOf(" "));
      String operation = ""; // 用来保存操作类型的 变量
      // 根据前缀 确定操作
      switch (prefix) {
      case "create":
         operation = "create table";
         break;
      case "alter":
         operation = "update table";
         break;
      case "drop":
         operation = "drop table";
         break;
      case "truncate":
         operation = "truncate table";
         break;
      case "insert":
         operation = "insert :";
         break;
      case "update":
         operation = "update :";
         break;
      case "delete":
         operation = "delete :";
         break;
      }
      if (params.length > 0) { // 说明有参数
         PreparedStatement ps = prepare(SQL, false);
         Connection c = null;
         try {
            c = ps.getConnection();
         } catch (SQLException e) {
            e.printStackTrace();
         }
         try {
            for (int i = 0; i < params.length; i++) {
               Object p = params[i];
               p = typeof(p);
               ps.setObject(i + 1, p);
            }
            ps.executeUpdate();
            commit(c);
            r = true;
         } catch (SQLException e) {
            System.out.println(operation + " 失败: " + e.getMessage());
            rollback(c);
         }

      } else { // 说明没有参数

         Statement st = statement();
         Connection c = null;
         try {
            c = st.getConnection();
         } catch (SQLException e) {
            e.printStackTrace();
         }
         // 执行 DDL 或 DML 语句,并返回执行结果
         try {
            st.executeUpdate(SQL);
            commit(c); // 提交事务
            r = true;
         } catch (SQLException e) {
            System.out.println(operation + " 失败: " + e.getMessage());
            rollback(c); // 回滚事务
         }
      }
      return r;
   }

   /*
    * 
    * @param SQL 需要执行的 INSERT 语句
    * 
    * @param autoGeneratedKeys 指示是否需要返回由数据库产生的键
    * 
    * @param params 将要执行的SQL语句中包含的参数占位符的 参数值
    * 
    * @return 如果指定 autoGeneratedKeys 为 true 则返回由数据库产生的键; 如果指定 autoGeneratedKeys
    * 为 false 则返回受当前SQL影响的记录数目
    */
   public static int insert(String SQL, boolean autoGeneratedKeys, List params) {
      int var = -1;
      if (SQL == null || SQL.trim().isEmpty()) {
         throw new RuntimeException("你没有指定SQL语句,请检查是否指定了需要执行的SQL语句");
      }
      // 如果不是 insert 开头开头的语句
      if (!SQL.trim().toLowerCase().startsWith("insert")) {
         System.out.println(SQL.toLowerCase());
         throw new RuntimeException("你指定的SQL语句不是插入语句,请检查你的SQL语句");
      }
      // 获得 被执行的 SQL 语句的 前缀 ( 第一个单词 )
      SQL = SQL.trim();
      SQL = SQL.toLowerCase();
      if (params.size() > 0) { // 说明有参数
         PreparedStatement ps = prepare(SQL, autoGeneratedKeys);
         Connection c = null;
         try {
            c = ps.getConnection(); // 从 PreparedStatement 对象中获得 它对应的连接对象
         } catch (SQLException e) {
            e.printStackTrace();
         }
         try {
            for (int i = 0; i < params.size(); i++) {
               Object p = params.get(i);
               p = typeof(p);
               ps.setObject(i + 1, p);
            }
            int count = ps.executeUpdate();
            if (autoGeneratedKeys) { // 如果希望获得数据库产生的键
               ResultSet rs = ps.getGeneratedKeys(); // 获得数据库产生的键集
               if (rs.next()) { // 因为是保存的是单条记录,因此至多返回一个键
                  var = rs.getInt(1); // 获得值并赋值给 var 变量
               }
            } else {
               var = count; // 如果不需要获得,则将受SQL影像的记录数赋值给 var 变量
            }
            commit(c);
         } catch (SQLException e) {
            System.out.println("数据保存失败: " + e.getMessage());
            rollback(c);
         }
      } else { // 说明没有参数
         Statement st = statement();
         Connection c = null;
         try {
            c = st.getConnection(); // 从 Statement 对象中获得 它对应的连接对象
         } catch (SQLException e) {
            e.printStackTrace();
         }
         // 执行 DDL 或 DML 语句,并返回执行结果
         try {
            int count = st.executeUpdate(SQL);
            if (autoGeneratedKeys) { // 如果企望获得数据库产生的键
               ResultSet rs = st.getGeneratedKeys(); // 获得数据库产生的键集
               if (rs.next()) { // 因为是保存的是单条记录,因此至多返回一个键
                  var = rs.getInt(1); // 获得值并赋值给 var 变量
               }
            } else {
               var = count; // 如果不需要获得,则将受SQL影像的记录数赋值给 var 变量
            }
            commit(c); // 提交事务
         } catch (SQLException e) {
            System.out.println("数据保存失败: " + e.getMessage());
            rollback(c); // 回滚事务
         }
      }
      return var;
   }

   /** 提交事务 */
   private static void commit(Connection c) {
      if (c != null && !autoCommit) {
         try {
            c.commit();
         } catch (SQLException e) {
            e.printStackTrace();
         }
      }
   }

   /** 回滚事务 */
   private static void rollback(Connection c) {
      if (c != null && !autoCommit) {
         try {
            c.rollback();
         } catch (SQLException e) {
            e.printStackTrace();
         }
      }
   }

   /**
    * 释放资源
    **/
   public static void release(Object cloaseable) {

      if (cloaseable != null) {

         if (cloaseable instanceof ResultSet) {
            ResultSet rs = (ResultSet) cloaseable;
            try {
               rs.close();
            } catch (SQLException e) {
               e.printStackTrace();
            }
         }

         if (cloaseable instanceof Statement) {
            Statement st = (Statement) cloaseable;
            try {
               st.close();
            } catch (SQLException e) {
               e.printStackTrace();
            }
         }

         if (cloaseable instanceof Connection) {
            Connection c = (Connection) cloaseable;
            try {
               c.close();
            } catch (SQLException e) {
               e.printStackTrace();
            }
         }

      }

   }

}
 
  

定义MyInvocationHandlerMbatis

insert思路
  1. 判断方法上是否存在@ExtInsert(自定义insert注解)
  2. 获取 SQL 语句,即:获取注解 insert 语句
  3. 获取方法上的参数和 SQL 参数进行匹配
  4. 替换参数变为 ?
  5. 调用 JDBC 底层代码执行语句
select思路
  1. 判断方法上是否存在@ExtSelect
  2. 获取 SQL 语句,获取注解 select 语句
  3. 获取方法上的参数和 SQL 参数进行匹配
  4. 替换参数变为 ?
  5. 调用 JDBC 底层代码执行语句
  6. 使用反射机制实例化对象 获取方法返回类型,进行实例化
    1. 使用反射机制获取方法的类型
    2. 判断是否有结果集,如果有结果集,再进行初始化
    3. 使用反射机制,给对象赋值
代码
package com.xiaoming.day21.aop;

import com.xiaoming.day21.orm.annotation.ExtInsert;
import com.xiaoming.day21.orm.annotation.ExtParam;
import com.xiaoming.day21.orm.annotation.ExtSelect;
import com.xiaoming.day21.orm.utils.JDBCUtils;
import com.xiaoming.day21.orm.utils.SQLUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 使用反射动态代理技术 拦截接口方法
 */
public class MyInvocationHandlerMybatis implements InvocationHandler {
    private Object object;

    public MyInvocationHandlerMybatis(Object object){
        this.object = object;
    }

    /**
     * 使用白话文翻译,@ExtInsert 封装过程
     *
     * 1. 判断方法上是否存在@ExtInsert
     * 2. 获取 SQL 语句,获取注解 Insert 语句
     * 3. 获取方法上的参数和 SQL 参数进行匹配
     * 4. 替换参数变为 ?
     * 5. 调用 JDBC 底层代码执行语句
     *
     * @param proxy 生成的代理类对象
     * @param method 被拦截的方法
     * @param args 被拦截的方法参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("使用反射动态代理技术拦截接口方法开始");
        //1. 判断方法上是否存在@ExtInsert
        ExtInsert extInsert = method.getAnnotation(ExtInsert.class);
        if (extInsert != null) {
            return extInsert(method, args, extInsert);
        }

        //查询思路
        //1.判断方法上是否存在@ExtSelect
        ExtSelect extSelect = method.getAnnotation(ExtSelect.class);
        if (extSelect != null){
            return extSelect(method, args, extSelect);
        }

        return null;
    }

    private Object extSelect(Method method, Object[] args, ExtSelect extSelect) throws SQLException, InstantiationException, IllegalAccessException {
        //2. 获取 SQL 语句,获取注解 select 语句
        String selectSql = extSelect.value();
        //3. 获取方法上的参数和 SQL 参数进行匹配
        //定义一个 Map 集合,key:@ExtParam     value:参数值
        ConcurrentHashMap paramMap = getparamsMap(method, args);
        //存放 SQL 的执行参数--参数绑定过程
        List selectParameter = SQLUtils.sqlSelectParameter(selectSql);
        List sqlParams = new ArrayList<>();
        for (String paramName : selectParameter) {
            Object paramValue = paramMap.get(paramName);
            sqlParams.add(paramValue);
        }

        String newSql = SQLUtils.parameQuestion(selectSql, selectParameter);
        System.out.println(newSql);
        //5. 调用 JDBC 底层代码执行语句
        ResultSet resultSet = JDBCUtils.query(newSql, sqlParams);

        //6,使用反射机制实例化对象 获取方法返回类型,进行实例化
        //1.使用反射机制获取方法的类型
        //2.判断是否有结果集,如果有结果集,再进行初始化
        //3.使用反射机制,给对象赋值

        //判断是否存在质
        if (!resultSet.next()){
            return null;
        }
        //下标往上移动一位
        resultSet.previous();
        //1.使用反射机制获取方法的类型
        Class returnType = method.getReturnType();
        Object object = returnType.newInstance();
        while (resultSet.next()){
            Field[] fields = returnType.getDeclaredFields();
            for (Field field : fields) {
                String fieldName = field.getName();
                Object filedValue = resultSet.getObject(fieldName);
                field.setAccessible(true);
                field.set(object, filedValue);
            }
            /*for (String paramName : selectParameter) {
                Object resultValue = resultSet.getObject(paramName);
                //使用 java 反射机制赋值
                Field field = returnType.getDeclaredField(paramName);
                field.setAccessible(true);
                field.set(object, resultValue);
            }*/
        }
        return object;
    }

    private Object extInsert(Method method, Object[] args, ExtInsert extInsert) {
        //2. 获取 SQL 语句,获取注解 Insert 语句
        String insertSql = extInsert.value();
        System.out.println(insertSql);

        //3. 获取方法上的参数和 SQL 参数进行匹配
        //定义一个 Map 集合,key:@ExtParam     value:参数值
        ConcurrentHashMap paramMap = getparamsMap(method, args);
        //存放 SQL 的执行参数--参数绑定过程
        String[] sqlInsertParameter = SQLUtils.sqlInsertParameter(insertSql);
        List sqlParams = sqlParams(paramMap, sqlInsertParameter);
        //4. 替换参数变为 ?
        String newSql = SQLUtils.parameQuestion(insertSql, sqlInsertParameter);
        System.out.println(newSql);
        //5. 调用 JDBC 底层代码执行语句
        int insert = JDBCUtils.insert(newSql, false, sqlParams);
        return 1;
    }

    private List sqlParams(ConcurrentHashMap paramMap, String[] sqlInsertParameter) {
        List sqlParams = new ArrayList<>();
        for (String paramName : sqlInsertParameter) {
            Object paramValue = paramMap.get(paramName);
            sqlParams.add(paramValue);
        }
        return sqlParams;
    }


    private ConcurrentHashMap getparamsMap(Method method, Object[] args) {
        ConcurrentHashMap paramMap = new ConcurrentHashMap<>();
        //获取方法上的参数
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];
            ExtParam extParam = parameter.getAnnotation(ExtParam.class);
            if (extParam!=null){
                //参数名称
                String paramName = extParam.value();
                Object paramValue = args[i];
                paramMap.put(paramName, paramValue);
            }
        }
        return paramMap;
    }
}
 
  

定义SqlSession

/**
 * 获取SqlSession对象
 */
public class SqlSession {
    public static   T getMapper(Class clazz){
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new MyInvocationHandlerMybatis(clazz));
    }
}

定义SQLUtils

/**
 * SQL拼接
*/ public class SQLUtils { /** * * 获取Insert语句后面values 参数信息
* * @param sql * @return */ public static String[] sqlInsertParameter(String sql) { int startIndex = sql.indexOf("values"); int endIndex = sql.length(); String substring = sql.substring(startIndex + 6, endIndex).replace("(", "").replace(")", "").replace("#{", "") .replace("}", ""); String[] split = substring.split(","); return split; } /** * * 获取select 后面where语句 * * @param sql * @return */ public static List sqlSelectParameter(String sql) { int startIndex = sql.indexOf("where"); int endIndex = sql.length(); String substring = sql.substring(startIndex + 5, endIndex); String[] split = substring.split("and"); List listArr = new ArrayList<>(); for (String string : split) { String[] sp2 = string.split("="); listArr.add(sp2[0].trim()); } return listArr; } /** * 将SQL语句的参数替换变为?
* * @param sql * @param parameterName * @return */ public static String parameQuestion(String sql, String[] parameterName) { for (int i = 0; i < parameterName.length; i++) { String string = parameterName[i]; sql = sql.replace("#{" + string + "}", "?"); } return sql; } public static String parameQuestion(String sql, List parameterName) { for (int i = 0; i < parameterName.size(); i++) { String string = parameterName.get(i); sql = sql.replace("#{" + string + "}", "?"); } return sql; } public static void main(String[] args) { // String[] sqlParameter = sqlInsertParameter(sql); // for (String string : sqlParameter) { // System.out.println(string); // } } }

运行

UserMapper userMapper = SqlSession.getMapper(UserMapper.class);
User result = userMapper.selectUser("xiaoming", 18);
System.out.println(result);

你可能感兴趣的:(源码分析)