Mybatis源码解析三(模拟Mybatis)

在阅读一遍源码后, 决定根据自己的理解模拟一下mybatis

先对mybatis的整个流程进行梳理:

分析:

  1. 创建SqlSessionFactory;  mybatis首先会去解析configuration.xml配置文件, 在解析这个配置文件时会对一些配置进行初始化, 例如数据源, mapper文件等; 这里要注意的时在解析mapper文件时, 对根据接口路径生成对应的MapperProxyFactory, 后面会需要通过MapperProxyFactory来创建代理类(采用JDK动态代理)
  2. 创建SqlSession; 将解析封装好的Configuration对象封装到SqlSession实例中, 因为我们是通过SqlSession中的相应select方法去查询数据, 而查询数据需要的sql相关信息,我们解析后封装在configuration中;
  3. 通过sqlSession的selectOne()方法去数据库查询; 相关操作委托给BaseExecutor和CachingExecutor处理器, 首先会调用CachingExecutor处理器进行处理, 在该处理器中会尝试通过二级缓存获取, 获取失败后再通过BaseExecutor处理器去数据库查询, 在去数据库查询之前会尝试通过一级缓存获取
  4. 通过session.getMapper()方法获取指定类型的接口实现类, 该实现类是通过MapperProxyFactory使用JDK动态代理进行实现, 对接口方法中的逻辑进行增强;  相关逻辑主要在MapperProxy中实现, 通过MapperProxyFactory对应的接口路径,以及方法名称可得到statementId, 根据statementId可以获取到MapperStatement, 而sql相关信息维护在MapperStatement中, 在获取MapperStatement后, 就可以进行数据查询, 以及结果封装

配置类:

Configuration类, 用于将解析configuration.xml中的信息进行封装

package core;

import org.apache.log4j.Logger;
import proxy.MapperProxyFactory;
import xml.XMLConfigBuilder;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Configuration {
    private static Logger logger = Logger.getLogger(Configuration.class);
    private Map<String,String> properties;
    private List<Mapper> mappers;
    private Map<String, MapperStatement> mapperStatements;
    private Connection connection;
    private Map<Class, MapperProxyFactory> knownMappers;

    public Map<Class, MapperProxyFactory> getKnownMappers() {
        if (knownMappers == null){
            knownMappers = new HashMap<Class, MapperProxyFactory>();
        }
        return knownMappers;
    }

    public void setKnownMappers(Map<Class, MapperProxyFactory> knownMappers) {
        this.knownMappers = knownMappers;
    }

    public Connection getConnection() {
        return connection;
    }

    public void initConnection(String driver, String url, String user, String pwd) {
        logger.info("初始化连接....");
        if(connection == null){
            try {
                Class.forName(driver);
               connection = DriverManager.getConnection(url,user,pwd);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (SQLException e) {
                e.printStackTrace();
            }

        }
    }

    public void setMapperStatements(Map<String, MapperStatement> mapperStatements) {
        this.mapperStatements = mapperStatements;
    }

    public Map<String, MapperStatement> getMapperStatements() {
        return mapperStatements;
    }

    public void addMapperStatement(String statementId, MapperStatement mapperStatement) {
       if(mapperStatements == null){
           mapperStatements = new HashMap<String, MapperStatement>();
       }
        mapperStatements.put(statementId,mapperStatement);
    }

    public Map<String, String> getProperties() {
        return properties;
    }

    public void setProperties(Map<String, String> properties) {
        this.properties = properties;
    }

    public List<Mapper> getMappers() {
        return mappers;
    }

    public void setMappers(List<Mapper> mappers) {
        this.mappers = mappers;
    }
}

BoundSql类, 将sql语句和参数,参数类型以及结果类型进行整合,便于后面执行sql

package core;

public class BoundSql {
    private String sql;
    private String parameterType;
    private String resultType;
    private MapperStatement mapperStatement;

    public BoundSql(MapperStatement mapperStatement) {
        this.mapperStatement = mapperStatement;
        this.parameterType = mapperStatement.getParameterType();
        this.resultType = mapperStatement.getResultType();
        this.sql = mapperStatement.getSql();
    }

    public MapperStatement getMapperStatement() {
        return mapperStatement;
    }

    public void setMapperStatement(MapperStatement mapperStatement) {
        this.mapperStatement = mapperStatement;
    }

    public String getSql() {
        return sql;
    }

    public void setSql(String sql) {
        this.sql = sql;
    }

    public String getParameterType() {
        return parameterType;
    }

    public void setParameterType(String parameterType) {
        this.parameterType = parameterType;
    }

    public String getResultType() {
        return resultType;
    }

    public void setResultType(String resultType) {
        this.resultType = resultType;
    }
}

Mapper类, 用于封装configuration.xml中<mappers>的子节点数据

package core;

public class Mapper {
    private String value;
    private String type;

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }
}

