MyBatis源码手写实现

                             MyBatis源码手写实现

                                                                                                                           作者:田超凡

                                                                                                                        时间:20190824

开始编码前的准备工作:

1.pom.xml加入apache框架基本工具包、dom4j XML文件解析工具、mysql驱动包。

核心配置管理器Configuration主要是基于目前主流的dom4j组件来解析XML配置文件

注意:不要引入多余的MyBatis或Hibernate相关Jar包,因为我们是自己手写实现整个持久层框架执行过程实现ORM                         <!-- TODO TCF 基础工具类 -->

       <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.4</version>
        </dependency>

        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.1</version>
        </dependency>

        <!-- TODO TCF XML文件读取工具dom4j -->
        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>
        
        <!-- TODO TCF MySQL驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.29</version>
        </dependency>

2.src/main/resources目录下新增数据源配置文件dataSource.xml

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

  <!-- TODO TCF 驱动类名称 -->
  <property name="driverClassName">com.mysql.jdbc.Driver</property>
  
  <!-- TODO TCF 数据库连接字符串 -->
  <property name="url">jdbc:mysql://localhost:3306/User?useUnicode=true&amp;characterEncoding=UTF-8</property>
  
  <!-- TODO TCF 用户名 -->
  <property name="userName">root</property>
  
  <!-- TODO TCF 密码 -->
  <property name="password">root</property>
  
</dataSource>

3.生成持久类、映射接口、映射文件

package com.tcf.kid.smart.framework.entity;

/***
 * TODO TCF 用户信息
 * @author 71485
 *
 */
public class UserInfo {

    //TODO TCF 主键ID
    private String id;
    
    //TODO TCF 姓名
    private String name;
    
    //TODO TCF 性别
    private String sex;
    
    //TODO TCF 年龄
    private Integer age;
    
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
}

package com.tcf.kid.smart.framework.dao;

import com.tcf.kid.smart.framework.entity.UserInfo;

/***
 * TODO TCF 用户信息持久层接口
 * @author 71485
 *
 */
public interface UserInfoMapper {

    public UserInfo selectById(String id);
}
 

<?xml version="1.0" encoding="UTF-8"?>
<mapper namespace="com.tcf.kid.smart.framework.dao.UserInfoMapper">
   
   <select id="selectById" parameterType="java.lang.String" resultType="com.tcf.kid.smart.framework.entity.UserInfo">
      SELECT * FROM `UserInfo`
      WHERE `ID`=?
   </select>
   
</mapper>

手写实现步骤:

1.定义模型类封装映射文件的持久化方法以及映射器接口和映射方法之间的关联关系

Function 定义SQL持久化方法相关信息,包括SQL语句类型、SQL语句、方法名、参数类型、返回值类型

package com.tcf.kid.smart.framework.model;

/***
 * TODO TCF 封装映射器接口中的持久化方法信息
 * @author 71485
 *
 */
public class Function {

    //TODO TCF 映射的SQL语句
    private String sql;
    
    //TODO TCF SQL语句类型(SQL前缀)
    private String sqlType;
    
    //TODO TCF 方法名
    private String methodName;
    
    //TODO TCF 返回值类型
    private Object returnType;
    
    //TODO TCF 参数类型
    private String parameterType;
    
    public String getSql() {
        return sql;
    }
    public void setSql(String sql) {
        this.sql = sql;
    }
    public String getSqlType() {
        return sqlType;
    }
    public void setSqlType(String sqlType) {
        this.sqlType = sqlType;
    }
    public String getMethodName() {
        return methodName;
    }
    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }
    public Object getReturnType() {
        return returnType;
    }
    public void setReturnType(Object returnType) {
        this.returnType = returnType;
    }
    public String getParameterType() {
        return parameterType;
    }
    public void setParameterType(String parameterType) {
        this.parameterType = parameterType;
    }
}

MapperBean封装映射器接口相关信息,包括映射器接口名(映射文件命名空间名)、映射器接口中定义的所有持久化方法

package com.tcf.kid.smart.framework.model;

import java.util.List;

/***
 * TODO TCF 映射器接口封装
 * TODO TCF 映射器接口名、接口中的所有方法
 * @author 71485
 *
 */
public class MapperBean {

    //TODO TCF 映射器接口名
    private String interfaceName;
    
    //TODO TCF 映射器接口中的所有方法
    private List<Function> functions;
    
