zoukankan      html  css  js  c++  java
  • 手写MyBatis,纯手工打造开源框架(第三篇:运筹帷幄)

    说明

    MyBatis版本:3.5.1

     

    相关历史文章(阅读本文之前,您可能需要先看下之前的系列

    Spring Boot MyBatis最全教程:你值得拥有
    MyBatis能脱离Spring吗一图纵览MyBatis的工作原理
    从源码看MyBatis,竟如此简单MyBatis的Mapper是什么`垃圾` 

    手写MyBatis,纯手工打造开源框架(第一篇:风云再起)

    手写MyBatis,纯手工打造开源框架(第二篇:君临天下) 

     

    前言

           通过上面我们已经可以构建了SqlSessionFactory,接下来的话就是要怎么获取一个SqlSession。

     

    一、分析

           对于SqlSession的构建的话,需要有一个属性Configuration,这个属性在上面的SqlSessionFactory已经有了;另外对于SqlSession的真正的Sql执行是交给了Executor,Executor是真正和数据库进行交互了,所以需要将数据库配置信息传给Executor。

     

    二、编码

    2.1 Executor

           构造SqlSession需要有Executor,我们先创建一个Executor接口:

    package com.kfit.mybatis.session;
    
    import java.util.List;
    
    import com.kfit.mybatis.config.MapperStatement;
    
    public interface Executor {
         <E> List<E> query(MapperStatement ms, Object parameter);
    }
    

      

     

           我们实现一个最基本的SimpleExecutor:

    package com.kfit.mybatis.session.impl;
    
    import java.util.List;
    
    import com.kfit.mybatis.config.JdbcProperties;
    import com.kfit.mybatis.config.MapperStatement;
    import com.kfit.mybatis.session.Executor;
    
    public class SimpleExecutor implements Executor {
        private JdbcProperties jdbcProperties;
    
        public SimpleExecutor(JdbcProperties jdbcProperties) {
            this.jdbcProperties = jdbcProperties;
        }
    
        public <E> List<E> query(MapperStatement ms, Object parameter) {
            //具体的方法待实现
    
            return null;
        }
    
    }
    

      

    说明:

    (1)这里我们实现了最基本的Simple,在MyBatis有3情况需要处理,我们实现最简单的方式。

    (2)这里我们接收了jdbcproperties为了之后直接进行数据库的连接操作,在mybatis数据库的连接关闭,提交,回滚是有一个事务类Transaction。

     

    2.2 SqlSessionFactory

           在SqlSessionFactory中添加一个获取SqlSession的方法:

    public interface SqlSessionFactory {
        public Configuration getConfiguration();
        public SqlSession openSession();
    }
    

      

     

           在DefaultSqlSessionFactory实现openSession()方法:

        public SqlSession openSession() {
            Executor executor = new SimpleExecutor(configuration.getJdbcProperties());
            SqlSession sqlSession = new DefaultSqlSession(configuration, executor);
            return sqlSession;
        }
    

      

     

     

    2.3 SimpleExecutor数据库操作

           我们这个对数据库操作的核心代码:

    package com.kfit.mybatis.session.impl;
    
    import java.lang.reflect.Field;
    import java.lang.reflect.Type;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.ArrayList;
    import java.util.List;
    
    import com.kfit.mybatis.config.JdbcProperties;
    import com.kfit.mybatis.config.MapperStatement;
    import com.kfit.mybatis.session.Executor;
    
    public class SimpleExecutor implements Executor {
        private JdbcProperties jdbcProperties;
    
        public SimpleExecutor(JdbcProperties jdbcProperties) {
            this.jdbcProperties = jdbcProperties;
        }
    
        public <E> List<E> query(MapperStatement ms, Object parameter) {
            List<E> ret = new ArrayList<E>();
            // 具体的方法待实现
            try {
                // 加载驱动
                Class.forName(jdbcProperties.getDriver());
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            Connection connection = null;
    
            PreparedStatement preparedStatement = null;
            ResultSet resultSet = null;
            try {
                // 获取连接
                connection = DriverManager.getConnection(jdbcProperties.getUrl(), jdbcProperties.getUsername(),
                        jdbcProperties.getPassword());
                // 预编译sql语句
                preparedStatement = connection.prepareStatement(ms.getSql());
                // 处理sql语句中的占位符
                parameterize(preparedStatement, parameter);
                // 执行sql语句
                resultSet = preparedStatement.executeQuery();
                // 处理结果
                handlerResultSet(resultSet, ret, ms.getResultType());
            } catch (SQLException e) {
                e.printStackTrace();
            }
    
            return ret;
        }
    
        private void parameterize(PreparedStatement preparedStatement, Object parameter) throws SQLException {
            if (parameter instanceof String) {
                preparedStatement.setString(1, (String) parameter);
            } else if (parameter instanceof Long) {
                preparedStatement.setLong(1, (Long) parameter);
            } else if (parameter instanceof Integer) {
                preparedStatement.setInt(1, (Integer) parameter);
            }
        }
    
        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中
                    // id,name,sex,age
                    // 获取实体类的所有属性,返回Field数组
                    Field[] fields = clazz.getDeclaredFields();
                    for (Field field : fields) {
                        field.setAccessible(true);
                        String fname = field.getName();
                        Type type = field.getGenericType();
                        if (type.toString().equals("class java.lang.String")) {
                            String column = resultSet.getString(fname);
                            field.set(entity, column);
                        }else if (type.toString().equals("long")) {
                            Long column = resultSet.getLong(fname);
                            field.set(entity, column);
                        }
                    }
                    ret.add((E) entity);
                }
            } catch (SQLException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    
    }
    

      

    
    

    说明:

    (1)在MyBatis中这个代码是分好几个类进行处理的,这里为了讲解方便,统一放在一个类中。

    (2)数据库的连接操作:这里使用jdbc连接数据库获取到connection进行操作。

    (3)使用泛型处理返回的结果(handlerResultSet)。

     

    2.4 SqlSession的方法

           到这里,我们就可以编写SqlSession的方法了,这里定义两个方法SelectOne和SelectList();

    public interface SqlSession {
         <T> T selectOne(String statement, Object parameter);
         <E> List<E> selectList(String statement);
    <E> List<E> selectList(String statement, Object parameter);
    }
    

      

     

           对应的DefaultSqlSession:

    package com.kfit.mybatis.session.impl;
    
    
    import java.util.List;
    
    import com.kfit.mybatis.config.Configuration;
    import com.kfit.mybatis.session.Executor;
    import com.kfit.mybatis.session.SqlSession;
    
    public class DefaultSqlSession implements SqlSession {
        private Configuration configuration;
        private Executor executor;
        public DefaultSqlSession(Configuration configuration,Executor executor) {
            this.configuration= configuration;
            this.executor = executor;
        }
    
    
        public <E> List<E> selectList(String statement) {
            return executor.query(configuration.getMapperStatement(statement),null);
        }
    
    public <E> List<E> selectList(String statement,Object parameter) {
            return executor.query(configuration.getMapperStatement(statement), parameter);
        }
    
    
        public <T> T selectOne(String statement,Object parameter) {
            List<T> list = executor.query(configuration.getMapperStatement(statement),parameter);
            if(list.size()>0) {
                return list.get(0);
            }
            return null;
        }
    }
    

      

     

    说明:DefaultSqlSession的具体处理交给了Executor,所以这里的具体的实现就比较简单了。

    2.5 测试下

           在main方法来进行测试一下吧:

     
       public static void main(String[] args) {
            String resource = "mybatis-config.xml";
            InputStream inputStream = App.class.getClassLoader().getResourceAsStream(resource);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            System.out.println(sqlSessionFactory);
            System.out.println(sqlSessionFactory.getConfiguration().getJdbcProperties().getUrl());
    
            SqlSession sqlSession = sqlSessionFactory.openSession();
    
    
            Demo demo = null;
            List<Demo> demos = null;
    
            //使用sqlSession直接查询
            demo = sqlSession.selectOne("com.kfit.mybatis.demo.mapper.DemoMapper.getById",1L);
            System.out.println(demo);
            demos = sqlSession.selectList("com.kfit.mybatis.demo.mapper.DemoMapper.getAll");
            System.out.println(demos);
    
        }
    

      

     

           这个方法和之前写mybatis的使用方式上是一模一样的,运行看下效果吧:

    Demo[id=1, name=张三1]

    [Demo [id=1, name=张三1], Demo [id=9, name=张三], Demo [id=10, name=张三], Demo [id=11, name=张三], Demo [id=12, name=张三], Demo [id=13, name=张三]]

           看到如此帅气的结果,这是爽歪歪,厉害了我的哥。本篇就先介绍到这里,下一篇我们将会介绍无敌的Mapper实现。

    我就是我,是颜色不一样的烟火。
    我就是我,是与众不同的小苹果。

    à悟空学院:http://t.cn/Rg3fKJD

    学院中有Spring Boot相关的课程!点击「阅读原文」进行查看!

    SpringBoot视频:http://t.cn/R3QepWG

    Spring Cloud视频:http://t.cn/R3QeRZc

    SpringBoot Shiro视频:http://t.cn/R3QDMbh

    SpringBoot交流平台:http://t.cn/R3QDhU0

    SpringData和JPA视频:http://t.cn/R1pSojf

    SpringSecurity5.0视频:http://t.cn/EwlLjHh

    Sharding-JDBC分库分表实战:http://t.cn/E4lpD6e

  • 相关阅读:
    修改版的jsonView,加入了PHP的反序列化
    CSBlogV2公测发布,欢迎大家下载试用体验.
    C#里使用Oracle提供的Oracle.DataAccess 返回包里的记录集游标,含Oralce里的分页包代码
    用一个示例方法来初始理解ManualResetEvent的用法。
    Head First 设计模式阅读所得:策略模式(Strategy Pattern) 接口的用处(之一)
    延时至调用时获取被反序列化数据的类型的实现
    权限设计中的"依赖颠倒"
    CSCMSV1终于上线内测了[广告贴]
    Windows Service 使用参数安装DEMO,可使用控制台启动方式进行调试,服务安装完后立即启动
    使用assembly.GetExportedTypes();方法时引发动:态程序集中不支持已调用的成员的异常
  • 原文地址:https://www.cnblogs.com/springboot-wuqian/p/11381814.html
Copyright © 2011-2022 走看看