MapperStatement类, 用于将解析mapper.xml中的信息进行封装 

package core;

public class MapperStatement {
    private String id;
    private String parameterType;
    private String resultType;
    private String command;
    private String sql;

    public MapperStatement(String id, String parameterType, String resultType, String command, String sql) {
        this.id = id;
        this.parameterType = parameterType;
        this.resultType = resultType;
        this.command = command;
        this.sql = sql;
    }

    public String getSql() {
        return sql;
    }

    public void setSql(String sql) {
        this.sql = sql;
    }

    public String getCommand() {
        return command;
    }

    public void setCommand(String command) {
        this.command = command;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getParameterType() {
        return parameterType;
    }

    public void setParameterType(String parameterType) {
        this.parameterType = parameterType;
    }

    public String getResultType() {
        return resultType;
    }

    public void setResultType(String resultType) {
        this.resultType = resultType;
    }
}

工厂类:

SqlSessionFactoryBuilder类,  根据解析后的Configuration实例创建SqlSessionFactory实例

package core;

import xml.XMLConfigBuilder;

public class SqlSessionFactoryBuilder {

    public SqlSessionFactory build(String path){
        XMLConfigBuilder parser = new XMLConfigBuilder(path);
        return build(parser.parse());
    }

    public SqlSessionFactory build(Configuration configuration){
        return new DefaultSqlSessionFactory(configuration);
    }

    public static void main(String[] args) {
        new SqlSessionFactoryBuilder().build("configuration.xml");
    }
}

 DefaultSqlSessionFactory类, 用于创建SqlSession实例

package core;

import executor.BaseExecutor;

public class DefaultSqlSessionFactory implements SqlSessionFactory {
    private Configuration configuration;

    public DefaultSqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
    }

    public SqlSession openSession() {
        return new DefaultSqlSession(configuration);
    }
}

MapperProxyFactory类, 用于创建mapper接口的实现类 

package proxy;

import core.SqlSession;

import java.lang.reflect.Proxy;

public class MapperProxyFactory<T> {
    private Class mapperInterface;

    public MapperProxyFactory(Class mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    protected Object newInstance(MapperProxy mapperProxy) {
        return Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
    }

    public Object newInstance(SqlSession sqlSession) {
        final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface);
        return newInstance(mapperProxy);
    }
}

解析器类

XMLConfigBuilder类, 用于解析xml文件, 将解析到的信息封装到Configuration中

package xml;

import java.io.File;
import java.util.*;

import core.Configuration;
import core.Mapper;
import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

public class XMLConfigBuilder {
    private static Logger logger = Logger.getLogger(XMLConfigBuilder.class);
    private Document document;
    private Configuration configuration;
    
    