    public String getInterfaceName() {
        return interfaceName;
    }
    public void setInterfaceName(String interfaceName) {
        this.interfaceName = interfaceName;
    }
    public List<Function> getFunctions() {
        return functions;
    }
    public void setFunctions(List<Function> functions) {
        this.functions = functions;
    }
}

2.定义MyBatis核心配置类Configuration,作用是读取数据源配置文件加载数据源信息、读取映射文件加载映射器接口信息并和映射器接口中定义的所有持久化方法建立关联关系

package com.tcf.kid.smart.framework.conf;

import java.io.InputStream;
import java.sql.DriverManager;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import com.tcf.kid.smart.framework.model.Function;
import com.tcf.kid.smart.framework.model.MapperBean;

import java.sql.*;

/***
 * TODO TCF MyBatis核心配置工具类
 * TODO TCF 读取数据源文件加载数据源
 * TODO TCF 根据映射文件名读取映射文件
 * @author 71485
 *
 */
public class Configuration {

    //TODO TCF 获取类加载器
    public ClassLoader getClassLoader()
    {
        return Thread.currentThread().getContextClassLoader();
    }
    
    //TODO TCF 读取数据源文件,注册数据源
    public Connection loadDataSource(String sourceFileName)
    {
        Connection connection=null;
        
        try
        {
            InputStream inputStream=getClassLoader().getResourceAsStream(sourceFileName);
            
            if(inputStream!=null)
            {
                //TODO TCF 读取到的数据源配置信息
                //TODO TCF 驱动类
                String driverClassName="";
                
                //TODO TCF 数据库连接字符串
                String url="";
                
                //TODO TCF 用户名
                String userName="";
                
                //TODO TCF 密码
                String password="";
                
                //TODO TCF 读取xml数据源配置文件
                SAXReader reader=new SAXReader();
                Document document=reader.read(inputStream);
                
                //TODO TCF 根节点
                Element rootElement=document.getRootElement();
                
                if(rootElement!=null)
                {
                    //TODO TCF 根节点名称
                    String rootName=rootElement.getName();
                    
                    if(StringUtils.isNotEmpty(rootName) && rootName.equals("dataSource"))
                    {
                        for(Object node:rootElement.elements())
                        {
                            Element element=(Element)node;
                            
                            if(element!=null)
                            {
                                //TODO TCF 节点Value值
                                String value="";
                                if(element.hasContent())
                                {
                                    value=element.getText();
                                }
                                else
                                {
                                    value=element.attributeValue("value");
                                }
                                
                                //TODO TCF 节点Name属性值
                                String name=element.attributeValue("name");
                                
                                switch(name)
                                {
                                case "driverClassName":
                                    driverClassName=value;
                                    break;
                                case "url":
                                    url=value;
                                    break;
                                case "userName":
                                    userName=value;
                                    break;
                                case "password":
                                    password=value;
                                    break;
                                }
                            }
                        }
                        
                        //TODO TCF 获取数据库连接
                        if(StringUtils.isNotEmpty(driverClassName) && StringUtils.isNotEmpty(userName) && StringUtils.isNotEmpty(password))
                        {
                            connection=DriverManager.getConnection(url,userName,password);
                        }
                    }
                }
            }
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        
        return connection;
    }
    
    //TODO TCF 根据持久类映射文件名读取映射文件中的SQL方法
    public MapperBean loadMapperFile(String mapperFileName)
    {
        MapperBean mapperBean=new MapperBean();
        
        try 
        {
            if(StringUtils.isNotEmpty(mapperFileName))
            {
                //TODO TCF 构建输入流读取xml映射文件
                InputStream inputStream=getClassLoader().getResourceAsStream(mapperFileName);
                
                if(inputStream!=null)
                {
                    SAXReader reader=new SAXReader();
                    Document document=reader.read(inputStream);
                    
                    //TODO TCF 根节点
                    Element rootElement=document.getRootElement();
                    
                    if(rootElement!=null)
                    {
                        //TODO TCF 根节点属性命名空间(映射的持久层接口名)
                        String namespace=rootElement.attributeValue("namespace");
                        mapperBean.setInterfaceName(namespace);
                        
                        //TODO TCF 映射文件中定义的所有持久化方法
                        List<Function> functions=new ArrayList<Function>();
                        
                        for(Object node:rootElement.elements())
                        {
                            Element element=(Element)node;
                            
                            if(element!=null)
                            {
                                Function function=new Function();
                                
                                //TODO TCF SQL前缀(SQL语句类型)
                                String sqlType=element.getName().trim();
                                function.setSqlType(sqlType);
                                
                                //TODO TCF SQL参数类型
                                String parameterType=element.attributeValue("parameterType");
                                function.setParameterType(parameterType);
                                
                                //TODO TCF SQL返回值类型
                                String resultType=element.attributeValue("resultType");
                                
                                //TODO TCF 根据SQL定义的返回值类型获取实例
                                Object instance=Class.forName(resultType);
                                function.setReturnType(instance);
                                
                                //TODO TCF SQL语句
                                String sql=element.getText();
                                function.setSql(sql);
                                
                                //TODO TCF 方法名
                                String methodName=element.attributeValue("id");
                                function.setMethodName(methodName);
                                
                                functions.add(function);
                            }
                        }
                        
                        //TODO TCF 当前映射器接口中定义的所有持久化方法
                        mapperBean.setFunctions(functions);
                    }
                }
            }
        } 
        catch (Exception e) 
        {
            e.printStackTrace();
        }
        
        return mapperBean;
    }
}
 

3.定义SQL语句执行器接口Executor,用来定义需要执行的持久化方法基于JDBC的具体实现

package com.tcf.kid.smart.framework.core;

/***
 * TODO TCF 定义SQL执行策略封装JDBC操作
 * @author 71485
 *
 */
public interface Executor {

