远方的灯塔 - 专注于服务端技术分享 远方的灯塔 - 专注于服务端技术分享
首页
  • Java SE
  • Struts2
  • Hibernate
  • MyBatis
  • JAX-WS
  • 并发
  • 分布式
  • Git
  • 文章分类
  • 文章标签
  • 文章归档
  • 《C程序设计语言》
心情随笔
友情链接
给我留言 (opens new window)
关于我
GitHub (opens new window)

Terwer Green

一个后端老菜鸟
首页
  • Java SE
  • Struts2
  • Hibernate
  • MyBatis
  • JAX-WS
  • 并发
  • 分布式
  • Git
  • 文章分类
  • 文章标签
  • 文章归档
  • 《C程序设计语言》
心情随笔
友情链接
给我留言 (opens new window)
关于我
GitHub (opens new window)
  • JavaSE

  • 开源框架

    • MyBatis

      • JDBC的问题分析
      • 自定义持久层框架的设计思路
      • 自定义持久层框架的代码实现一
      • 自定义持久层框架的代码实现二
        • 代码实现续
          • 核心执行引擎Executor的实现
        • 问题修复
        • 代码仓库
      • 使用getMapper方式对自定义持久层框架进行优化
      • MyBatis的基本介绍及优势
      • MyBatis的基本使用
      • Mybatis基本流程及配置文件解析
      • MyBatis复杂映射开发之一对一查询
      • MyBatis复杂映射开发之一对多查询
      • MyBatis复杂映射开发之多对多查询
      • MyBatis常用注解及基本增删改查的注解实现
      • MyBatis的注解实现复杂映射开发
      • MyBatis缓存的概念
      • MyBatis的一级缓存
      • MyBatis的二级缓存
      • MyBatis的二级缓存整合redis
      • MyBatis-RedisCache源码分析
  • Linux

  • Struts2

  • Hibernate

  • Webservice

  • 分布式

  • 分布式框架

  • 后端开发
  • 开源框架
  • MyBatis
terwer
2022-08-29
目录

自定义持久层框架的代码实现二

# 代码实现续

