mybatis源码刨析总结

拉勾 mybatis

初始化

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对象。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LX6M9gvA-1597914279840)(C:\Users\gaoyuan\Desktop\simpleExexutor.png)]

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);
        }
    }



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值