    //TODO TCF 根据id查询唯一结果
    public <T> T selectOne(String sql,Object parameter);
}
 

4.定义Executor SQL执行器接口实现类SQLExecutor,基于JDBC实现SQL操作

package com.tcf.kid.smart.framework.core;

import java.sql.*;

import com.tcf.kid.smart.framework.conf.Configuration;
import com.tcf.kid.smart.framework.entity.UserInfo;

/**
 * TODO TCF SQL执行控制器接口实现类,定义具体的JDBC封装操作
 * @author 71485
 *
 */
public class SQLExecutor implements Executor{

    //TODO TCF 核心配置类
    private Configuration configuration=new Configuration();
    
    //TODO TCF 根据id查询唯一返回结果
    @SuppressWarnings("unchecked")
    @Override
    public <T> T selectOne(String sql, Object parameter)
    {
        T result=null;
        
        //TODO TCF 获取连接
        Connection connection=configuration.loadDataSource("dataSource.xml");
        
        //TODO TCF SQL执行实例
        PreparedStatement preparedStatement=null;
        
        //TODO TCF 查询结果集
        ResultSet resultSet=null;
        
        try
        {
            if(connection!=null)
            {
                preparedStatement=connection.prepareStatement(sql);
                preparedStatement.setString(1,parameter.toString());
                resultSet=preparedStatement.executeQuery();
                
                if(resultSet!=null)
                {
                    while(resultSet.next())
                    {
                        //TODO TCF 读取的用户信息
                        String id=resultSet.getString(1);
                        String name=resultSet.getString(2);
                        String sex=resultSet.getString(3);
                        Integer age=resultSet.getInt("age");
                        
                        UserInfo userInfo=new UserInfo();
                        userInfo.setId(id);
                        userInfo.setName(name);
                        userInfo.setSex(sex);
                        userInfo.setAge(age);
                        
                        result=(T)userInfo;
                    }
                }
            }
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        finally
        {
            try
            {
                //TODO TCF 资源释放
                if(resultSet!=null)
                {
                    resultSet.close();
                }
                if(preparedStatement!=null)
                {
                    preparedStatement.close();
                }
                if(connection!=null)
                {
                    connection.close();
                }
            }
            catch(Exception e)
            {
                e.printStackTrace();
            }
        }
        
        return result;
    }

}

5.定义MapperProxyManage类,作用是:作为映射器接口的代理类,实现InvocationInterceptor接口重写invoke方法定义代理方法执行体,读取映射文件并基于反射和MapperBean存放的接口和持久化方法映射关系调用对应的持久化方法进行持久化操作。

package com.tcf.kid.smart.framework.core;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.List;

import org.apache.commons.lang3.StringUtils;

import com.tcf.kid.smart.framework.conf.Configuration;
import com.tcf.kid.smart.framework.model.Function;
import com.tcf.kid.smart.framework.model.MapperBean;

/***
 * TODO TCF 映射器代理目标,读取映射文件,基于反射执行对应SQL语句
 * @author 71485
 *
 */
public class MapperProxyManage implements InvocationHandler{