# 核心执行引擎Executor的实现

    /**
     * 执行器的实现
     *
     * @name: SimpleExecutor
     * @author: terwer
     * @date: 2022-05-08 16:53
     */
    class SimpleExecutor : Executor {
        @Throws(Exception::class)
        override fun <E> query(
            configuration: Configuration,
            mappedStatement: MappedStatement,
            vararg params: Any
        ): List<E> {
            // 注册驱动,获取链接
            val connection = configuration.dataSource.connection
    
            // 获取sql语句
            // 获取的sql
            // select * from user where id = #{id} and username = #{username}
            // 转换后的sql
            // select * from user where id = ? and username = ?
            val sql = mappedStatement.sql
    
            // 转换sql语句
            val boundSql = getBoundSql(sql)
    
            // 获取预处理对象
            val preparedStatement = connection.prepareStatement(boundSql.sqlText)
    
            // 设置参数
            // 参数全路径
            val parameterType = mappedStatement.parameterType
            val parameterClass = getClassType(parameterType)
            val parameterMappingList = boundSql.parameterMappingList
            for (i in parameterMappingList.indices) {
                val parameterMapping = parameterMappingList[i]
                val content = parameterMapping.content
                val field = parameterClass!!.getDeclaredField(content)
                field.isAccessible = true
                val value = field[params[0]]
                preparedStatement.setObject(i + 1, value)
            }
    
            // 执行sql
            val resultSet = preparedStatement.executeQuery()
            val returnType = mappedStatement.resultType
            val resultTypeClass = getClassType(returnType)
            val objects = ArrayList<Any>()
    
            // 封装返回结果集
            while (resultSet.next()) {
                val o = resultTypeClass!!.newInstance()
                val metaData = resultSet.metaData
                for (i in 1..metaData.columnCount) {
                    val columnName = metaData.getColumnName(i)
                    // 获取字段值
                    val value = resultSet.getObject(columnName)
    
                    // 使用反射或者内省,根据数据库表和实体的对应关系完成封装
                    val propertyDescriptor = PropertyDescriptor(columnName, resultTypeClass)
                    val writeMethod = propertyDescriptor.writeMethod
                    writeMethod.invoke(o, value)
                }
                objects.add(o)
            }
            return objects as List<E>
        }
    
        @Throws(ClassNotFoundException::class)
        private fun getClassType(parameterType: String?): Class<*>? {
            return if (parameterType != null) {
                Class.forName(parameterType)
            } else null
        }
    
        /**
         * 1、将#{}使用?代替
         * 2、解析出#{}的值进行存储
         *
         * @param sql
         * @return
         */
        private fun getBoundSql(sql: String): BoundSql {
            // 标记处理类,配合标记解析器完成对占位符的解析
            val tokenHandler = ParameterMappingTokenHandler()
            val genericTokenParser = GenericTokenParser("#{", "}", tokenHandler)
    
            // 解析后的sql
            val parseSql = genericTokenParser.parse(sql)
            // 解析的参数名称
            val parameterMappings = tokenHandler.parameterMappings
            return BoundSql(parseSql, parameterMappings)
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    /**
     * 执行器的实现
     *
     * @name: SimpleExecutor
     * @author: terwer
     * @date: 2022-03-14 16:53
     **/
    public class SimpleExecutor implements Executor {
        @Override
        public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception {
            // 注册驱动,获取链接
            Connection connection = configuration.getDataSource().getConnection();
    
            // 获取sql语句
            // 获取的sql
            // select * from user where id = #{id} and username = #{username}
            // 转换后的sql
            // select * from user where id = ? and username = ?
            String sql = mappedStatement.getSql();
    
            // 转换sql语句
            BoundSql boundSql = getBoundSql(sql);
    
            // 获取预处理对象
            PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());
    
            // 设置参数
            // 参数全路径
            String parameterType = mappedStatement.getParameterType();
            Class<?> parameterClass = getClassType(parameterType);
    
            List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
            for (int i = 0; i < parameterMappingList.size(); i++) {
                ParameterMapping parameterMapping = parameterMappingList.get(i);
                String content = parameterMapping.getContent();
    
                Field field = parameterClass.getDeclaredField(content);
                field.setAccessible(true);
                Object value = field.get(params[0]);
    
                preparedStatement.setObject(i + 1, value);
            }
    
            // 执行sql
            ResultSet resultSet = preparedStatement.executeQuery();
            String returnType = mappedStatement.getResultType();
            Class<?> resultTypeClass = getClassType(returnType);
            Object o = resultTypeClass.newInstance();
            ArrayList<Object> objects = new ArrayList<>();
    
            // 封装返回结果集
            while (resultSet.next()) {
                ResultSetMetaData metaData = resultSet.getMetaData();
                for (int i = 1; i <= metaData.getColumnCount(); i++) {
                    String columnName = metaData.getColumnName(i);
                    // 获取字段值
                    Object value = resultSet.getObject(columnName);
    
                    // 使用反射或者内省,根据数据库表和实体的对应关系完成封装
                    PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultTypeClass);
                    Method writeMethod = propertyDescriptor.getWriteMethod();
                    writeMethod.invoke(o, value);
                }
                objects.add(o);
            }
    
            return (List<E>) objects;
        }
    
        private Class<?> getClassType(String parameterType) throws ClassNotFoundException {
            if (parameterType != null) {
                Class<?> aClass = Class.forName(parameterType);
                return aClass;
            }
            return null;
        }
    
        /**
         * 1、将#{}使用?代替
         * 2、解析出#{}的值进行存储
         *
         * @param sql
         * @return
         */
        private BoundSql getBoundSql(String sql) {
            // 标记处理类,配合标记解析器完成对占位符的解析
            ParameterMappingTokenHandler tokenHandler = new ParameterMappingTokenHandler();
            GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", tokenHandler);
    
            // 解析后的sql
            String parseSql = genericTokenParser.parse(sql);
            // 解析的参数名称
            List<ParameterMapping> parameterMappings = tokenHandler.getParameterMappings();
    
            BoundSql boundSql = new BoundSql(parseSql, parameterMappings);
    
            return boundSql;
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    // Make sure to add code blocks to your code group

    运行效果

    ​

    # 问题修复

    1、selectList打印的全部是同一个值

    /Library/Java/JavaVirtualMachines/jdk1.8.0_291.jdk/Contents/Home/bin/java -... com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 com.terwergreen.test.IPersistenceTest,test2
    Connected to the target VM, address: '127.0.0.1:50317', transport: 'socket'
    log4j:WARN No appenders could be found for logger (com.mchange.v2.log.MLog).
    log4j:WARN Please initialize the log4j system properly.
    User{id=5, username='dali'}
    User{id=5, username='dali'}
    User{id=5, username='dali'}
    User{id=5, username='dali'}
    Disconnected from the target VM, address: '127.0.0.1:50317', transport: 'socket'
    
    Process finished with exit code 0
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    修正方案

    ​

    修正后

    /Library/Java/JavaVirtualMachines/jdk1.8.0_291.jdk/Contents/Home/bin/java -... com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 com.terwergreen.test.IPersistenceTest,test2
    Connected to the target VM, address: '127.0.0.1:50820', transport: 'socket'
    log4j:WARN No appenders could be found for logger (com.mchange.v2.log.MLog).
    log4j:WARN Please initialize the log4j system properly.
    User{id=1, username='tyw'}
    User{id=2, username='张月'}
    User{id=4, username='haha'}
    User{id=5, username='dali'}
    Disconnected from the target VM, address: '127.0.0.1:50820', transport: 'socket'
    
    Process finished with exit code 0
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    # 代码仓库

    custom-persistence (opens new window)

    文章更新历史
    2022/05/08 feat:增加Kotlin实现

    ‍

    编辑 (opens new window)
    #custom#dao#framework#persistence#mybatis#mybatis-4
    上次更新: 2023/02/22, 13:47:25
    自定义持久层框架的代码实现一
    使用getMapper方式对自定义持久层框架进行优化

    ← 自定义持久层框架的代码实现一 使用getMapper方式对自定义持久层框架进行优化→

    最近更新
    01
    解决css部分border被圆角切掉之后圆角的边框消失问题
    03-18
    02
    使用TypeScript开发一个自定义的Node-js前端开发脚手架
    03-08
    03
    Github-Actions使用release-please实现自动发版
    03-06
    更多文章>
    Theme by Vdoing | Copyright © 2011-2023 Terwer Green | MIT License | 粤ICP备2022020721号-1 | 百度统计
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式