美文网首页干货分享
源码系列——mybatis源码刨析总结

源码系列——mybatis源码刨析总结

作者: 小白菜aaa | 来源:发表于2020-10-09 16:01 被阅读0次

    初始化

    1.创建git仓库

    1.新建一个目录 然后点击右键 git base here 创建git (会弹出一个窗口)
    2.初始化 再窗口输入 git init
    3.指定仓库 git clone 仓库地址
    4.上传文件 点击右键 git提交->master (选择提交并推送)

    1. jdbc连接数据库的缺陷

    1.1 代码

      public static void main(String[] args) {
            Connection connection = null;
            PreparedStatement preparedStatement = null;
            ResultSet resultSet = null;
            try {
                //加载数据库驱动
                Class.forName("com.mysql.jdbc.Driver");
                //通过驱动管理类获取数据库连接
                connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/lgstudy?characterEncoding=utf-8", "root", "root");
                //---有参数
    //            //?占位符
    //            String sql = "select * from user where username = ?";
    //            //获取预处理statement
    //            preparedStatement = connection.prepareStatement(sql);
    //            // 设置参数
    //            preparedStatement.setString(1, "占山");
                //---无参数
                String sql = "select * from user";
                preparedStatement = connection.prepareStatement(sql);
                // 执行sql查询结果集
                resultSet = preparedStatement.executeQuery();
                //遍历结果集
                while (resultSet.next()) {
                    int id = resultSet.getInt("id");
                    String username = resultSet.getString("username");
                    User user = new User();
                    //封装user
                    user.setId(id);
                    user.setUsername(username);
                    System.out.println(user);
                }
    
            } catch (Exception e) {
                e.printStackTrace();
            } finally
    
            {
                // 释放资源
                if (resultSet != null) {
                    try {
                        resultSet.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (preparedStatement != null) {
                    try {
                        preparedStatement.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (connection != null) {
                    try {
                        connection.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
    
        }
    
    

    1.2 缺陷

    1.连接数据库的配置信息硬编码  ===》存到配置文件中
    2.数据库的频繁断开连接==》连接池
    3.sql语句,设置参数 硬编码 ==》存到配置文件中 设置参数 反射
    4.手动遍历封装结果集  ==》 内省
    
    

    2. 自定义持久层框架

    2.1 思路

    2.1.1使用端

    1.引入自定义持久层的包
    2.提供两部分配置信息:
        1.数据库的连接配置信息 sql配置信息
        2.mapper.xml 存放sql配置信息
    
    

    2.1.2自定义持久层框架(封装jdbc的操作)

    1.加载配置文件;根据配置文件的路径,加载配置成字节输入流,存储在内存中
    2.创建两个javaBean(容器)
        Configuration核心配置类:存放sql的配置信息
        MappedStatement映射配置类 存放mapper.xml信息
    3.解析配置文件 dom4j
        创建类sqlSessionFactoryBuilder
            1.使用dom4j解析配置文件,将解析出来的文件存放到javabean(容器)中
            2.创建sqlSessionFactory对象,生产session (工厂模式)
    4.创建sqlSessionFactory接口实现类
        openSession
    5.创建sqlSession接口及DefaultSession
        定义数据库的crud操作 selectlist()
                          update()
    6.执行Exector接口及实现类SimpleExector
        query()//执行jdbc代码
    
    

    2.1.3自定义持久层框架(缺陷)

    1.dao层,存在代码重复,整个操作的过程模版重复(加载配置文件)
    2.statementId存在硬编码
    ========》jdk动态代理 
    Proxy.newProxyInstance  InvocationHandler
    
    

    2.2 代码实现

    2.2.1 使用端

    1.创建项目

    1.创建lagou-mybatis项目(maven骨架项目)

    2.创建sqlMapConfig.xml(sql源配置文件)
    <configuration>
        <!--数据库配置信息-->
        <dataSource>
            <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
            <property name="jdbcUrl" value="jdbc:mysql:///lgstudy"></property>
            <property name="username" value="root"></property>
            <property name="password" value="root"></property>
        </dataSource>
    
        <!--存放mapper.xml的全路径-->
        <mapper resource="UserMapper.xml"></mapper>
    </configuration>
    
    
    2.创建UserMapper.xml(user类的sql)
    <!--namespace 命名空间 当前xml的-->
            <!--
            有三种全路径:namespace绑定实体类的全路径,绑定dao接口的全路径,绑定mapper的sql.xml文件。
            第一种:namespace绑定实体类的全路径:
            第二种:namespace绑定dao层接口的全路径:
            第三种:namespace绑定的是mapper接口对应的sql.xml。
    
            https://www.cnblogs.com/zjdxr-up/p/8681382.html
            -->
    <mapper namespace="com.test.dao.IUserDao">
    
        <!--sql的唯一标识:namespace.id来组成 : statementId-->
        <select id="findAll" resultType="com.test.pojo.User" >
            select * from user
        </select>
    
        <select id="findOneById" resultType="com.test.pojo.User" paramterType="com.test.pojo.User">
            select * from user where id = #{id}
        </select>
    
    </mapper>
    
    

    2.2.2 框架端

    1.创建项目

    1.创建lagou-mybatis项目(maven骨架项目)

    2.pojo类

    Configuration 用于存储 数据库源

    public class Configuration {
    
        private DataSource dataSource;
    
        /*
        *   key: statementid  value:封装好的mappedStatement对象
         * */
        Map<String,MappedStatement> mappedStatementMap = new HashMap<>();
        }
    
    

    MappedStatement 用于存储 sql文件的引用

    //存储的SQL xml文件的映射对象
    public class MappedStatement {
    
        //id标识
        private String id;
        //返回值类型
        private String resultType;
        //参数值类型
        private String paramterType;
        //sql语句
        private String sql;
        }
    
    
    3.io类

    Resources

    public class Resources {
        // 根据配置文件的路径,将配置文件加载成字节输入流,存储在内存中
        public static InputStream getResourceAsSteam(String path){
            InputStream resourceAsStream = Resources.class.getClassLoader().getResourceAsStream(path);
            return  resourceAsStream;
        }
    }
    
    

    4.扫描类 封装 UserMapper

    
    public class XMLMapperBuilder {
    
        private Configuration configuration;
    
        private String namespace;
        public XMLMapperBuilder(Configuration configuration) {
            this.configuration =configuration;
        }
    
        public void parse(InputStream inputStream) throws DocumentException {
    
            Document document = new SAXReader().read(inputStream);
            Element rootElement = document.getRootElement();
            //获取定义的命名空间
            namespace = rootElement.attributeValue("namespace");
            getNodes(rootElement);
        }
    
        public void getNodes(Element rootElement){
            //获取标签名称
            String name = rootElement.getName();
            List<Element> list = rootElement.selectNodes("//"+name);
            for (Element element : list) {
                String id = element.attributeValue("id");
                String resultType = element.attributeValue("resultType");
                String paramterType = element.attributeValue("paramterType");
                String sqlText = element.getTextTrim();
                if(id!=null){
                    MappedStatement mappedStatement = new MappedStatement();
                    mappedStatement.setId(id);
                    mappedStatement.setResultType(resultType);
                    mappedStatement.setParamterType(paramterType);
                    mappedStatement.setSql(sqlText);
                    mappedStatement.setTypeValue(name);
                    //封装key
                    String key = namespace+"."+id;
                    configuration.getMappedStatementMap().put(key,mappedStatement);
    
                }
    
            }
    
            //递归遍历当前节点所有的子节点
            List<Element> listElement=rootElement.elements();//所有一级子节点的list
            for(Element e:listElement){//遍历所有一级子节点
                this.getNodes(e);//递归
            }
        }
    }
    
    

    5.方法反射类

    public class DefaultSqlSession implements SqlSession {
    
        private Configuration configuration;
    
        public DefaultSqlSession(Configuration configuration) {
            this.configuration = configuration;
        }
    
        @Override
        public <E> List<E> selectList(String statementId, Object... param) throws Exception {
            //调用执行器
    
            //将要去完成对simpleExecutor里的query方法的调用
            simpleExecutor simpleExecutor = new simpleExecutor();
            //根据statementId获取Mapped
            MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
            List<Object> list = simpleExecutor.query(configuration, mappedStatement, param);
            return (List<E>) list;
        }
    
        @Override
        public <T> T selectOne(String statementId, Object... params) throws Exception {
            List<Object> objects = selectList(statementId, params);
            if (objects.size() == 1) {
                return (T) objects.get(0);
            } else {
                throw new RuntimeException("查询结果为空或者返回结果过多");
            }
        }
    
        @Override
        public int update(String statementId, Object... params) throws Exception {
            simpleExecutor simpleExecutor = new simpleExecutor();
            MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
            int update = simpleExecutor.update(configuration, mappedStatement, params);
            return update;
        }
    
        @Override
        public <T> T getMapper(Class<?> mapperClass) {
            // 使用JDK动态代理来为Dao接口生成代理对象,并返回
    //        loader: 用哪个类加载器去加载代理对象
    //        interfaces:动态代理类需要实现的接口
    //        h:动态代理方法在执行时,会调用h里面的invoke方法去执行
            Object proxyInstance = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
                //proxy 当前代理对象的应用
                //method 当前被调用方法的引用
                //args 传递的参数
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    // 底层都还是去执行JDBC代码 
                    //根据不同情况,来调用selctList或者selectOne
                    // 准备参数 1:statmentid :sql语句的唯一标识:namespace.id= 接口全限定名.方法名
                    // 方法名:findAll
                    String methodName = method.getName();
                    String className = method.getDeclaringClass().getName();
    
                    Object result = new Object();
    
                    String statementId = className + "." + methodName;
                    //com.test.dao.IUserDao.updateById
                    //com.test.dao.IUserDao.update
                    MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
                    //获取方法的标签值
                    String typeValue = mappedStatement.getTypeValue();
    
                    switch (typeValue) {
                        case "insert": {
    
                            result = update(statementId, args);
                            break;
                        }
                        case "update": {
    
                            result = update(statementId, args);
                            break;
                        }
                        case "delete": {
                            result = update(statementId, args);
                            break;
                        }
                        case "select": {
                            Type genericReturnType = method.getGenericReturnType();
                            // 判断是否进行了 泛型类型参数化
                            if (genericReturnType instanceof ParameterizedType) {
                                List<Object> objects = selectList(statementId, args);
                                result= objects;
                                break;
                            }else{
    
                                result= selectOne(statementId, args);
                                break;
                            }
                        }
                    }
                    return result;
                }
            });
            return (T) proxyInstance;
        }
    
    }
    
    

    6.JDbc执行类

    public class simpleExecutor implements  Executor {
    
        @Override                                                                                //user
        public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception {
            // 1\. 注册驱动,获取连接
            Connection connection = configuration.getDataSource().getConnection();
    
            // 2\. 获取sql语句 : select * from user where id = #{id} and username = #{username}
                //转换sql语句: select * from user where id = ? and username = ? ,转换的过程中,还需要对#{}里面的值进行解析存储
            String sql = mappedStatement.getSql();
            BoundSql boundSql = getBoundSql(sql);
    
            // 3.获取预处理对象:preparedStatement
            PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());
    
            // 4\. 设置参数
            //获取到了 参数的全路径
             String paramterType = mappedStatement.getParamterType();
             Class<?> paramtertypeClass = getClassType(paramterType);
    
            List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
            for (int i = 0; i < parameterMappingList.size(); i++) {
                ParameterMapping parameterMapping = parameterMappingList.get(i);
                String content = parameterMapping.getContent();
                //反射
                Field declaredField = paramtertypeClass.getDeclaredField(content);
                //暴力访问
                declaredField.setAccessible(true);
                Object o = declaredField.get(params[0]);
                preparedStatement.setObject(i+1,o);
            }
    
            // 5\. 执行sql
            ResultSet resultSet = preparedStatement.executeQuery();
            //获取返回值类
            String resultType = mappedStatement.getResultType();
            Class<?> resultTypeClass = getClassType(resultType);
    
            ArrayList<Object> objects = new ArrayList<>();
    
            // 6\. 封装返回结果集
            while (resultSet.next()){
                //反射new一个类
                Object o =resultTypeClass.newInstance();
                //元数据
                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;
    
        }
    
        @Override
        public int update(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception {
            //update user set username=#{} where id=#{}
            // 1\. 注册驱动,获取连接
            Connection connection = configuration.getDataSource().getConnection();
    
            // 2\. 获取sql语句 : select * from user where id = #{id} and username = #{username}
            //转换sql语句: select * from user where id = ? and username = ? ,转换的过程中,还需要对#{}里面的值进行解析存储
            String sql = mappedStatement.getSql();
            BoundSql boundSql = getBoundSql(sql);
    
            // 3.获取预处理对象:preparedStatement
            PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());
    
            // 4\. 设置参数
            //获取到了 参数的全路径
            String paramterType = mappedStatement.getParamterType();
            Class<?> paramtertypeClass = getClassType(paramterType);
    
            List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
            for (int i = 0; i < parameterMappingList.size(); i++) {
                ParameterMapping parameterMapping = parameterMappingList.get(i);
                String content = parameterMapping.getContent();
                Object o =null;
                if(mappedStatement.getTypeValue().equals("delete")){
                    o= params[i];
                }else{
                    //反射
                    Field declaredField = paramtertypeClass.getDeclaredField(content);
                    //暴力访问
                    declaredField.setAccessible(true);
                    o = declaredField.get(params[0]);
                }
    
                preparedStatement.setObject(i+1,o);
    
            }
    
            // 5\. 执行sql
            int i1 = preparedStatement.executeUpdate();
    
            return i1;
        }
    
        private Class<?> getClassType(String paramterType) throws ClassNotFoundException {
            if(paramterType!=null){
                Class<?> aClass = Class.forName(paramterType);
                return aClass;
            }
             return null;
    
        }
    
        /**
         * 完成对#{}的解析工作:1.将#{}使用?进行代替,2.解析出#{}里面的值进行存储
         * @param sql
         * @return
         */
        private BoundSql getBoundSql(String sql) {
            //标记处理类:配置标记解析器来完成对占位符的解析处理工作
            ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
            GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
            //解析出来的sql
            String parseSql = genericTokenParser.parse(sql);
            //#{}里面解析出来的参数名称
            List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();
    
            BoundSql boundSql = new BoundSql(parseSql,parameterMappings);
            return boundSql;
    
        }
    }
    
    

    7.测试类

    @Test
    public void testProxyDao() throws IOException {
     InputStream resourceAsStream =
    Resources.getResourceAsStream("SqlMapConfig.xml");
     SqlSessionFactory sqlSessionFactory = new
    SqlSessionFactoryBuilder().build(resourceAsStream);
     SqlSession sqlSession = sqlSessionFactory.openSession();
    
     UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
     User user = userMapper.findById(1);
     System.out.println(user);
     sqlSession.close();
    }
    
    

    2.2.3打jar问题

    1.在pom文件中添加编译属性

    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
            <java.version>1.8</java.version>
            <maven.compiler.source>1.8</maven.compiler.source>
            <maven.compiler.target>1.8</maven.compiler.target>
        </properties>
    
    

    2.idea中点击 projrct Structure =>点击module 设置项目的编译版本
    3.srtting=> builder=>compiler=>java compiler设置编译版本

    3.mybatis快速启动

    3.1导依赖

        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
            <java.version>1.8</java.version>
            <maven.compiler.source>1.8</maven.compiler.source>
            <maven.compiler.target>1.8</maven.compiler.target>
        </properties>
    
        <!--引入依赖-->
        <dependencies>
            <!--mybatis坐标-->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.4.5</version>
            </dependency>
            <!--mysql驱动坐标-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.6</version>
                <scope>runtime</scope>
            </dependency>
            <!--单元测试坐标-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
            </dependency>
        </dependencies>
    
    

    4 自定义插件

    xml

      <plugins>
            <plugin interceptor="com.lagou.plugin.MyPlugin">
                <property name="name" value="Bob"/>
            </plugin>
        </plugins>
    
    

    java

    package com.lagou.plugin;
    
    import org.apache.ibatis.executor.statement.StatementHandler;
    import org.apache.ibatis.plugin.*;
    
    import java.sql.Connection;
    import java.util.Properties;
    
        @Intercepts({
                @Signature(type= StatementHandler.class,//拦截那个接口
                        method = "prepare",//这个接口内的那个方法名
                        args={Connection.class,Integer.class})//拦截方法的入参,如果方法重载,通过方法名跟参数确定唯一
        })
        public class MyPlugin implements Interceptor {
            //这里是每次执行操作的时候,都会进行这个方法
            @Override
            public Object intercept(Invocation invocation) throws Throwable {
                System.out.println("增强了");
                return invocation.proceed();
            }
    
            //把这个拦截器生成一个代理放到拦截器链中
            @Override
            public Object plugin(Object target) {
                System.out.println("将要包装的目标对象"+target);
                return Plugin.wrap(target,this);
            }
            //插件初始化时候调用,只调用一次  获取配置文件的属性
            @Override
            public void setProperties(Properties properties) {
                System.out.println("获取配置文件的属性"+properties);
            }
        }
    
    

    mybatis 拦截的类

    一、executor,executor类可以说是执行sql的全过程,如组装参数,sql改造,结果处理,比较广泛,但实际用的不多

    二、StatementHandler,这个是执行sql的过程,可以获取到待执行的sql,可用来改造sql,如分页,分表,最常拦截的类

    三、paremeterHandler,这个用来拦截sql的参数,可以自定义参数组装规则

    四、resultHandler,这个用来处理结果

    5 缓存

    一级缓存

    二级缓存

    二级缓存是序列化存储 需要实现POJO需要实现Serializable接口

    <settings>
      <setting name="cacheEnabled" value="true" />
    </settings>
    //在sql的mapper的添加 <cache/>表填
    
    

    问题

    1.缓存使用顺序

    先到二级缓存当中查找
    如果二级缓存中没有,就去找一级缓存
    如果一级缓存中也没有就去到数据库当中查询
    
    

    2.二级Chache使用原则

    1.只能在一个命名空间下使用二级缓存
    由于二级缓存中的数据是基于namespace的,即不同namespace中的数据互不干扰。在多个namespace中若均存在对同一个表的操作,那么这多个namespace中的数据可能就会出现不一致现象。
    2.在单表上使用二级缓存
    如果一个表与其它表有关联关系,那么久非常有可能存在多个namespace对同一数据的操作。而不同namespace中的数据互补干扰,所以就有可能出现多个namespace中的数据不一致现象。
    3.查询多于修改时使用二级缓存
    在查询操作远远多于增删改操作的情况下可以使用二级缓存。因为任何增删改操作都将刷新二级缓存,对二级缓存的频繁刷新将降低系统性能。

    java补充

    1.java类型 T,E,K,V,?

    泛型中的通配符
    
    ?表示不确定的 java 类型
    ?无界通配符
    例子 List<? extends Animal> listAnimals
    
    T (type) 表示具体的一个java类型
     List<T>是确定的某一个类型
    
    K V (key value) 分别代表java键值中的Key Value
    
    E (element) 代表Element
    上界通配符 < ? extends E>
    下界通配符 < ? super E>
    
    T和?运用的地方有点不同,
        ? 是定义在引用变量上
        T 是类上或方法上
    
    

    2. List<T.?.Object>区别

    ArrayList<T> arrayList = new ArrayList<T>(); 指定集合元素类型只可以是T类型
    ArrayList<?>arrayList = new ArrayList<?>(); 集合元素可以是任意类型,一般是方法中为了说明方法
    ArrayList<? extends E> arrayList = new ArrayList<? extends E>();泛型的限定:? extends E:接受E类型或者E的子类型、? super E :接受E类型或者E的父类型
    
    

    3.java的内省与反射

    //https://www.cnblogs.com/winclpt/articles/7405271.html
    
    1.内省是先得到属性描述器PropertyDecriptor后再进行各种操作,内省(Introspector)是Java语言对JavaBean类属性、事件的处理方法
    2.反射则是先得到类的字节码Class后再进行各种操作的。对任意一个类,能够获取得到这个类的所有属性和方法;
    代码:
     反射:
         Field f = user.getClass().getDeclaredField("name");
         f.setAccessible(true);
         f.set(user, "mld");//设置属性值
     内省
         //操作单个属性
         PropertyDescriptor pd = new PropertyDescriptor("name", User.class);
         Method w = pd.getWriteMethod();//获取属性的setter方法
         w.invoke(user, "winclpt");
    
    

    4.java的代理

    1.静态代理

    静态代理:由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成
    
    

    2.动态代理

    实现InvocationHandler接口
    动态代理就是要生成一个包装类对象,由于代理的对象是动态的,所以叫动态代理。由于我们需要增强,这个增强是需要留给开发人员开发代码的,因此代理类不能直接包含被代理对象,而是一个InvocationHandler,该InvocationHandler包含被代理对象,并负责分发请求给被代理对象,分发前后均可以做增强。从原理可以看出,JDK动态代理是“对象”的代理。
    原文链接:https://blog.csdn.net/flyfeifei66/article/details/81481222 **

           // 使用JDK动态代理来为Dao接口生成代理对象,并返回
    //        loader: 用哪个类加载器去加载代理对象
    //
    //        interfaces:动态代理类需要实现的接口
    //
    //        h:动态代理方法在执行时,会调用h里面的invoke方法去执行
            Object proxyInstance = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
                //proxy 当前代理对象的应用
                //method 当前被调用方法的引用
                //args 传递的参数
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    // 底层都还是去执行JDBC代码 //根据不同情况,来调用selctList或者selectOne
                    // 准备参数 1:statmentid :sql语句的唯一标识:namespace.id= 接口全限定名.方法名
                    // 方法名:findAll
                    String methodName = method.getName();
                    String className = method.getDeclaringClass().getName();
    
                    String statementId = className+"."+methodName;
    
                    // 准备参数2:params:args
                    // 获取被调用方法的返回值类型
                    Type genericReturnType = method.getGenericReturnType();
                    // 判断是否进行了 泛型类型参数化
                    if(genericReturnType instanceof ParameterizedType){
                        List<Object> objects = selectList(statementId, args);
                        return objects;
                    }
    
                    return selectOne(statementId,args);
    
                }
            });
    
            return (T) proxyInstance;
        }
    
    

    5.newInstance与new的区别

    new关键字能调用任何构造方法。
    newInstance()只能调用无参构造方法。
    newInstance()构造对象的地方通过new关键字也可以创建对象.
    在使用newInstance()方法的时候,必须保证这个类已经加载并且已经连接
    适用:
    使用newInstance()在通用性方面比较高,className我们可以用配置文件进行相关的配置。
    String className = 从配置文件中读取className;
    A a = (A) Class.forName(className).newInstance();
    再配合依赖注入的方法,就提高了软件的可伸缩性、可扩展性。框架的开发中用的比较多!

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
            String str = (String) Class.forName("java.lang.String").newInstance();
            String str1 = new String();
            if(str.getClass() == str1.getClass()){
                System.out.println("YES");
            }
        }
    
    output:YES
    //原文链接:https://blog.csdn.net/qq_33704186/java/article/details/86596614
    
    

    单词

    statement 声明,陈述
    bound 一定会,边界
    Declaring 公然地,生命
    Accessible 可进入的
    descrip 描述
    relation 关系
    example例子
    Generic通用的
    Security 安全的
    Multiple 多样的
    extract提取
    wrap包
    signature签名
    Additional附加的 额外的

    作业

    简答题

    一.

    1.Mybatis动态sql是做什么的?

    1.动态sql就是 根据条件标签动态的拼接sql,包括判空,循环,拼接等

    2.哪些动态sql?

    动态sql大致有
    1.: if是为了判断传入的值是否符合某种规则,比如是否不为空;
    2.:where标签可以用来做动态拼接查询条件,当和if标签配合的时候,不用显示的声明类似where 1=1这种无用的条件
    3.:foreach标签可以把传入的集合对象进行遍历,然后把每一项的内容作为参数传到sql语句中,
    4.:include可以把大量重复的代码整理起来,当使用的时候直接include即可,减少重复代码的编写;
    5.:是一个格式化标签
    6.choose、when、otherwise 标签类似于 Java 中的 switch、case、default。只有一个条件生效,

    <select id="findOneById"  resultType="com.lagou.pojo.User">
      select <include refid="userInfo"/> from user
      <where>
            <if test="id != null and id != 0">
              AND id = #{id}
            </if>
      <foreach collection="list" item="id" open="(" close=")" separator="," >
        #{id}
      </foreach>
         <trim prefix="where" suffix="order by id" prefixOverrides="and | or" suffixOverrides=",">
            <if test="name != null and name != ''">
              AND name = #{name}
            </if>
            <if test="id != null">
              AND id = #{id}
            </if>
          </trim>
      </where>
    </select>
    
    

    3. 简述一下动态sql的执行原理?

    1.在xmlMapperBuilder中 解析配置文件时

    2.解析 <mapper /> 节点

    3.解析 节点

        // 1.在xmlMapperBuilder中 解析配置文件时
        public void parse() {
                // 解析 `<mapper />` 节点
                configurationElement(parser.evalNode("/mapper"));
        }
        //2\. 解析 `<mapper />` 节点
        private void configurationElement(XNode context) {
            try {
                // 获得 namespace 属性
                String namespace = context.getStringAttribute("namespace");
                if (namespace == null || namespace.equals("")) {
                    throw new BuilderException("Mapper's namespace cannot be empty");
                }
                // 设置 namespace 属性
                builderAssistant.setCurrentNamespace(namespace);
                // 解析 <resultMap /> 节点们
                resultMapElements(context.evalNodes("/mapper/resultMap"));
                // 解析 <sql /> 节点们
                sqlElement(context.evalNodes("/mapper/sql"));
                // 解析 <select /> <insert /> <update /> <delete /> 节点们
                buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
            } catch (Exception e) {
                throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
            }
        }
      // 3.解析 <select /> <insert /> <update /> <delete /> 节点们
        private void buildStatementFromContext(List<XNode> list) {
            if (configuration.getDatabaseId() != null) {
                buildStatementFromContext(list, configuration.getDatabaseId());
            }
            buildStatementFromContext(list, null);
            // 上面两块代码,可以简写成 buildStatementFromContext(list, configuration.getDatabaseId());
        }
        private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
            //遍历 <select /> <insert /> <update /> <delete /> 节点们
            for (XNode context : list) {
                // 创建 XMLStatementBuilder 对象,执行解析
                final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
                try {
                    statementParser.parseStatementNode();
                } catch (IncompleteElementException e) {
                    // 解析失败,添加到 configuration 中
                    configuration.addIncompleteStatement(statementParser);
                }
            }
        }
    
    

    4.XMLStatementBuilder对象

    public class XMLStatementBuilder extends BaseBuilder {
       /**
         * 执行解析
         */
        public void parseStatementNode() {
            // 获得 id 属性,编号。
            String id = context.getStringAttribute("id");
            // 获得 databaseId , 判断 databaseId 是否匹配
            String databaseId = context.getStringAttribute("databaseId");
            if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
                return;
            }
    
            // 获得各种属性
            Integer fetchSize = context.getIntAttribute("fetchSize");
            Integer timeout = context.getIntAttribute("timeout");
            String parameterMap = context.getStringAttribute("parameterMap");
            String parameterType = context.getStringAttribute("parameterType");
            Class<?> parameterTypeClass = resolveClass(parameterType);
            String resultMap = context.getStringAttribute("resultMap");
            String resultType = context.getStringAttribute("resultType");
            String lang = context.getStringAttribute("lang");
    
            // 获得 lang 对应的 LanguageDriver 对象
            LanguageDriver langDriver = getLanguageDriver(lang);
    
            // 获得 resultType 对应的类
            Class<?> resultTypeClass = resolveClass(resultType);
            // 获得 resultSet 对应的枚举值
            String resultSetType = context.getStringAttribute("resultSetType");
            ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
            // 获得 statementType 对应的枚举值
            StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
    
    }
    
    

    5.LanguageDriver的实现类XMLLanguageDriver的方法解析方法createSqlSource

    public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
            // 创建 XMLScriptBuilder 对象,执行解析
            XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
            return builder.parseScriptNode();
        }
    
    

    6.解析

    XMLScriptBuilder标签解析
    /**
     * XML 动态语句( SQL )构建器,负责将 SQL 解析成 SqlSource 对象
     *
     * @author Clinton Begin
     */
    public class XMLScriptBuilder extends BaseBuilder {
    
        /**
         * 当前 SQL 的 XNode 对象
         */
        private final XNode context;
        /**
         * 是否为动态 SQL
         */
        private boolean isDynamic;
        /**
         * SQL 方法类型
         */
        private final Class<?> parameterType;
        /**
         * NodeNodeHandler 的映射
         */
        private final Map<String, NodeHandler> nodeHandlerMap = new HashMap<>();
    
        public XMLScriptBuilder(Configuration configuration, XNode context) {
            this(configuration, context, null);
        }
    
        public XMLScriptBuilder(Configuration configuration, XNode context, Class<?> parameterType) {
            super(configuration);
            this.context = context;
            this.parameterType = parameterType;
            // 初始化 nodeHandlerMap 属性
            initNodeHandlerMap();
        }
    
        /**
         * 初始化 {@link #nodeHandlerMap} 属性
         */
        private void initNodeHandlerMap() {
            nodeHandlerMap.put("trim", new TrimHandler());
            nodeHandlerMap.put("where", new WhereHandler());
            nodeHandlerMap.put("set", new SetHandler());
            nodeHandlerMap.put("foreach", new ForEachHandler());
            nodeHandlerMap.put("if", new IfHandler());
            nodeHandlerMap.put("choose", new ChooseHandler());
            nodeHandlerMap.put("when", new IfHandler());
            nodeHandlerMap.put("otherwise", new OtherwiseHandler());
            nodeHandlerMap.put("bind", new BindHandler());
        }
    
        /**
         * 负责将 SQL 解析成 SqlSource 对象
         *
         * @return SqlSource 对象
         */
        public SqlSource parseScriptNode() {
            // 解析 SQL
            MixedSqlNode rootSqlNode = parseDynamicTags(context);
            // 创建 SqlSource 对象
            SqlSource sqlSource;
            if (isDynamic) {
                sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
            } else {
                sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
            }
            return sqlSource;
        }
    
        /**
         * 解析 SQL 成 MixedSqlNode 对象
         *
         * @param node XNode 节点
         * @return MixedSqlNode
         */
        protected MixedSqlNode parseDynamicTags(XNode node) {
            // 创建 SqlNode 数组
            List<SqlNode> contents = new ArrayList<>();
            // 遍历 SQL 节点的所有子节点
            NodeList children = node.getNode().getChildNodes();
            for (int i = 0; i < children.getLength(); i++) {
                // 当前子节点
                XNode child = node.newXNode(children.item(i));
                // 如果类型是 Node.CDATA_SECTION_NODE 或者 Node.TEXT_NODE 时
                if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
                    // 获得内容
                    String data = child.getStringBody("");
                    // 创建 TextSqlNode 对象
                    TextSqlNode textSqlNode = new TextSqlNode(data);
                    // 如果是动态的 TextSqlNode 对象
                    if (textSqlNode.isDynamic()) {
                        // 添加到 contents 中
                        contents.add(textSqlNode);
                        // 标记为动态 SQL
                        isDynamic = true;
                    // 如果是非动态的 TextSqlNode 对象
                    } else {
                        // 创建 StaticTextSqlNode 添加到 contents 中
                        contents.add(new StaticTextSqlNode(data));
                    }
                // 如果类型是 Node.ELEMENT_NODE
                } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
                    // 根据子节点的标签,获得对应的 NodeHandler 对象
                    String nodeName = child.getNode().getNodeName();
                    NodeHandler handler = nodeHandlerMap.get(nodeName);
                    if (handler == null) { // 获得不到,说明是未知的标签,抛出 BuilderException 异常
                        throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
                    }
                    // 执行 NodeHandler 处理
                    handler.handleNode(child, contents);
                    // 标记为动态 SQL
                    isDynamic = true;
                }
            }
            // 创建 MixedSqlNode 对象
            return new MixedSqlNode(contents);
        }
    
        /**
         * Node 处理器接口
         */
        private interface NodeHandler {
    
            /**
             * 处理 Node
             *
             * @param nodeToHandle 要处理的 XNode 节点
             * @param targetContents 目标的 SqlNode 数组。实际上,被处理的 XNode 节点会创建成对应的 SqlNode 对象,添加到 targetContents 中
             */
            void handleNode(XNode nodeToHandle, List<SqlNode> targetContents);
    
        }
    
        /**
         * `<bind />` 标签的处理器
         *
         * @see VarDeclSqlNode
         */
        private class BindHandler implements NodeHandler {
    
            public BindHandler() {
                // Prevent Synthetic Access
            }
    
            @Override
            public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
                // 解析 name、value 属性
                final String name = nodeToHandle.getStringAttribute("name");
                final String expression = nodeToHandle.getStringAttribute("value");
                // 创建 VarDeclSqlNode 对象
                final VarDeclSqlNode node = new VarDeclSqlNode(name, expression);
                // 添加到 targetContents 中
                targetContents.add(node);
            }
        }
    
        /**
         * `<trim />` 标签的处理器
         *
         * @see TrimSqlNode
         */
        private class TrimHandler implements NodeHandler {
    
            public TrimHandler() {
                // Prevent Synthetic Access
            }
    
            @Override
            public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
                // 解析内部的 SQL 节点,成 MixedSqlNode 对象
                MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
                // 获得 prefix、prefixOverrides、"suffix"、suffixOverrides 属性
                String prefix = nodeToHandle.getStringAttribute("prefix");
                String prefixOverrides = nodeToHandle.getStringAttribute("prefixOverrides");
                String suffix = nodeToHandle.getStringAttribute("suffix");
                String suffixOverrides = nodeToHandle.getStringAttribute("suffixOverrides");
                // 创建 TrimSqlNode 对象
                TrimSqlNode trim = new TrimSqlNode(configuration, mixedSqlNode, prefix, prefixOverrides, suffix, suffixOverrides);
                // 添加到 targetContents 中
                targetContents.add(trim);
            }
    
        }
    
        /**
         * `<where />` 标签的处理器
         *
         * @see WhereSqlNode
         */
        private class WhereHandler implements NodeHandler {
    
            public WhereHandler() {
                // Prevent Synthetic Access
            }
    
            @Override
            public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
                // 解析内部的 SQL 节点,成 MixedSqlNode 对象
                MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
                // 创建 WhereSqlNode 对象
                WhereSqlNode where = new WhereSqlNode(configuration, mixedSqlNode);
                // 添加到 targetContents 中
                targetContents.add(where);
            }
    
        }
    
        /**
         * `<set />` 标签的处理器
         *
         * @see SetSqlNode
         */
        private class SetHandler implements NodeHandler {
    
            public SetHandler() {
                // Prevent Synthetic Access
            }
    
            @Override
            public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
                // 解析内部的 SQL 节点,成 MixedSqlNode 对象
                MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
                // 创建 SetSqlNode 对象
                SetSqlNode set = new SetSqlNode(configuration, mixedSqlNode);
                // 添加到 targetContents 中
                targetContents.add(set);
            }
    
        }
    
        /**
         * `<foreach />` 标签的处理器
         *
         * @see ForEachSqlNode
         */
        private class ForEachHandler implements NodeHandler {
    
            public ForEachHandler() {
                // Prevent Synthetic Access
            }
    
            @Override
            public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
                // 解析内部的 SQL 节点,成 MixedSqlNode 对象
                MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
                // 获得 collection、item、index、open、close、separator 属性
                String collection = nodeToHandle.getStringAttribute("collection");
                String item = nodeToHandle.getStringAttribute("item");
                String index = nodeToHandle.getStringAttribute("index");
                String open = nodeToHandle.getStringAttribute("open");
                String close = nodeToHandle.getStringAttribute("close");
                String separator = nodeToHandle.getStringAttribute("separator");
                // 创建 ForEachSqlNode 对象
                ForEachSqlNode forEachSqlNode = new ForEachSqlNode(configuration, mixedSqlNode, collection, index, item, open, close, separator);
                // 添加到 targetContents 中
                targetContents.add(forEachSqlNode);
            }
    
        }
    
        /**
         * `<if />` 标签的处理器
         *
         * @see IfSqlNode
         */
        private class IfHandler implements NodeHandler {
    
            public IfHandler() {
                // Prevent Synthetic Access
            }
    
            @Override
            public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
                // 解析内部的 SQL 节点,成 MixedSqlNode 对象
                MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
                // 获得 test 属性
                String test = nodeToHandle.getStringAttribute("test");
                // 创建 IfSqlNode 对象
                IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test);
                // 添加到 targetContents 中
                targetContents.add(ifSqlNode);
            }
    
        }
    
        /**
         * `<otherwise />` 标签的处理器
         */
        private class OtherwiseHandler implements NodeHandler {
    
            public OtherwiseHandler() {
                // Prevent Synthetic Access
            }
    
            @Override
            public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
                // 解析内部的 SQL 节点,成 MixedSqlNode 对象
                MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
                // 添加到 targetContents 中
                targetContents.add(mixedSqlNode);
            }
    
        }
    
        /**
         * `<choose />` 标签的处理器
         *
         * @see ChooseSqlNode
         */
        private class ChooseHandler implements NodeHandler {
    
            public ChooseHandler() {
                // Prevent Synthetic Access
            }
    
            @Override
            public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
                List<SqlNode> whenSqlNodes = new ArrayList<>();
                List<SqlNode> otherwiseSqlNodes = new ArrayList<>();
                // 解析 `<when />` 和 `<otherwise />` 的节点们
                handleWhenOtherwiseNodes(nodeToHandle, whenSqlNodes, otherwiseSqlNodes);
                // 获得 `<otherwise />` 的节点
                SqlNode defaultSqlNode = getDefaultSqlNode(otherwiseSqlNodes);
                // 创建 ChooseSqlNode 对象
                ChooseSqlNode chooseSqlNode = new ChooseSqlNode(whenSqlNodes, defaultSqlNode);
                // 添加到 targetContents 中
                targetContents.add(chooseSqlNode);
            }
    
            private void handleWhenOtherwiseNodes(XNode chooseSqlNode, List<SqlNode> ifSqlNodes, List<SqlNode> defaultSqlNodes) {
                List<XNode> children = chooseSqlNode.getChildren();
                for (XNode child : children) {
                    String nodeName = child.getNode().getNodeName();
                    NodeHandler handler = nodeHandlerMap.get(nodeName);
                    if (handler instanceof IfHandler) { // 处理 `<when />` 标签的情况
                        handler.handleNode(child, ifSqlNodes);
                    } else if (handler instanceof OtherwiseHandler) { // 处理 `<otherwise />` 标签的情况
                        handler.handleNode(child, defaultSqlNodes);
                    }
                }
            }
    
            // 至多允许有一个 SqlNode 节点
            private SqlNode getDefaultSqlNode(List<SqlNode> defaultSqlNodes) {
                SqlNode defaultSqlNode = null;
                if (defaultSqlNodes.size() == 1) {
                    defaultSqlNode = defaultSqlNodes.get(0);
                } else if (defaultSqlNodes.size() > 1) {
                    throw new BuilderException("Too many default (otherwise) elements in choose statement.");
                }
                return defaultSqlNode;
            }
        }
    
    }
    
    

    二、

    Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?

    延迟加载主要通过动态代理实现,通过代理拦截指定方法没执行数据加载。

    javassisProxyFactory会创建一个User代理对象,所有调用User对象方法,都会经过EnhancedResultObjectProxyImpl.invoke()方法的拦截。、

    于是当调用User.getOrder()方法时,才真正去执行查询Order的动作并把结果赋值

    <settings>
        <!-- 开启全局配置的懒加载 -->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!-- 关闭积极加载 -->
        <setting name="aggressiveLazyLoading" value="false"/>    
    </settings>
    <mapper namespace="demo.cyj.dao.TeacherDao">
        <select id="findTeacherByTname" resultType="Teacher" resultMap="getTeacher">
            select t.t_id t_id,t.t_name t_name from teacher t where t.t_name =#{name}
        </select>
        //延迟加载
        <resultMap type="Teacher" id="getTeacher">
            <collection ofType="Student" property="stuList" fetchType="lazy" column="t_name" select="findTeacherByTnameLazy" />
        </resultMap>
    
        <select id="findTeacherByTnameLazy" resultType="Student" >
            select s.id id,s.stu_name stu_name,s.stu_age age,s.t_id t_id from student s left join teacher t on t.t_id = s.t_id where t.t_name=#{name} 
        </select>       
    </mapper>
    
    

    2.延迟加载的类

    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
        if (closed) {
          throw new ExecutorException("Executor was closed.");
        }
        if (queryStack == 0 && ms.isFlushCacheRequired()) {
          clearLocalCache();
        }
        List<E> list;
        try {
          queryStack++;
          list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
          if (list != null) {
            handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
          } else {
            list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
          }
        } finally {
          queryStack--;
        }
        if (queryStack == 0) {
          for (DeferredLoad deferredLoad : deferredLoads) {
            deferredLoad.load();
          }
          // issue #601
          deferredLoads.clear();
          if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
            // issue #482
            clearLocalCache();
          }
        }
        return list;
      }
    
    

    延迟加载的类

      private static class DeferredLoad {
    
        private final MetaObject resultObject;
        private final String property;
        private final Class<?> targetType;
        private final CacheKey key;
        private final PerpetualCache localCache;
        private final ObjectFactory objectFactory;
        private final ResultExtractor resultExtractor;
    
        // issue #781
        public DeferredLoad(MetaObject resultObject,
                            String property,
                            CacheKey key,
                            PerpetualCache localCache,
                            Configuration configuration,
                            Class<?> targetType) {
          this.resultObject = resultObject;
          this.property = property;
          this.key = key;
          this.localCache = localCache;
          this.objectFactory = configuration.getObjectFactory();
          this.resultExtractor = new ResultExtractor(configuration, objectFactory);
          this.targetType = targetType;
        }
    
        public boolean canLoad() {
          return localCache.getObject(key) != null && localCache.getObject(key) != EXECUTION_PLACEHOLDER;
        }
    
        public void load() {
          @SuppressWarnings( "unchecked" )
          // we suppose we get back a List
          List<Object> list = (List<Object>) localCache.getObject(key);
          Object value = resultExtractor.extractObjectFromList(list, targetType);
          resultObject.setValue(property, value);
        }
    
      }
    
    

    1.Mybatis都有哪些Executor执行器

    BaseExecutor 简单执行器,是 MyBatis 中默认使用的执行器,每执行一次 update 或 select,就开启一个 Statement 对象,用完就直接关闭 Statement 对象(可以是 Statement 或者是 PreparedStatment 对象)
    BatchExecutor 批处理执行器,用于将多个SQL一次性输出到数据库
    simpleExexutor 每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。
    ReuseExecutor 执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map内,供下一次使用。简言之,就是重复使用Statement对象。

    cachingExecutor 更新缓存

    2.它们之间的区别是什么?

    作用范围:Executor的这些特点,都严格限制在SqlSession生命周期范围内。

    默认是SimplExcutor,需要配置在创建SqlSession对象的时候指定执行器的类型即可。

    1.一级缓存

    Mybatis的一级缓存是指SqlSession级别的,作用域是SqlSession,Mybatis默认开启一级缓存,在同一个SqlSession中,相同的Sql查询的时候,第一次查询的时候,就会从缓存中取,如果发现没有数据,那么就从数据库查询出来,并且缓存到HashMap中,如果下次还是相同的查询,就直接从缓存中查询,就不在去查询数据库,对应的就不在去执行SQL语句。当查询到的数据,进行增删改的操作的时候,缓存将会失效

    2. 二级缓存

    MyBatis的二级缓存是基于Mapper级别的,也就是说多个SqlSession去使用某个Mapper的查询语句时,得到的缓存数据是可共用的。第一次调用mapper下的sql 的时候去查询信息,查询到的信息会存放到该mapper对应的二级缓存区域,第二次调用namespace下的mapper映射文件中,相同的SQL去查询,回去对应的二级缓存内取结果。二级缓存开启后,查询就会走二级缓存,没查到直接查库。MyBatis默认不开启二级缓存

    简述Mybatis的插件运行原理,以及如何编写一个插件?

    插件运行原理

    实现Mybatis的Interceptor接口并复写intercept()方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可

    实现Interceptor接口 在定义StatementHandler处理器的时候拦截prepare方法也就是准备的方法

    //1.configuration.newStatementHandler()获取对象
      public int doUpdate(MappedStatement ms, Object parameter) throws  SQLException {
        Statement stmt = null;
          Configuration configuration = ms.getConfiguration();
          //定义StatementHandler处理器
          StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
          //初始化
          stmt = prepareStatement(handler, ms.getStatementLog());
          //执行
          return handler.update(stmt);
    
      }
      //2.获取执行sql的StatementHandler组件
        public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
        //代理对象
        statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
        return statementHandler;
      }
      //3.
      public class InterceptorChain {
    
      private final List<Interceptor> interceptors = new ArrayList<Interceptor>();
    
      public Object pluginAll(Object target) {
        for (Interceptor interceptor : interceptors) {
          target = interceptor.plugin(target);
        }
        return target;
      }
      }
    
    

    如何编写插件

    实现Interceptor接口,Interceptors注解表明要拦截的类,方法,参数

    package com.lagou.plugin;
    
    import org.apache.ibatis.executor.statement.StatementHandler;
    import org.apache.ibatis.plugin.*;
    
    import java.sql.Connection;
    import java.util.Properties;
    
        @Intercepts({
                @Signature(type= StatementHandler.class,//拦截那个接口
                        method = "prepare",//这个接口内的那个方法名
                        args={Connection.class,Integer.class})//拦截方法的入参,如果方法重载,通过方法名跟参数确定唯一
        })
        public class MyPlugin implements Interceptor {
            //这里是每次执行操作的时候,都会进行这个方法
            @Override
            public Object intercept(Invocation invocation) throws Throwable {
                System.out.println("增强了");
                return invocation.proceed();
            }
    
            //把这个拦截器生成一个代理放到拦截器链中
            @Override
            public Object plugin(Object target) {
                System.out.println("将要包装的目标对象"+target);
                return Plugin.wrap(target,this);
            }
            //插件初始化时候调用,只调用一次  获取配置文件的属性
            @Override
            public void setProperties(Properties properties) {
                System.out.println("获取配置文件的属性"+properties);
            }
        }
    
    tementHandler);
        return statementHandler;
      }
      //3.
      public class InterceptorChain {
    
      private final List<Interceptor> interceptors = new ArrayList<Interceptor>();
    
      public Object pluginAll(Object target) {
        for (Interceptor interceptor : interceptors) {
          target = interceptor.plugin(target);
        }
        return target;
      }
      }
    
    

    如何编写插件

    实现Interceptor接口,Interceptors注解表明要拦截的类,方法,参数

    package com.lagou.plugin;
    
    import org.apache.ibatis.executor.statement.StatementHandler;
    import org.apache.ibatis.plugin.*;
    
    import java.sql.Connection;
    import java.util.Properties;
    
        @Intercepts({
                @Signature(type= StatementHandler.class,//拦截那个接口
                        method = "prepare",//这个接口内的那个方法名
                        args={Connection.class,Integer.class})//拦截方法的入参,如果方法重载,通过方法名跟参数确定唯一
        })
        public class MyPlugin implements Interceptor {
            //这里是每次执行操作的时候,都会进行这个方法
            @Override
            public Object intercept(Invocation invocation) throws Throwable {
                System.out.println("增强了");
                return invocation.proceed();
            }
    
            //把这个拦截器生成一个代理放到拦截器链中
            @Override
            public Object plugin(Object target) {
                System.out.println("将要包装的目标对象"+target);
                return Plugin.wrap(target,this);
            }
            //插件初始化时候调用,只调用一次  获取配置文件的属性
            @Override
            public void setProperties(Properties properties) {
                System.out.println("获取配置文件的属性"+properties);
            }
        }
    
    

    结尾

    本文到这里就结束了,感谢看到最后的朋友,都看到最后了,点个赞再走啊,如有不对之处还请多多指正。

    相关文章

      网友评论

        本文标题:源码系列——mybatis源码刨析总结

        本文链接:https://www.haomeiwen.com/subject/scqmyktx.html