    public XMLConfigBuilder(String path) {
        configuration = new Configuration();
        SAXReader saxReader = new SAXReader();
        try {
            if(path == null){
                logger.error("配置资源为空!");
            }
            this.document = saxReader.read(new File(this.getClass().getResource("/").getPath()+"//"+path));
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }

    public Configuration parse(){
        parseConfiguration();
        return configuration;
    }

    private void parseConfiguration(){
        logger.info("正在解析配置文件. . .");
        //解析<properties>节点
        parseProperties(evalNode("environment"));
        //解析<mappers>节点
        parseMappers(evalNode("mappers"));

        XMLConfigParser xmlConfigParser = new XMLConfigParser(configuration);
        xmlConfigParser.parseProperties();
        xmlConfigParser.parseMappers();

    }


    private void parseProperties(Element element){
        //遍历<properties>的子节点进行解析
        Map<String, String> properties = new HashMap<String, String>();
        logger.info("正在解析<environment>标签");
        for (Iterator<Element> childEle = element.elementIterator(); childEle.hasNext(); ) {
            Element propertyEle = childEle.next();
            String name = propertyEle.attribute("name").getValue();
            String value = propertyEle.attribute("value").getValue();
            properties.put(name,value);
        }
       configuration.setProperties(properties);
    }


    private void parseMappers(Element element){
        //遍历<properties>的子节点进行解析
       List<Mapper> mappers = new ArrayList<Mapper>();
        logger.info("正在解析<mappers>标签");
        for (Iterator<Element> childEle = element.elementIterator(); childEle.hasNext(); ) {
            Mapper mapper = new Mapper();
            Element mapperEle = childEle.next();
            if(mapperEle.attributeValue("resource") != null){
                mapper.setType("resource");
                mapper.setValue(mapperEle.attributeValue("resource"));
            }else if(mapperEle.attributeValue("url") != null){
                mapper.setType("url");
                mapper.setValue(mapperEle.attributeValue("url"));
            }else if(mapperEle.attributeValue("class") != null){
                mapper.setType("class");
                mapper.setValue(mapperEle.attributeValue("class"));
            }else if(mapperEle.attributeValue("package") != null){
                mapper.setType("package");
                mapper.setValue(mapperEle.attributeValue("package"));
            }
            mappers.add(mapper);
        }
        configuration.setMappers(mappers);
    }

    private Element evalNode(String eleName){
        Element root = this.document.getRootElement();
        return root.element(eleName);
    }

}