    //TODO TCF 核心配置信息
    private Configuration configuration;
    
    //TODO TCF 会话
    private SqlSession sqlSession;
    
    //TODO TCF 构造注入
    public MapperProxyManage(Configuration configuration,SqlSession sqlSession)
    {
        this.configuration=configuration;
        this.sqlSession=sqlSession;
    }
    
    //TODO TCF 默认无参构造
    public MapperProxyManage()
    {
        
    }
    
    //TODO TCF 映射器代理目标需要执行的代理方法(织入到映射器接口的对应持久化操作方法执行)
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
    {
        //TODO TCF SQL执行之后的返回值
        Object invokeResult=null;
        
        try
        {
            //TODO TCF 获取映射文件
            MapperBean mapperBean=configuration.loadMapperFile("UserInfoMapper.xml");
            
            //TODO TCF 映射器接口类名
            String interfaceClassName=mapperBean.getInterfaceName();
            
            if(StringUtils.isNotEmpty(interfaceClassName))
            {
                //TODO TCF 匹配需要执行的目标方法所在类类名必须和读取到的映射器命名空间对应
                if(method.getDeclaringClass().getName().equals(interfaceClassName))
                {
                    //TODO TCF 映射器定义的所有持久化方法
                    List<Function> functions=mapperBean.getFunctions();
                    
                    if(functions!=null && functions.size()>0)
                    {
                        for(Function function:functions)
                        {
                            //TODO TCF 方法名
                            String methodName=function.getMethodName();
                            
                            if(StringUtils.isNotEmpty(methodName))
                            {
                                if(methodName.equals(method.getName()))
                                {
                                    invokeResult=sqlSession.selectOne(function.getSql(),String.valueOf(args[0]));    
                                }
                            }
                        }
                    }
                }
            }
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        
        return invokeResult;
    }
}
 

6.定义SqlSession作为会话工厂,建立不同类型映射器的jdk动态代理实例并调用MapperProxyManage中的invoke执行代理的目标方法,定义SQL执行策略,如根据主键id查询返回唯一结果的方法、根据查询条件查询多个匹配记录的方法等,分别调用SQL执行器Executor的接口实现类如SQLExecutor基于JDBC实现持久化操作。

package com.tcf.kid.smart.framework.core;

import java.lang.reflect.Proxy;

import com.tcf.kid.smart.framework.conf.Configuration;

/***
 * TODO TCF SQL会话类,定义执行的持久化方法策略类型:如查询一个结果、查询多个结果等
 * @author 71485
 *
 */
public class SqlSession {

    //TODO TCF MyBatis核心配置信息
    private Configuration configuration=new Configuration();
    
    //TODO TCF MyBatis SQL执行控制器
    private Executor executor=new SQLExecutor();
    
    //TODO TCF 定义需要执行的SQL策略
    //TODO TCF 根据id查询返回唯一结果
    public <T> T selectOne(String sql,Object parameter)
    {
        return executor.selectOne(sql,parameter);
    }
    
    //TODO TCF 定义其他需要执行的SQL策略...
    
    //TODO TCF 基于JDK动态代理创建映射器接口的的代理实例,获取映射器实例
    @SuppressWarnings("unchecked")
    public <T> T getMapper(Class<?> mapperClass)
    {
        return (T) Proxy.newProxyInstance(
                                       mapperClass.getClassLoader(),
                                       new Class[] {mapperClass},
                                       new MapperProxyManage(configuration,this)
                                     );
    }
}

7.编写测试类查看测试结果

package com.tcf.kid.smart.framework.test;

import com.tcf.kid.smart.framework.core.SqlSession;
import com.tcf.kid.smart.framework.dao.UserInfoMapper;
import com.tcf.kid.smart.framework.entity.UserInfo;

public class Test {

    public static void main(String[] args) 
    {
        SqlSession sqlSession=new SqlSession();
        UserInfoMapper userInfoMapper=sqlSession.getMapper(UserInfoMapper.class);
        UserInfo userInfo=userInfoMapper.selectById("20190824001");
        
        System.out.println(userInfo.getId());
        System.out.println(userInfo.getName());
        System.out.println(userInfo.getSex());
        System.err.println(userInfo.getAge());
    }
}
 

运行结果:


 

发布了100 篇原创文章 · 获赞 10 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/qq_30056341/article/details/100048870