• 简单版Mybatis框架的实现


    一、基本概述

    最近心血来潮,想加深学习框架实现的思想,所以抽空研究研究Mybatis,Mybatis的核心工作流程实现其实并不是很难。核心都是对反射,动态代理、设计模式以及JDBC的运用。

    当然想要很完善的实现,包括各种特性、各种严谨的代码规范,那可能需要花费更多时间去研究源码。这里更多的是通过手写简单版本的mybatis加深理解mybatis的核心工作流程,核心原理。

    二、Mybatis的核心架构流程

    上图是mybatis的核心工作流程,通过理解其工作流程后,对于自己去实现就会有了一个很好的思路。

    mybatis的核心工作流程就是(分为mybatis读取配置的初始阶段和执行jdbc操作的运行阶段):

    初始阶段

    1、通过SqlSessionFactoryBuilder加载配置,包括了核心配置文件,里面保存与mybatis运行环境有关的全局配置(JDBC配置,Mapper文件位置等)

    和Mapper映射文件,里面都是CRUD标签的内容。加载的时候通过相应的解析器,最终将所有的内容以面向对象的思想封装成对象,

    最终保存到Configuration对象中。最后生成SqlSessionFactory对象,session工厂对象。

    2、另外对于<select/insert/update/delete>的标签解析都会生成MappedStatement对象,里面都封装每条SQL的参数类型,结果类型,statement类型,sql文本等

    执行阶段

    1、每次进行JDBC操作的时候,会通过SqlsessionFactory对象生成一个SqlSession接口的对象,作为session访问处理。真正的CRUD操作,

    它会交给Executor接口的实现类去真正实现。

    2、Executor也会将相应的操作交给具体handler去处理,包括statementhandler、parameterhandler、resultsethandler处理。

    当然里面无非就是对于jdbc的statement处理输入参数的映射,最后对结果集的映射封装结果而已。

    所以在这里也看得出来mybatis算是半ORM框架,具体体现在输入参数和输出结果集的映射处理。

    三、实现细节

    上面的是具体实现的工程目录,里面实现也是基于执行流程的思路去实现的。

    3.1、pom.xml依赖

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <groupId>com.hjj.mybatis</groupId>
      <artifactId>simplemybatis</artifactId>
      <version>0.0.1-SNAPSHOT</version>
      
      <dependencies>
          <dependency>
              <groupId>dom4j</groupId>
              <artifactId>dom4j</artifactId>
              <version>1.6</version>
          </dependency>
          <dependency>
              <groupId>commons-dbcp</groupId>
              <artifactId>commons-dbcp</artifactId>
              <version>1.4</version>
          </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.20</version>
        </dependency>
      </dependencies>
      
      <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.8.0</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                        <encoding>UTF-8</encoding>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>

    3.2、SqlSessionFactoryBuilder,主要就是传入主配置文件的流对象,通过相应解析器解析得到Configuration对象,封装所有的配置文件信息

    public class SqlSessionFactoryBuilder {
        
        public SqlSessionFactory build(InputStream inputStream) {
            //解析配置,封装Configuration对象,
            XmlConfigBuilder parser = new XmlConfigBuilder(inputStream);
            return build(parser.parse());
        }
        
        
        public SqlSessionFactory build(Reader reader) {
            //TO DO
            return null;
        }
        
        public SqlSessionFactory build(Configuration configuration) {
            return new DefaultSqlSessionFactory(configuration);
        }
        
        
    }

    3.3、DefaultSqlSessionFactory,主要就是通过它获取Session会话对象

    public class DefaultSqlSessionFactory implements SqlSessionFactory {
        
        private Configuration configuration;
        
        public DefaultSqlSessionFactory(Configuration configuration) {
            super();
            this.configuration = configuration;
        }
    
        @Override
        public SqlSession openSession() {
            // TODO Auto-generated method stub
            return new DefaultSqlSession(configuration, null);
        }
    
    }

    3.4、DefaultSqlSession,主要是封装JDBC操作,内部通过Executor接口的具体对象真正执行

    public class DefaultSqlSession implements SqlSession {
        
        private Configuration configuration;
        private Executor executor;
        
        public DefaultSqlSession(Configuration configuration, String executorType) {
            this.configuration = configuration;
            
            if(executorType == null) {
                this.executor = new SimpleExecutor();            
            }else {
                ////
            }
        }
    
        @Override
        public <T> T selectOne(String statementId, Object params) throws SQLException {
            // TODO Auto-generated method stub
            List<T> list = this.selectList(statementId, params);
            
            if (list.size() == 1) {
                return list.get(0);
            } else if (list.size() > 1) {
                throw new RuntimeException(
                        "通过selectOne()方法,期待返回一个结果, 但实际返回结果数为: " + list.size());
            } else {
                return null;
            }
        }
    
        @Override
        public <T> List<T> selectList(String statementId, Object params) throws SQLException {
            // TODO Auto-generated method stub
            MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
            return executor.selectList(configuration, mappedStatement, params);
        }
    
    }

    3.5、XmlConfigBuilder,主要是用于解析mybatis的主配置文件,在这里使用dom4j对xml文件进行解析。以及主要解析封装数据源对象

    public class XmlConfigBuilder {
        
        private InputStream inputStream;
        private Configuration configuration;
    
        public XmlConfigBuilder(InputStream inputStream) {
            super();
            this.inputStream = inputStream;
            this.configuration = new Configuration();
        }
        
        public Configuration parse(InputStream inputStream) {
            Document document = DocumentReader.createDocument(inputStream);
            parseConfiguration(document.getRootElement());
            
            return configuration;
        }
    
        public Configuration parse() {
            return parse(inputStream);
        }
        
        private void parseConfiguration(Element element) {
            // TODO Auto-generated method stub
            //解析<environments>
            parseEnvironments(element.element("environments"));
            //解析<mappers>
            parseMappers(element.element("mappers"));
        }
    
        private void parseEnvironments(Element element) {
            // TODO Auto-generated method stub
            String attr = element.attributeValue("default");
            List<Element> elements = element.elements("environment");
            
            if(attr != null) {
                for (Element ele : elements) {
                    String eleId = ele.attributeValue("id");
                    if(eleId != null && eleId.equals(attr)) {
                        parseDataSource(ele.element("dataSource"));
                        
                        break;
                    }
                }
            }else {
                throw new RuntimeException("environments标签的default属性不能为空");
            }
    
            
        }
    
        private void parseDataSource(Element element) {
            // TODO Auto-generated method stub
            String type = element.attributeValue("type");
            if(type == null || type.trim().equals("")) 
                type = "DBCP";
            
            Properties prop = new Properties();
            /*    for (Element ele : element.elements("property")) {
                
            }*/
            List<Element> elements = element.elements("property");
            for (Element ele : elements) {
                String name = ele.attributeValue("name");
                String value = ele.attributeValue("value");
                prop.setProperty(name, value);
            }
            
            //封装数据源对象
            BasicDataSource dataSource = null;
            if (type.equals("DBCP")) {
                dataSource = new BasicDataSource();
                dataSource.setDriverClassName(prop.getProperty("driver"));
                dataSource.setUrl(prop.getProperty("url"));
                dataSource.setUsername(prop.getProperty("username"));
                dataSource.setPassword(prop.getProperty("password"));
            }
            
            configuration.setDataSource(dataSource);
        }
        
    
        private void parseMappers(Element element) {
            // TODO Auto-generated method stub
            List<Element> elements = element.elements("mapper");
            for(Element ele : elements) {
                parseMapper(ele);
            }
            
        }
    
        private void parseMapper(Element element) {
            String resource = element.attributeValue("resource");
            InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(resource);
            XmlMapperBuilder parser = new XmlMapperBuilder(inputStream, configuration);
            parser.parse();
        }
    }

    3.6、XmlMapperBuilder,主要是解析所有的Mapper配置文件,封装所以SQL语句标签到MappedStatement对象中

    public class XmlMapperBuilder {
        private InputStream inputStream;
        private Configuration configuration;
        private String namespace;
    
        public XmlMapperBuilder(InputStream inputStream, Configuration configuration) {
            // TODO Auto-generated constructor stub
            this.inputStream = inputStream;
            this.configuration = configuration;
        }
    
        public void parse() {
            // TODO Auto-generated method stub
            Document document = DocumentReader.createDocument(inputStream);
            Element element = document.getRootElement();
            this.namespace = element.attributeValue("namespace");
            
            if(this.namespace == null || this.namespace.equals("")) {
                throw new RuntimeException("Mapper的namespace值不能为空");
            }
            
            //解析所有select标签
            parseSelectStatements(element.elements("select"));
            //解析所有insert标签
            parseInsertStatements(element.elements("insert"));
        }
    
        private void parseSelectStatements(List<Element> elements) {
            
            for (Element element : elements) {
                parseSelectStatement(element);
            }
            
        }
        
        private void parseSelectStatement(Element element) {
            // TODO Auto-generated method stub
            String id = this.namespace + "." + element.attributeValue("id");
            Class<?> parameterTypeClass = ClassUtil.getClazz(element.attributeValue("parameterType"));
            Class<?> resultTypeClass = ClassUtil.getClazz(element.attributeValue("resultType"));
            String statementType = element.attributeValue("statementType");
            SqlSource sqlSource = new SqlSource(element.getTextTrim());
            
            MappedStatementBuilder msBuilder = new MappedStatementBuilder();
            MappedStatement mappedStatement = msBuilder.id(id)
                                                       .parameterTypeClass(parameterTypeClass)
                                                       .resultTypeClass(resultTypeClass)
                                                       .statementType(statementType)
                                                       .sqlSource(sqlSource)
                                                       .builder();
            
            configuration.addMappedStatement(id, mappedStatement);
            
        }
    
        private void parseInsertStatements(List<Element> elements) {
            // TODO Auto-generated method stub
        }
    
        
        
    
    }

    3.7、Configuration,主要是所有配置信息最终保存到这个类

    public class Configuration {
        
        private BasicDataSource dataSource;
        
        private Map<String, MappedStatement> mappedStatementMap = new HashMap<>(); 
    
        public BasicDataSource getDataSource() {
            return dataSource;
        }
    
        public void setDataSource(BasicDataSource dataSource) {
            this.dataSource = dataSource;
        }
    
        public Map<String, MappedStatement> getMappedStatementMap() {
            return mappedStatementMap;
        }
    
        public void addMappedStatement(String statementId, MappedStatement mappedStatement) {
            this.mappedStatementMap.put(statementId, mappedStatement);
        }
        
    }

    3.8、MappedStatement,主要封装所有解析后SQL的CRUD标签内容

    public class MappedStatement {
        
        private String id;
        private Class<?> parameterTypeClass;
        private Class<?> resultTypeClass;
        private String statementType;
        private SqlSource sqlSource;
        
        public MappedStatement() {
            
        }
        public MappedStatement(String id, Class<?> parameterTypeClass, Class<?> resultTypeClass, String statementType,
                SqlSource sqlSource) {
            super();
            this.id = id;
            this.parameterTypeClass = parameterTypeClass;
            this.resultTypeClass = resultTypeClass;
            this.statementType = statementType;
            this.sqlSource = sqlSource;
        }
        public String getId() {
            return id;
        }
        public void setId(String id) {
            this.id = id;
        }
        public Class<?> getParameterTypeClass() {
            return parameterTypeClass;
        }
        public void setParameterTypeClass(Class<?> parameterTypeClass) {
            this.parameterTypeClass = parameterTypeClass;
        }
        public Class<?> getResultTypeClass() {
            return resultTypeClass;
        }
        public void setResultTypeClass(Class<?> resultTypeClass) {
            this.resultTypeClass = resultTypeClass;
        }
        public String getStatementType() {
            return statementType;
        }
        public void setStatementType(String statementType) {
            this.statementType = statementType;
        }
        public SqlSource getSqlSource() {
            return sqlSource;
        }
        public void setSqlSource(SqlSource sqlSource) {
            this.sqlSource = sqlSource;
        }
    }

    3.9、MappedStatementBuilder,主要是MapperStatement的构造者对象,用于定制MappedStatement对象

    ublic class MappedStatementBuilder {
        
        private MappedStatement mappedStatement = new MappedStatement();
        
        public MappedStatementBuilder id(String id) {
            mappedStatement.setId(id);
            return this;
        }
        
        public MappedStatementBuilder parameterTypeClass(Class<?> parameterTypeClass) {
            mappedStatement.setParameterTypeClass(parameterTypeClass);
            return this;
        }
        
        public MappedStatementBuilder resultTypeClass(Class<?> resultTypeClass) {
            mappedStatement.setResultTypeClass(resultTypeClass);
            return this;
        }
        
        public MappedStatementBuilder statementType(String statementType) {
            mappedStatement.setStatementType(statementType);
            return this;
        }
        
        public MappedStatementBuilder sqlSource(SqlSource sqlSource) {
            mappedStatement.setSqlSource(sqlSource);
            return this;
        }
        
        public MappedStatement builder() {
            return mappedStatement;
        }
    }

    3.10、SqlSource,主要是封装原来的sql文本,通过这个类可以得到BoundSql对象,该对象封装最终执行的SQL语句以及参数名称列表(便于通过反射实现参数映射)

    public class SqlSource {
        
        private String sqlText;
        
        public SqlSource(String sqlText) {
            super();
            this.sqlText = sqlText;
        }
    
        public String getSqlText() {
            return sqlText;
        }
    
        public void setSqlText(String sqlText) {
            this.sqlText = sqlText;
        }
        
        
        public BoundSql getBoundSql() {
            //这是从mybatis源码中直接获得的工具类,用于解析sql获得原始的sql语句
            ParameterMappingTokenHandler tokenHandler = new ParameterMappingTokenHandler();
            GenericTokenParser parser = new GenericTokenParser("#{", "}", tokenHandler);
            //封装原始的sql以及参数列表名称
            return new BoundSql(parser.parse(sqlText), tokenHandler.getParameterMappings());
        }
        
    
    }

    3.11、BoundSql,主要封装用于执行SQL和参数名称列表

    public class BoundSql {
        
        private String originalSql;
        
        private List<ParameterMapping> parameterMappings = new ArrayList<>();
    
        public BoundSql(String originalSql, List<ParameterMapping> parameterMappings) {
            super();
            this.originalSql = originalSql;
            this.parameterMappings = parameterMappings;
        }
    
        public String getOriginalSql() {
            return originalSql;
        }
    
        public void setOriginalSql(String originalSql) {
            this.originalSql = originalSql;
        }
    
        public List<ParameterMapping> getParameterMappings() {
            return parameterMappings;
        }
    
        public void addParameterMapping(ParameterMapping parameterMapping) {
            this.parameterMappings.add(parameterMapping);
        }
    }

    3.12、SimpleExecutor,主要是Executor的一个实现类,里面封装基于JDBC的底层操作,以及输入参数和输出结果的映射操作

    public class SimpleExecutor implements Executor {
    
        @Override
        public <T> List<T> selectList(Configuration configuration, MappedStatement mappedStatement, Object params) throws SQLException {
            // TODO Auto-generated method stub
            Connection connection = getConnection(configuration);
            
            return handleStatementSelect(connection, mappedStatement, params);
        }
        
        private Connection getConnection(Configuration configuration) throws SQLException {
            if(configuration == null || configuration.getDataSource() == null) {
                throw new RuntimeException("Configuration对象或者DataSource对象不能为null");
            }
            
            return configuration.getDataSource().getConnection();
        }
        
        private <E> List<E> handleStatementSelect(Connection connection, MappedStatement mappedStatement, Object params) throws SQLException {
            String statementType = mappedStatement.getStatementType();
            List<Object> result = null;
            //不同statementType会有不同操作
            if("prepared".equals(statementType)) {
                result = handlePreparedStatementSelect(connection, mappedStatement, params);
            }else {
                //other ....
            }
            
            return (List<E>) result;
        }
        
        /**
         * JDBC操作
         * @param connection
         * @param mappedStatement
         * @param params
         * @throws SQLException
         */
        private <E> List<E> handlePreparedStatementSelect(Connection connection, MappedStatement mappedStatement, Object params) throws SQLException {
            // TODO Auto-generated method stub
            List<Object> result = new ArrayList<Object>();
            
            try {
                BoundSql boundSql = mappedStatement.getSqlSource().getBoundSql();
                String sql = boundSql.getOriginalSql();
                
                PreparedStatement preparedStatement = connection.prepareStatement(sql);
                //获得参数类型
                Class<?> parameterTypeClass = mappedStatement.getParameterTypeClass();
                //获得参数名称
                List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
                
                if("BASIC".equals(getParamenteTypeString(parameterTypeClass))) {
                    preparedStatement.setObject(1, params);
                }else {
                    //Object
                    for (int i = 0; i < parameterMappings.size(); i++) {
                        ParameterMapping parameterMapping = parameterMappings.get(i);
                        String name = parameterMapping.getName();
                        
                        Object fieldValue = ClassUtil.getPrivateFieldValue(parameterTypeClass, name, params);
                        preparedStatement.setObject(i+1, fieldValue);
                    }
                    
                    
                    Class<?> resultTypeClass = mappedStatement.getResultTypeClass();
                    ResultSet resultSet = preparedStatement.executeQuery();
                    
                    while(resultSet.next()) {
                        //遍历封装Result
                        Object newInstance = resultTypeClass.newInstance();
                        int columnCount = resultSet.getMetaData().getColumnCount();
                        for(int i = 1; i <= columnCount; i++) {
                            String columnName = resultSet.getMetaData().getColumnName(i);
                            Field field = resultTypeClass.getDeclaredField(columnName);
                            field.setAccessible(true);
                            field.set(newInstance, resultSet.getObject(columnName));
                        }
                        
                        result.add(newInstance);
                    }
                }
            } catch (Exception e) {
                // TODO: handle exception
            }
            
            return (List<E>) result;
        }
        
        private String getParamenteTypeString(Class<?> parameterTypeClass) {
            //八种基本类型以及String类型
            if(parameterTypeClass == Integer.class || parameterTypeClass == Double.class || parameterTypeClass == String.class) {
                
                return "BASIC"; 
            }else {
                //.....Map和List以及Object的处理,这里先处理Object,有待扩展
                return "OBJECT";
            }
        }
        
        
        
    
    }

     

    四、测试代码

    4.1、sqlConfig.xml, 主配置文件

    <configuration>
        <environments default="dev">
            <environment id="dev">
                <dataSource type="DBCP">
                    <property name="driver" value="com.mysql.jdbc.Driver"></property>
                    <property name="url" value="jdbc:mysql://ali_server01:3306/mybatis_study"></property>
                    <property name="username" value="root"></property>
                    <property name="password" value="root"></property>
                </dataSource>
            </environment>
        </environments>
    
        <mappers>
            <mapper resource="mapper/UserMapper.xml"></mapper>
        </mappers>
    </configuration>

    4.2、UserMapper.xml

    <mapper namespace="basic">
        <select id="selectOne" parameterType="com.hjj.mybatis.framework.pojo.User"
            resultType="com.hjj.mybatis.framework.pojo.User" statementType="prepared">
            SELECT * FROM user WHERE id = #{id} 
        </select>
    </mapper>

    4.3、UserDaoImol,封装CRUD

    public class UserDaoImpl implements UserDao {
        
        private SqlSessionFactory sqlSessionFactory;
        
        public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
            super();
            this.sqlSessionFactory = sqlSessionFactory;
        }
    
        public User selectOne(User user) {
            // TODO Auto-generated method stub
            try {
                return sqlSessionFactory.openSession().selectOne("basic.selectOne", user);
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            
            return null;
        }
    }

    4.4、测试

    public class SimpleTest {
        
        private SqlSessionFactory sqlSessionFactory;
        
        @Before
        public void init() {
            String xmlConfig = "sqlConfig.xml";
            InputStream is = this.getClass().getClassLoader().getResourceAsStream(xmlConfig);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        }
        
        @Test
        public void testSelectOne() {
            User param = new User();
            param.setId(1);
            
            User user = new UserDaoImpl(sqlSessionFactory).selectOne(param);
            System.out.println("user: " + user.toString());
        }
    }

    4.5、测试结果 

    本文作者:JaySpace
    原文出处:https://www.cnblogs.com/jayhou/p/12686986.html
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

  • 相关阅读:
    过滤器,拦截器,监听器的区别
    RedisTemplate常用集合使用说明-opsForZSet(六)
    RedisTemplate常用集合使用说明-opsForSet(五)
    RedisTemplate常用集合使用说明-opsForHash(四)
    RedisTemplate常用集合使用说明-opsForList(三)
    pip 加速方案
    swoole 使用 1
    Fatal error in launcher: Unable to create process using '"'
    webpack 的简单使用
    我 && symfony3 (路由)
  • 原文地址:https://www.cnblogs.com/jayhou/p/12686986.html
Copyright © 2020-2023  润新知