zoukankan      html  css  js  c++  java
  • 手写Mybatis框架

    一、简介

    Mybatis是一款优秀的持久层框架,在互联网公司的项目开发中使用非常广泛。通过对MyBatis源码的学习,可以更好的了解Mybatis的使用,同时也可以借鉴其中优秀的编程方式和设计模式。学习是一个抽象归纳然后再运用的过程,通过对Mybatis源码核心部分的抽象,手写一个Mybatis框架,来更好的学习Mybatis。

    二、处理流程

    Mybatis核心流程分为三步:

    1.读取XML配置文件和注解中的配置信息,创建配置对象,并完成各个模块的初始化工作;

    2.封装iBatis的编程模式,使用mapper接口开发的初始化工作;

    3.通过SqlSession完成SQL的解析,参数的映射、SQL的执行、结果的反射解析过程;

    三、手写Mybatis具体实现

    1. 实体相关类

    Dept.java:

    package com.taoxi.mybatis.mysimple.entity;
    
    public class Dept {
        private Integer deptNo;
        private String dName;
        private String loc;
    
        public Integer getDeptNo() {
            return deptNo;
        }
    
        public void setDeptNo(Integer deptNo) {
            this.deptNo = deptNo;
        }
    
        public String getDname() {
            return dName;
        }
    
        public void setDname(String dname) {
            this.dName = dname;
        }
    
        public String getLoc() {
            return loc;
        }
    
        public void setLoc(String loc) {
            this.loc = loc;
        }
    
    }

    DeptMapper.java:

    package com.taoxi.mybatis.mysimple.mapper;
    
    import com.taoxi.mybatis.mysimple.entity.Dept;
    
    public interface DeptMapper {
    
        Dept deptFindById(Integer deptId);
    
    }

    DeptMapper.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper
      PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
      "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.taoxi.mybatis.mysimple.mapper.DeptMapper">
     
     <select id="deptFindById" resultType="com.taoxi.mybatis.mysimple.entity.Dept">
            select * from dept where deptno= ?
     </select>
       
    </mapper>

    2.配置相关类

    Configuration.java:

    package com.taoxi.mybatis.mysimple.config;
    
    import java.util.HashMap;
    import java.util.Map;
    
    public class Configuration {
    
        private String jdbcDriver;
        private String jdbcUrl;
        private String jdbcUsername;
        private String jdbcPassword;
    
        private Map<String, MappedStatement> mappedStatments = new HashMap<String, MappedStatement>();
    
        public String getJdbcDriver() {
            return jdbcDriver;
        }
    
        public void setJdbcDriver(String jdbcDriver) {
            this.jdbcDriver = jdbcDriver;
        }
    
        public String getJdbcUrl() {
            return jdbcUrl;
        }
    
        public void setJdbcUrl(String jdbcUrl) {
            this.jdbcUrl = jdbcUrl;
        }
    
        public String getJdbcUsername() {
            return jdbcUsername;
        }
    
        public void setJdbcUsername(String jdbcUsername) {
            this.jdbcUsername = jdbcUsername;
        }
    
        public String getJdbcPassword() {
            return jdbcPassword;
        }
    
        public void setJdbcPassword(String jdbcPassword) {
            this.jdbcPassword = jdbcPassword;
        }
    
        public Map<String, MappedStatement> getMappedStatments() {
            return mappedStatments;
        }
    
        public void setMappedStatments(Map<String, MappedStatement> mappedStatments) {
            this.mappedStatments = mappedStatments;
        }
    }

    MappedStatemant.java:

    package com.taoxi.mybatis.mysimple.config;
    
    public class MappedStatement {
    
        private String namespace;
        private String sourceId;
        private String resultType;
        private String sql;
    
        public String getNamespace() {
            return namespace;
        }
    
        public void setNamespace(String namespace) {
            this.namespace = namespace;
        }
    
        public String getSourceId() {
            return sourceId;
        }
    
        public void setSourceId(String sourceId) {
            this.sourceId = sourceId;
        }
    
        public String getResultType() {
            return resultType;
        }
    
        public void setResultType(String resultType) {
            this.resultType = resultType;
        }
    
        public String getSql() {
            return sql;
        }
    
        public void setSql(String sql) {
            this.sql = sql;
        }
    }

    3.SqlSessionFactory工厂类

    SqlSessionFactory.java:

    package com.taoxi.mybatis.mysimple.session;
    
    import com.taoxi.mybatis.mysimple.config.Configuration;
    import com.taoxi.mybatis.mysimple.config.MappedStatement;
    import org.dom4j.Document;
    import org.dom4j.DocumentException;
    import org.dom4j.Element;
    import org.dom4j.io.SAXReader;
    
    import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.URL;
    import java.util.List;
    import java.util.Properties;
    
    public class SqlSessionFactory {
    
        private final Configuration configuration = new Configuration();
    
        public SqlSessionFactory() {
            loadDbInfo();
            loadMappersInfo();
        }
    
        public SqlSession openSession() {
            return new DefaultSqlSession(configuration);
        }
    
        //记录mapper xml文件存放的位置
        public static final String MAPPER_CONFIG_LOCATION = "mappers";
        //记录数据库连接信息文件存放位置
        public static final String DB_CONFIG_FILE = "db.properties";
    
        //加载数据库配置信息
        private void loadDbInfo() {
            //加载数据库信息配置文件
            InputStream dbIn = SqlSessionFactory.class.getClassLoader().getResourceAsStream(DB_CONFIG_FILE);
            Properties properties = new Properties();
            try {
                properties.load(dbIn);
            } catch (IOException e) {
                throw new RuntimeException("load db info error:", e);
            }
            //将数据库配置信息写入configuration对象
            configuration.setJdbcDriver(properties.get("jdbc.driver").toString());
            configuration.setJdbcPassword(properties.get("jdbc.password").toString());
            configuration.setJdbcUrl(properties.get("jdbc.url").toString());
            configuration.setJdbcUsername(properties.get("jdbc.username").toString());
        }
    
    
        //加载指定文件夹下的所有mapper.xml
        private void loadMappersInfo() {
            URL resources = null;
            resources = SqlSessionFactory.class.getClassLoader().getResource(MAPPER_CONFIG_LOCATION);
            File mappers = new File(resources.getFile());
            if (mappers.isDirectory()){
                File[] listFiles = mappers.listFiles();
                //遍历文件夹下所有的mapper.xml,解析信息后,注册到configuration对象中
                for (File file :
                        listFiles) {
                    loadMapperInfo(file);
                }
            }
    
        }
    
        //加载指定的mapper.xml文件
        private void loadMapperInfo(File file) {
            //创建saxReader对象
            SAXReader reader = new SAXReader();
            //通过read方法读取一个文件,转换成Document对象
            Document document = null;
            try {
                document = reader.read(file);
            } catch (DocumentException e) {
                throw new RuntimeException("load mapper info error:", e);
            }
            //获取根节点元素对象<mapper>
            Element root = document.getRootElement();
            //获取命名空间
            String namespace = root.attribute("namespace").getData().toString();
            //获取select子节点列表
            List<Element> selects = root.elements("select");
            //遍历select节点,将信息记录到MappedStatement对象,并登记到configuration对象中
            for (Element element :
                    selects) {
                MappedStatement mappedStatement = new MappedStatement();//实例化mappedStatement
                String id = element.attribute("id").getData().toString();//读取id属性
                String resultType = element.attribute("resultType").getData().toString();//读取resultType属性
                String sql = element.getData().toString();//读取SQL语句信息
                String sourceId = namespace + "." + id;
                //给mappedStatement属性赋值
                mappedStatement.setSourceId(sourceId);
                mappedStatement.setResultType(resultType);
                mappedStatement.setSql(sql);
                mappedStatement.setNamespace(namespace);
                //注册到configuration对象中
                configuration.getMappedStatments().put(sourceId, mappedStatement);
            }
    
        }
    
    }

    4.SqlSession相关类

    SqlSession.java接口:

    package com.taoxi.mybatis.mysimple.session;
    
    import java.util.List;
    
    public interface SqlSession {
    
        public <T> T selectOne(String statement, Object parameter);
    
        public <E> List<E> selectList(String statement, Object parameter);
    
        public <T>  T getMapper(Class<T> type);
    
    }

    DefaultSqlSession.java默认实现类:

    package com.taoxi.mybatis.mysimple.session;
    
    import com.taoxi.mybatis.mysimple.binding.MapperProxy;
    import com.taoxi.mybatis.mysimple.config.Configuration;
    import com.taoxi.mybatis.mysimple.config.MappedStatement;
    import com.taoxi.mybatis.mysimple.executor.DefaultExecutor;
    import com.taoxi.mybatis.mysimple.executor.Executor;
    
    import java.lang.reflect.Proxy;
    import java.util.List;
    
    public class DefaultSqlSession implements SqlSession{
    
        private final Configuration configuration;
    
        private Executor executor;
    
        public DefaultSqlSession(Configuration configuration) {
            super();
            this.configuration = configuration;
            executor = new DefaultExecutor(configuration);
        }
    
        public <T> T selectOne(String statement, Object parameter) {
            //执行Sql语句获取查询结果
            List<Object> selectList = this.selectList(statement, parameter);
            //查询结果为空返回null
            if (selectList == null || selectList.size()==0) {
                return null;
            }
            if (selectList.size()==1) {
                return (T)selectList.get(0);
            }else{
                throw new RuntimeException("Too Many Results!");
            }
    
        }
    
        public <E> List<E> selectList(String statement, Object parameter) {
            try {
                MappedStatement ms = configuration.getMappedStatments().get(statement);
                return executor.query(ms, parameter);
            } catch (Exception e) {
                throw new RuntimeException("select list error:", e);
            }
    
        }
    
        public <T> T getMapper(Class<T> type) {
            MapperProxy mapperProxy = new MapperProxy(this);
    
            return (T) Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type}, mapperProxy);
        }
    }

    MapperProxy.java代理处理类:

    package com.taoxi.mybatis.mysimple.binding;
    
    import com.taoxi.mybatis.mysimple.session.SqlSession;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.util.Collection;
    
    public class MapperProxy implements InvocationHandler {
    
        private SqlSession session;
    
        public MapperProxy(SqlSession session) {
            super();
            this.session = session;
        }
    
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (Collection.class.isAssignableFrom(method.getReturnType())){
                return session.selectList(method.getDeclaringClass().getName()+"."+method.getName(),
                        args==null?null:args[0]);
            }else {
                return session.selectOne(method.getDeclaringClass().getName()+"."+method.getName(),
                        args==null?null:args[0]);
            }
        }
    }

    5.Executor执行器相关类

    Executor.java执行器接口:

    package com.taoxi.mybatis.mysimple.executor;
    
    import com.taoxi.mybatis.mysimple.config.MappedStatement;
    
    import java.util.List;
    
    public interface Executor {
        public <E> List<E> query(MappedStatement ms, Object parameter) throws Exception;
    }

    DefaultExecutor.java执行器默认实现类:

    package com.taoxi.mybatis.mysimple.executor;
    
    import com.taoxi.mybatis.mysimple.config.Configuration;
    import com.taoxi.mybatis.mysimple.config.MappedStatement;
    import com.taoxi.mybatis.mysimple.util.ReflectionUtil;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.sql.*;
    import java.util.ArrayList;
    import java.util.List;
    
    public class DefaultExecutor implements Executor{
    
        private static Logger logger = LoggerFactory.getLogger(DefaultExecutor.class);
    
        private final Configuration configuration;
    
        public DefaultExecutor(Configuration configuration) {
            super();
            this.configuration = configuration;
        }
    
        public <E> List<E> query(MappedStatement ms, Object parameter) throws SQLException{
            ArrayList<E> ret = new ArrayList<E>();//定义返回结果集
            try {
                Class.forName(configuration.getJdbcDriver());//加载驱动程序
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            Connection connection = null;
            PreparedStatement preparedStatement = null;
            ResultSet resultSet = null;
    
            try {
                //获取连接,从Configuration中获取数据库信息
                connection = DriverManager.getConnection(configuration.getJdbcUrl(), configuration.getJdbcUsername(),
                        configuration.getJdbcPassword());
                //创建prepareStatement,从MappedStatement中获取sql语句
                preparedStatement = connection.prepareStatement(ms.getSql());
                //处理sql语句中的占位符
                parameterize(preparedStatement, parameter);
                //执行查询操作获取resultSet
                resultSet = preparedStatement.executeQuery();
                //将结果集通过反射技术,填充到list中
                handlerResultSet(resultSet, ret, ms.getResultType());
    
            } catch (SQLException e) {
                throw e;
            }finally {
                try {
                    resultSet.close();
                    preparedStatement.close();
                    connection.close();
                } catch (SQLException e) {
                    throw e;
                }
            }
    
            return ret;
        }
    
    
        //对prepareStatement中的占位符进行处理
        private void parameterize(PreparedStatement preparedStatement, Object parameter) throws SQLException {
            if (parameter instanceof Integer) {
                preparedStatement.setInt(1,((Integer) parameter).intValue());
            } else if (parameter instanceof Long) {
                preparedStatement.setLong(1, ((Long) parameter).longValue());
            } else if (parameter instanceof String) {
                preparedStatement.setString(1, (String)parameter);
            }
        }
    
        //读取resultset中的数据,并转换成目标对象
        private <E> void handlerResultSet(ResultSet resultSet, List<E> ret, String className) {
            Class<E> clazz = null;
            try {
                //通过反射获取类对象
                clazz = (Class<E>)Class.forName(className);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            try {
                while (resultSet.next()) {
                    Object entity = clazz.newInstance();
                    //使用反射工具将resultSet中的数据填充到entity中
                    ReflectionUtil.setPropToBeanFromResult(entity, resultSet);
                    //对象加入返回集合中
                    ret.add((E)entity);
    
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
    
    }

    ReflectionUti.java反射工具类:

    package com.taoxi.mybatis.mysimple.util;
    
    
    import java.lang.reflect.Field;
    import java.sql.ResultSet;
    
    public class ReflectionUtil {
    
        public static void setPropToBean(Object bean, String propName, Object value) {
            Field field;
            try {
                field = field = bean.getClass().getDeclaredField(propName);
                field.setAccessible(true);
                field.set(bean, value);
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (SecurityException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            }
    
        }
    
        public static void setPropToBeanFromResult(Object entity, ResultSet resultSet) throws Exception {
            Field[] declaredFields = entity.getClass().getDeclaredFields();
            for (int i = 0;i<declaredFields.length;i++) {
                if (declaredFields[i].getType().getSimpleName().equals("String")){
                    setPropToBean(entity, declaredFields[i].getName(), resultSet.getString(declaredFields[i].getName()));
                } else if (declaredFields[i].getType().getSimpleName().equals("Integer")){
                    setPropToBean(entity, declaredFields[i].getName(), resultSet.getInt(declaredFields[i].getName()));
                } else if (declaredFields[i].getType().getSimpleName().equals("Long")){
                    setPropToBean(entity, declaredFields[i].getName(), resultSet.getLong(declaredFields[i].getName()));
                }
            }
        }
    
    
        public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
            Class<?> clazz = Class.forName("com.taoxi.mybatis.mysimple.entity.Dept");
            Object dept = clazz.newInstance();
            ReflectionUtil.setPropToBean(dept, "dName", "zhongguo");
            System.out.println(dept);
        }
    
    }

    源码实现工程地址:https://github.com/taoxibj/study/tree/master/mysimple

    DeptMapper
  • 相关阅读:
    疫情环境下的网络学习笔记 python 5.8 数据库入门终章
    疫情环境下的网络学习笔记 python 5.7 navicat数据库,例题,sql注入
    疫情环境下的网络学习笔记 python 5.6 暂时看看
    疫情环境下的网络学习笔记 python 5.5 MYSql 表关系,外键
    疫情环境下的网络学习笔记 python 5.4 数据库基础
    疫情环境下的网络学习笔记 python 4.30 初识数据库
    疫情环境下的网络学习笔记 python 4.29 网络小项目
    XJOI 夏令营501-511测试11 游戏
    XJOI 夏令营501-511测试11 统计方案
    CF1197D Yet Another Subarray Problem
  • 原文地址:https://www.cnblogs.com/windpoplar/p/12221443.html
Copyright © 2011-2022 走看看