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 网络客户端编程端口,模块
    Python反转
    ASP.NET的路由系统
    yield 关键字
    C# Lock关键字
    C#中as和is关键字
    13.4 上下文对象
    请求生命周期
    ASP.NET常用的指令
    ASP.NET Page 指令
  • 原文地址:https://www.cnblogs.com/windpoplar/p/12221443.html
Copyright © 2011-2022 走看看