• 如何深度自定义mybatis


    • 回顾mybatis的操作的核心步骤

    • 编写核心类SqlSessionFacotryBuild进行解析配置文件

    • 深度分析解析SqlSessionFacotryBuild干的核心工作

    • 编写核心类SqlSessionFacotry

    • 深度分析解析SqlSessionFacotry干的核心工作

    • 编写核心类SqlSession

    • 深度分析解析SqlSession干的核心工作

    • 总结自定义mybatis用的技术点

    一. 回顾mybatis的操作的核心步骤

    声明一点我们本篇主要探讨的是mybatis的注解方式的操作, 完全从头开始都是小编从头开搞的, 如果与其他大神的代码思维有出入请多指教

    我们首先需要准备mybatis的核心配置文件(当然导入相关的坐标这里不在啰嗦)

    
    DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    
                    <property name="driver" value="com.mysql.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql:///db6?useSSL=false"/>
                    <property name="username" value="root"/>
                    <property name="password" value="root"/>
                dataSource>
            environment>
    
        environments>
        <mappers>
            
            <package name="cn.itcast.mapper"/>
        mappers>
    configuration>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    准备好结果的实体类以及在mapper接口上编写需要执行的sql语句

    public class User {
        private Integer uid;
        private String username;
        private String password;
        private String nickname;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    package cn.itcast.mapper;
    
    import cn.itcast.pojo.User;
    import org.apache.ibatis.annotations.Select;
    import java.util.List;
    
    public interface UserMapper {
        @Select("select * from users")
        List<User> findAll();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    使用mybatis的api来帮助我们完成sql语句的执行以及结果集的封装

    //1.关联主配置文件
    InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
    //2.解析配置文件
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    SqlSessionFactory sqlSessionFactory = builder.build(in);
    //3.创建会话对象
    SqlSession sqlSession = sqlSessionFactory.openSession();
    //4.可以采用接口代理的方式
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> all = mapper.findAll();
    System.out.println(all);
    //5.释放资源
    sqlSession.close();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    思考: mybatis大致是如何帮我们完成相关操作的 ?

    我们通过Resources的getResourceAsStream告诉了mybatis我们编写的核心配置文件的位置, mybatis就可以找到我们数据库的连接信息, 也同时找到我们编写的sql语句的地方, 然后可以将其解析按照某种规则存放起来, 我们通过调用接口代理的方式执行方法时, 可以找到对应方法上的sql语句然后执行将结果封装返回给我们

    二. 编写核心类SqlSessionFacotryBuild进行解析配置文件

    那么我们废话不多说开始我们自定义mybatis的旅程,

    1.首先我们需要用户编写配置文件, 然后通过我们自己的Resources来告诉我们配置文件所在位置
    package com.itheima.ibatis.configuration;
    
    import java.io.InputStream;
    
    public class Resources {
    
        public static InputStream getResourceAsStream(String path) {
            return ClassLoader.getSystemClassLoader().getResourceAsStream(path);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    2. 然后需要定义SqlSessionFacotryBuild来对配置文件进行解析分发
    package com.itheima.ibatis.configuration;
    
    import com.itheima.ibatis.core.session.SqlSessionFactory;
    import com.itheima.ibatis.core.session.impl.DefaultSqlSessionFactory;
    import org.dom4j.Document;
    import org.dom4j.DocumentException;
    import org.dom4j.Element;
    import org.dom4j.Node;
    import org.dom4j.io.SAXReader;
    
    import javax.sql.DataSource;
    import java.io.File;
    import java.io.InputStream;
    import java.lang.reflect.Method;
    import java.util.List;
    import java.util.Properties;
    
    public class SqlSessionFactoryBuilder {
        private Configuration configuration = new Configuration();
    
        public SqlSessionFactory build(InputStream in) {
            SAXReader saxReader = new SAXReader();
            Document document = null;
            try {
                document = saxReader.read(in);
            } catch (DocumentException e) {
                e.printStackTrace();
            }
            Element rootElement = document.getRootElement();
            parseEnvironment(rootElement.element("environments"));
            parseMapper(rootElement.element("mappers"));
            return new DefaultSqlSessionFactory(configuration);
        }
    
        private void parseMapper(Element mapper) {
            String pack = mapper.element("package").attributeValue("name");
    
            String directory = pack.replace(".", "/");
            String path = ClassLoader.getSystemClassLoader().getResource("").getPath();
            File mapperDir = new File(path, directory);
            if (!mapperDir.exists()) {
                throw new RuntimeException("找不到mapper映射");
            }
            findMapper(mapperDir, pack);
            // System.out.println(configuration.getSql());
    
        }
    
        private void findMapper(File mapperDir, String base) {
            File[] files = mapperDir.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.isFile()) {
                        if (file.getName().endsWith(".class")) {
                            String name = file.getName();
                            name = name.substring(0, name.lastIndexOf("."));
                            String className = base + "." + name;
                            initMapper(className);
                        }
                    } else {
                        findMapper(file, base + "." + file.getName());
                    }
                }
            }
        }
    
        private void initMapper(String className) {
            try {
                Class<?> clazz = Class.forName(className);
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    if(method.getAnnotations().length>0){
                        Mapper mapper = ParseMapper.parse(method);
                        this.configuration.getMappers().put(className + "." + method.getName(), mapper);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        private void parseEnvironment(Element environments) {
            String defEnv = environments.attributeValue("default");
            Node node = environments.selectSingleNode("//environment[@id='" + defEnv + "']");
            List<Element> list = node.selectNodes("//property");
            Properties properties = new Properties();
            for (Element element : list) {
                String name = element.attributeValue("name");
                String value = element.attributeValue("value");
                properties.put(name, value);
            }
            DataSource dataSource = new DefaultDataSource().getDataSource(properties);
            configuration.setDataSource(dataSource);
        }
    }
    
    
    • 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
    三. 深度分析解析SqlSessionFacotryBuild干的核心工作
    1. build(InputStream in) 方法做的工作

    ①借助Dom4j的来解析了xml文件, 将environments解析工作分发给了parseEnvironment(Element environments)

    ②将mappers的解析工作分发给了parseMapper(Element mapper)

    2. parseEnvironment(Element environments)方法做的工作

    ①主要解析了连接数据库的参数们, 并且创建了数据库连接池

    自定义连接池非本章节的重点,所以这里内部本质采用的Druid连接池来做了简化

    package com.itheima.ibatis.configuration;
    
    import com.alibaba.druid.pool.DruidDataSourceFactory;
    
    import javax.sql.DataSource;
    import java.util.Properties;
    
    public class DefaultDataSource {
        
        public DataSource getDataSource(Properties properties) {
            try {
                return DruidDataSourceFactory.createDataSource(properties);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    ②将解析好的连接池放入configuration对象中,mappers成员变量先别纠结下一章节会讲解

    package com.itheima.ibatis.configuration;
    
    import lombok.Data;
    
    import javax.sql.DataSource;
    import java.util.HashMap;
    import java.util.Map;
    
    @Data
    public class Configuration {
        private Map<String, Mapper> mappers = new HashMap<>();
        private DataSource dataSource;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    详细图解如下图
    在这里插入图片描述

    3.parseMapper(Element mapper) 方法做的工作

    ①解析出用户配置的package找到sql语句所在接口的文件夹, 交给initMapper来处理
    在这里插入图片描述

    ②递归找到这个包下所有的.class文件,并且获取到接口的全类名, 然后交给initMapper来处理

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a4CB0Gqj-1668581339456)(./imgs%5CUsers%5CAdministrator%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1658744387816.png)]

    ③initMapper通过反射获取类中的每一个方法,将方法交给一个专门解析方法上的注解的工具类ParseMapper的parse方法处理,处理完后将其放到configuration中的mappers的集合中

    在这里插入图片描述

    ④ParseMapper的parse方法做的工作, 这是解析配置的核心地方

    package com.itheima.ibatis.configuration;
    
    import java.lang.annotation.Annotation;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.lang.reflect.ParameterizedType;
    import java.lang.reflect.Type;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    public class ParseMapper {
    
    
        public static Mapper parse(Method method) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
            Annotation[] annotations = method.getAnnotations();
            Object value = annotations[0].getClass().getMethod("value").invoke(annotations[0]);
            Mapper mapper = new Mapper();
            Class<?> resultType = method.getReturnType();
            String val = (String) value;
            Pattern pattern = Pattern.compile("\\#\\{\\s*\\w+\\s*\\}");
            Matcher matcher = pattern.matcher(val);
            List<String> paramNames = new ArrayList<>();
            while (matcher.find()) {
                String group = matcher.group();
                String fieldName = group.substring(2, group.length() - 1).trim();
                paramNames.add(fieldName);
            }
            String sql = val.replaceAll("\\#\\{\\s*\\w+\\s*\\}", "?");
            mapper.setSql(sql);
            mapper.setParameterNames(paramNames);
            mapper.setSql(sql);
            if (resultType == List.class) {
                mapper.setSelectList(true);
                Type genericReturnType = method.getGenericReturnType();
                ParameterizedType parameterizedType = (ParameterizedType) genericReturnType;
                Type actualTypeArgument = parameterizedType.getActualTypeArguments()[0];
                mapper.setResultType(actualTypeArgument.getTypeName());
                mapper.setType("SELECT");
            } else if (resultType == Integer.class || resultType == int.class) {
                mapper.setType("UPDATE");
            } else {
                mapper.setType("SELECT");
                mapper.setResultType(resultType.getName());
            }
            return mapper;
        }
    }
    
    
    • 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

    首先拿到方法上的注解,得到用户填入的sql语句

    在这里插入图片描述

    然后处理sql语句#{参数}的这些数据, 然后将参数的顺序保存起来, 用来后期设置参数的数据做准备, 一个

    方法对应一个Mapper对象

    在这里插入图片描述

    然后再根据结果类型, 判断是什么类型相关的操作,方便后期执行对应的sql语句

    在这里插入图片描述

    四. 编写核心类SqlSessionFacotry
    1.回顾那个地方创建的SqlSessionFacotry对象

    经过SqlSessionFacotryBuilder的努力, 我们成功的将配置文件中核心的信息解析出来并放入了configuration对象中了, 然后我们此时将解析好的configuration传入到SqlSessionFacotry中

    在这里插入图片描述

    SqlSessionFactory的实现类如下:

    public class DefaultSqlSessionFactory implements SqlSessionFactory {
        private final Configuration configuration;
        private TransactionManagement defaultTransactionManagement;
    
        public DefaultSqlSessionFactory(Configuration configuration) {
            this.configuration =configuration;
            defaultTransactionManagement = new DefaultTransactionManagement(configuration.getDataSource());
        }
        @Override
        public SqlSession openSession() {
            return new DefaultSqlSession(configuration,defaultTransactionManagement,false);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    2.添加事务管理器

    事务管理是一个小的功能, 里面希望使用ThreadLocal集合来保证一个用户拿到的链接是同一个

    在这里插入图片描述

    事务管理的代码如下:

    public class DefaultTransactionManagement implements TransactionManagement {
        private ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
        private DataSource dataSource;
    
        public DefaultTransactionManagement(DataSource dataSource) {
            this.dataSource = dataSource;
        }
    
        public Connection getConnection() {
            Connection connection = threadLocal.get();
            if (connection == null) {
                try {
                    connection = dataSource.getConnection();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                threadLocal.set(connection);
            }
            return connection;
        }
    
        @Override
        public void commit() {
            Connection connection = threadLocal.get();
            if (connection != null ) {
                try {
                    connection.commit();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
    
        }
    
        @Override
        public void rollback() {
            Connection connection = threadLocal.get();
            if (connection != null) {
                try {
                    connection.rollback();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    
    
        public void close() {
            Connection connection = threadLocal.get();
            if (connection != null) {
                try {
                    connection.close();
                    threadLocal.remove();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    
        @Override
        public void begin() {
            Connection connection = threadLocal.get();
            if (connection != null) {
                try {
                    connection.setAutoCommit(false);
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    • 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
    五. 深度分析解析SqlSessionFacotry干的核心工作
    1. SqlSession openSession() 方法做的工作

    可以看的出来我们在这个方法创建了DefaultSqlSession对象,并传入封装好的configuration,默认的事务管理器

    默认通过openSession事务是开启的等等相关的参数

    在这里插入图片描述

    六.编写核心类SqlSession

    其实有SqlSession的接口,我们使用的实现类是DefaultSession, 这里记录了解析的配置对象configuration

    默认事务管理器对象transactionManagement, 默认事务开启的状态tx标记

    package com.itheima.ibatis.core.session.impl;
    
    import com.itheima.ibatis.configuration.Configuration;
    import com.itheima.ibatis.configuration.Mapper;
    import com.itheima.ibatis.core.BaseExecutor;
    import com.itheima.ibatis.core.annotation.Param;
    import com.itheima.ibatis.core.session.SqlSession;
    import com.itheima.ibatis.core.transaction.TransactionManagement;
    import java.lang.reflect.*;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    public class DefaultSqlSession implements SqlSession {
        private final Configuration configuration;
        private final boolean tx;
        private TransactionManagement transactionManagement;
    
        public DefaultSqlSession(Configuration configuration, TransactionManagement transactionManagement, boolean tx) {
            this.configuration = configuration;
            this.transactionManagement = transactionManagement;
            this.tx = tx;
        }
        public void close() {
            transactionManagement.close();
        }
        @Override
        public void commit() {
            transactionManagement.commit();
        }
        @Override
        public void rollback() {
            transactionManagement.rollback();
        }
    
        @Override
        public <T> List<T> selectList(String sqlId) {
            return selectList(sqlId, null);
        }
    
        @Override
        public <T> List<T> selectList(String sqlId, Object param) {
            List<Object> list = new BaseExecutor(transactionManagement, tx).queryList(getMapper(sqlId), param);
            return (List<T>) list;
        }
    
        @Override
        public <T> T selectOne(String sqlId) {
            return selectOne(sqlId, null);
        }
    
        @Override
        public <T> T selectOne(String sqlId, Object param) {
            return new BaseExecutor(transactionManagement, tx).query(getMapper(sqlId), param);
        }
    
        @Override
        public int delete(String sqlId) {
            return update0(sqlId, null);
        }
    
        @Override
        public int delete(String sqlId, Object param) {
            return update0(sqlId, param);
        }
    
        @Override
        public int update(String sqlId) {
            return update0(sqlId, null);
        }
    
        @Override
        public int update(String sqlId, Object param) {
            return update0(sqlId, param);
        }
    
        @Override
        public int insert(String sqlId) {
            return update0(sqlId, null);
        }
    
        @Override
        public int insert(String sqlId, Object param) {
            return update0(sqlId, param);
        }
    
        @Override
        public <T> T getMapper(Class<T> clazz) {
            Object o = Proxy.newProxyInstance(
                    clazz.getClassLoader(),
                    new Class[]{clazz}, new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            String sqlId = clazz.getName() + "." + method.getName();
                            Mapper mapper = configuration.getMappers().get(sqlId);
                            String type = mapper.getType();
                            Object findParam = null;
                            if (args != null) {
                                if (args.length == 1) {
                                    Object param = args[0];
                                    boolean isArray = param.getClass().isArray();
                                    if (!isArray) {
                                        findParam = param;
                                    }
                                } else {
                                    Map<String, Object> map = new HashMap<>();
                                    Parameter[] parameters = method.getParameters();
                                    for (int i = 0; i < parameters.length; i++) {
                                        Param param = parameters[i].getAnnotation(Param.class);
                                        String key = "arg"+i;
                                        if(param !=null){
                                            key = param.value();
                                        }
                                        map.put(key, args[i]);
                                    }
                                    findParam = map;
                                }
                            }
    
                            if (type.equals("SELECT")) {
                                boolean selectList = mapper.isSelectList();
                                if (selectList)
                                    return selectList(sqlId, findParam);
                                else
                                    return selectOne(sqlId, findParam);
                            } else {
                                return update0(sqlId, findParam);
                            }
                        }
                    });
            return (T) o;
        }
    
        private int update0(String sqlId, Object param) {
            return new BaseExecutor(transactionManagement, tx).update(getMapper(sqlId), param);
        }
    
        public Mapper getMapper(String sqlId) {
            Mapper mapper = configuration.getMappers().get(sqlId);
            if (mapper == null) {
                throw new RuntimeException("没有找到sql映射,请检查");
            }
            return mapper;
        }
    }
    
    • 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
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    七.深度分析解析SqlSession干的核心工作
    1.selectOne & selectList做的工作

    主要是分发了下功能, 执行sql语句避免不了有参数和无参数的, 都让调用有参数的方便管理

    在这里插入图片描述

    在执行前, 考虑还有一种情况, 用户不是通过接口代理的方式来执行以上方法, 这样手动输入sqlId容易造成错误

    这里做一个健壮性判断

    在这里插入图片描述

    BaseExecutor中的query以及queryList做的核心工作

    首先这两个方法的特点都是查询, 其步骤基本类似, 所以这里可以合并一起转调query0功能

    在这里插入图片描述

    这里需要对参数进行设定, 还根据最后isOne的参数决定返回值是否是单个

    在这里插入图片描述

    参数设置这里比较复杂我们通过图解的方式来解释, (注: 参数是List集合类型的和数组类型的没有做!!!)

    在这里插入图片描述

    对结果的封装主要用到内省技术和数据库元数据等等知识点

    在这里插入图片描述

    2.update&delete&insert做的工作

    在这里插入图片描述

    BaseExecutor中的update做的核心工作

    还是和query&queryList一样需要设置参数, 不管是增删改其本质其结果都是一致

    在这里插入图片描述

    3.getMapper代理模式开发的原理

    主要使用的动态代理的技术创建接口的实现类, 内部主要整合了sqlId和参数, 省去用户自己拼sqlId拼错的风险

    也同时解决用户手动合参数的麻烦, 但是最终工作的还是selectOne,selectList以及update0这些方法

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    总结自定义mybatis用的技术点

    一款框架的诞生肯定不是一蹴而就的, 随着时间慢慢推进逐步更新出来, 所以一款好的框架肯定要经过

    很多考验才能够稳定靠谱, 但是纵观整篇用的技术点, 不难发现框架也是由基础代码编写而来,解决大量重复

    的工作, 提供扩展性等等机制,比如本篇用核心的技术点有

    ① 反射

    ② 内省

    ③ 解析xml

    ④ 动态代理

    ⑤ 工厂设计模式

    等等, 感谢大家耐心阅览, 附件有本篇的原码, 如果有更好的建议和想法欢迎和小编一起探讨交流

  • 相关阅读:
    分布式ID
    服务器端使用django websocket,客户端使用uniapp 请问服务端和客户端群组互发消息的代码怎么写的参考笔记
    2022中国物流产业大会暨企业家高峰论坛在杭州举办!
    Java基础知识
    用于语义线检测的深度霍夫变换
    “赔率”和“概率”
    mfc对话框添加OnMouseMove
    【ACDC数据集】:预处理ACDC心脏3D MRI影像数据集到VOC数据集格式,nii转为jpg,label转为png
    一、Python Django源码运行过程
    论文浅尝 | GPT-RE:基于大语言模型针对关系抽取的上下文学习
  • 原文地址:https://blog.csdn.net/cz_00001/article/details/127885256