 XMLConfigParser类, 将解析后的数据进行处理, 实例化相关对象

package xml;

import core.Configuration;
import core.Mapper;
import core.MapperStatement;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import proxy.MapperProxyFactory;

import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class XMLConfigParser {
    private Configuration configuration;
    private SAXReader reader = new SAXReader();

    public XMLConfigParser(Configuration configuration) {
        this.configuration = configuration;
    }

    /**
     * 加载连接
     */
    public void parseProperties(){
        Map<String, String> properties = configuration.getProperties();
        String dirver = properties.get("driver");
        String url = properties.get("url");
        String user = properties.get("username");
        String pwd = properties.get("password");
        configuration.initConnection(dirver,url,user,pwd);
    }

    /**
     * 解析mapper文件
     */
    public void parseMappers(){
        List<Mapper> mappers = configuration.getMappers();
        Map<String, MapperStatement> mapperStatements = new HashMap<String, MapperStatement>();
        for (Mapper mapper: mappers ) {
            if(mapper.getType().equals("resource")){
                try {
                    Document document =reader.read(new File(this.getClass().getResource("/").getPath()+"//"+mapper.getValue()));
                    Element root = document.getRootElement();
                    String namespace = root.attributeValue("namespace");
                    //循环解析子标签
                    for (Iterator<Element> childEle = root.elementIterator(); childEle.hasNext(); ) {
                        Element curdEle = childEle.next();
                        String id = curdEle.attributeValue("id");
                        String parameterType = curdEle.attributeValue("parameterType");
                        String resultType = curdEle.attributeValue("resultType");
                        String sql = (String) curdEle.getData();
                        String command =  curdEle.getName();
                        MapperStatement mapperStatement = new MapperStatement(id, parameterType, resultType, command, sql);
                        mapperStatements.put(namespace+"."+id, mapperStatement);
                    }
                    configuration.getKnownMappers().put(Class.forName(namespace),new MapperProxyFactory(Class.forName(namespace)));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        configuration.setMapperStatements(mapperStatements);

    }
}

执行/增强类

BaseExecutor类, 用于执行sql, 以及结果解析封装

package executor;

import core.BoundSql;
import core.Configuration;
import ibatis.entity.User;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

public class BaseExecutor implements Executor {
    private Configuration configuration;

    public BaseExecutor(Configuration configuration) {
        this.configuration = configuration;
    }

    public List<Object> query(String statementId, Object parameter) {
        try {
            BoundSql boundSql = new BoundSql(configuration.getMapperStatements().get(statementId));

            PreparedStatement preparedStatement = configuration.getConnection().prepareStatement(boundSql.getSql());
            if(boundSql.getParameterType().equals("int")){
                preparedStatement.setInt(1,(Integer) parameter);
            }else if(boundSql.getParameterType().equals("String")){
                preparedStatement.setString(1,(String) parameter);
            }
            ResultSet resultSet = preparedStatement.executeQuery();
            String resultType = boundSql.getResultType();
            List<Object> results = new ArrayList();
            while(resultSet.next()){
                Class<?> resultClass = Class.forName(resultType);  //获取结果类型Class对象
                Object resultObj = resultClass.newInstance();  //实例化结果对象
                Field[] declaredFields = resultClass.getDeclaredFields();
                for (Field declaredField: declaredFields) {
                    //如果该属性是String类型
                    if(declaredField.getGenericType().toString().equals("class java.lang.String")){
                        Method method = resultClass.getMethod("set" + toUpperCatip(declaredField.getName()), String.class);
                        method.invoke(resultObj,resultSet.getString(declaredField.getName()));
                    }
                    //如果该属性是Integer类型
                    if(declaredField.getGenericType().toString().equals("class java.lang.Integer")){
                        Method method = resultClass.getMethod("set" + toUpperCatip(declaredField.getName()), Integer.class);
                        method.invoke(resultObj,resultSet.getInt(declaredField.getName()));
                    }

                }
                results.add(resultObj);
            }
            resultSet.close();
            return results;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public String toUpperCatip(String str){
        return str.substring(0, 1).toUpperCase() + str.substring(1);
    }

}

 MapperProxy类,用创建代理, 对接口方法进行实现, 根据接口路径以及方法名获取statementId, 调用sqlsession相关方法进行数据查询

package proxy;
import core.SqlSession;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MapperProxy implements InvocationHandler {
    private SqlSession sqlSession;
    private Class mapperInterface;
    public MapperProxy(SqlSession sqlSession, Class mapperInterface) {
        this.sqlSession = sqlSession;
        this.mapperInterface = mapperInterface;
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return sqlSession.selectOne(mapperInterface.getName()+"."+method.getName(),args[0]);
    }
}

测试类:

package ibatis.test;

import core.SqlSession;
import core.SqlSessionFactory;
import core.SqlSessionFactoryBuilder;
import ibatis.entity.Student;
import ibatis.entity.User;
import ibatis.mapper.StudentMapper;
import ibatis.mapper.UserMapper;

public class MybatisHelloWorld2 {
  public static void main(String[] args) {

      SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build("configuration.xml");
      SqlSession session = sqlSessionFactory.openSession();

      UserMapper userMapper = session.getMapper(UserMapper.class);
      User user = userMapper.getUserById(1);
      System.out.println(user.toString());
      System.out.println("=====================");
      StudentMapper studentMapper = session.getMapper(StudentMapper.class);
      Student student = studentMapper.getStudentrById(10);
      System.out.println(student.toString());

  }
}

控制台输出:

完美运行!

相关文章:

        Mybatis源码解析一(SqlSessionFactory和SqlSession的获取)

        Mybatis源码解析二(请求处理过程解析)

        Mybatis源码解析三(模拟Mybatis)

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值