源碼系列——mybatis源碼刨析總結(jié)

初始化

1.創(chuàng)建git倉(cāng)庫(kù)

1.新建一個(gè)目錄 然后點(diǎn)擊右鍵 git base here 創(chuàng)建git (會(huì)彈出一個(gè)窗口)
2.初始化 再窗口輸入 git init
3.指定倉(cāng)庫(kù) git clone 倉(cāng)庫(kù)地址
4.上傳文件 點(diǎn)擊右鍵 git提交->master (選擇提交并推送)

1. jdbc連接數(shù)據(jù)庫(kù)的缺陷

1.1 代碼

  public static void main(String[] args) {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        try {
            //加載數(shù)據(jù)庫(kù)驅(qū)動(dòng)
            Class.forName("com.mysql.jdbc.Driver");
            //通過(guò)驅(qū)動(dòng)管理類獲取數(shù)據(jù)庫(kù)連接
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/lgstudy?characterEncoding=utf-8", "root", "root");
            //---有參數(shù)
//            //?占位符
//            String sql = "select * from user where username = ?";
//            //獲取預(yù)處理statement
//            preparedStatement = connection.prepareStatement(sql);
//            // 設(shè)置參數(shù)
//            preparedStatement.setString(1, "占山");
            //---無(wú)參數(shù)
            String sql = "select * from user";
            preparedStatement = connection.prepareStatement(sql);
            // 執(zhí)行sql查詢結(jié)果集
            resultSet = preparedStatement.executeQuery();
            //遍歷結(jié)果集
            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.連接數(shù)據(jù)庫(kù)的配置信息硬編碼  ===》存到配置文件中
2.數(shù)據(jù)庫(kù)的頻繁斷開(kāi)連接==》連接池
3.sql語(yǔ)句,設(shè)置參數(shù) 硬編碼 ==》存到配置文件中 設(shè)置參數(shù) 反射
4.手動(dòng)遍歷封裝結(jié)果集  ==》 內(nèi)省

2. 自定義持久層框架

2.1 思路

2.1.1使用端

1.引入自定義持久層的包
2.提供兩部分配置信息:
    1.數(shù)據(jù)庫(kù)的連接配置信息 sql配置信息
    2.mapper.xml 存放sql配置信息

2.1.2自定義持久層框架(封裝jdbc的操作)

1.加載配置文件;根據(jù)配置文件的路徑锰提,加載配置成字節(jié)輸入流松忍,存儲(chǔ)在內(nèi)存中
2.創(chuàng)建兩個(gè)javaBean(容器)
    Configuration核心配置類:存放sql的配置信息
    MappedStatement映射配置類 存放mapper.xml信息
3.解析配置文件 dom4j
    創(chuàng)建類sqlSessionFactoryBuilder
        1.使用dom4j解析配置文件致开,將解析出來(lái)的文件存放到j(luò)avabean(容器)中
        2.創(chuàng)建sqlSessionFactory對(duì)象弦撩,生產(chǎn)session (工廠模式)
4.創(chuàng)建sqlSessionFactory接口實(shí)現(xiàn)類
    openSession
5.創(chuàng)建sqlSession接口及DefaultSession
    定義數(shù)據(jù)庫(kù)的crud操作 selectlist()
                      update()
6.執(zhí)行Exector接口及實(shí)現(xiàn)類SimpleExector
    query()//執(zhí)行jdbc代碼

2.1.3自定義持久層框架(缺陷)

1.dao層文虏,存在代碼重復(fù)瞒瘸,整個(gè)操作的過(guò)程模版重復(fù)(加載配置文件)
2.statementId存在硬編碼
========》jdk動(dòng)態(tài)代理 
Proxy.newProxyInstance  InvocationHandler

2.2 代碼實(shí)現(xiàn)

2.2.1 使用端

1.創(chuàng)建項(xiàng)目

1.創(chuàng)建lagou-mybatis項(xiàng)目(maven骨架項(xiàng)目)

2.創(chuàng)建sqlMapConfig.xml(sql源配置文件)
<configuration>
    <!--數(shù)據(jù)庫(kù)配置信息-->
    <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.創(chuàng)建UserMapper.xml(user類的sql)
<!--namespace 命名空間 當(dāng)前xml的-->
        <!--
        有三種全路徑:namespace綁定實(shí)體類的全路徑坷备,綁定dao接口的全路徑,綁定mapper的sql.xml文件情臭。
        第一種:namespace綁定實(shí)體類的全路徑:
        第二種:namespace綁定dao層接口的全路徑:
        第三種:namespace綁定的是mapper接口對(duì)應(yīng)的sql.xml省撑。

        https://www.cnblogs.com/zjdxr-up/p/8681382.html
        -->
<mapper namespace="com.test.dao.IUserDao">

    <!--sql的唯一標(biāo)識(shí):namespace.id來(lái)組成 : 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.創(chuàng)建項(xiàng)目

1.創(chuàng)建lagou-mybatis項(xiàng)目(maven骨架項(xiàng)目)

2.pojo類

Configuration 用于存儲(chǔ) 數(shù)據(jù)庫(kù)源

public class Configuration {

    private DataSource dataSource;

    /*
    *   key: statementid  value:封裝好的mappedStatement對(duì)象
     * */
    Map<String,MappedStatement> mappedStatementMap = new HashMap<>();
    }

MappedStatement 用于存儲(chǔ) sql文件的引用

//存儲(chǔ)的SQL xml文件的映射對(duì)象
public class MappedStatement {

    //id標(biāo)識(shí)
    private String id;
    //返回值類型
    private String resultType;
    //參數(shù)值類型
    private String paramterType;
    //sql語(yǔ)句
    private String sql;
    }

3.io類

Resources

public class Resources {
    // 根據(jù)配置文件的路徑,將配置文件加載成字節(jié)輸入流俯在,存儲(chǔ)在內(nèi)存中
    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){
        //獲取標(biāo)簽名稱
        String name = rootElement.getName();
        List<Element> list = rootElement.selectNodes("http://"+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);

            }

        }

        //遞歸遍歷當(dāng)前節(jié)點(diǎn)所有的子節(jié)點(diǎn)
        List<Element> listElement=rootElement.elements();//所有一級(jí)子節(jié)點(diǎn)的list
        for(Element e:listElement){//遍歷所有一級(jí)子節(jié)點(diǎn)
            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 {
        //調(diào)用執(zhí)行器

        //將要去完成對(duì)simpleExecutor里的query方法的調(diào)用
        simpleExecutor simpleExecutor = new simpleExecutor();
        //根據(jù)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("查詢結(jié)果為空或者返回結(jié)果過(guò)多");
        }
    }

    @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動(dòng)態(tài)代理來(lái)為Dao接口生成代理對(duì)象竟秫,并返回
//        loader: 用哪個(gè)類加載器去加載代理對(duì)象
//        interfaces:動(dòng)態(tài)代理類需要實(shí)現(xiàn)的接口
//        h:動(dòng)態(tài)代理方法在執(zhí)行時(shí),會(huì)調(diào)用h里面的invoke方法去執(zhí)行
        Object proxyInstance = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
            //proxy 當(dāng)前代理對(duì)象的應(yīng)用
            //method 當(dāng)前被調(diào)用方法的引用
            //args 傳遞的參數(shù)
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 底層都還是去執(zhí)行JDBC代碼 
                //根據(jù)不同情況跷乐,來(lái)調(diào)用selctList或者selectOne
                // 準(zhǔn)備參數(shù) 1:statmentid :sql語(yǔ)句的唯一標(biāo)識(shí):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);
                //獲取方法的標(biāo)簽值
                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();
                        // 判斷是否進(jìn)行了 泛型類型參數(shù)化
                        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執(zhí)行類

public class simpleExecutor implements  Executor {

    @Override                                                                                //user
    public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception {
        // 1\. 注冊(cè)驅(qū)動(dòng)肥败,獲取連接
        Connection connection = configuration.getDataSource().getConnection();

        // 2\. 獲取sql語(yǔ)句 : select * from user where id = #{id} and username = #{username}
            //轉(zhuǎn)換sql語(yǔ)句: select * from user where id = ? and username = ? ,轉(zhuǎn)換的過(guò)程中,還需要對(duì)#{}里面的值進(jìn)行解析存儲(chǔ)
        String sql = mappedStatement.getSql();
        BoundSql boundSql = getBoundSql(sql);

        // 3.獲取預(yù)處理對(duì)象:preparedStatement
        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());

        // 4\. 設(shè)置參數(shù)
        //獲取到了 參數(shù)的全路徑
         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);
            //暴力訪問(wèn)
            declaredField.setAccessible(true);
            Object o = declaredField.get(params[0]);
            preparedStatement.setObject(i+1,o);
        }

        // 5\. 執(zhí)行sql
        ResultSet resultSet = preparedStatement.executeQuery();
        //獲取返回值類
        String resultType = mappedStatement.getResultType();
        Class<?> resultTypeClass = getClassType(resultType);

        ArrayList<Object> objects = new ArrayList<>();

        // 6\. 封裝返回結(jié)果集
        while (resultSet.next()){
            //反射new一個(gè)類
            Object o =resultTypeClass.newInstance();
            //元數(shù)據(jù)
            ResultSetMetaData metaData = resultSet.getMetaData();
            for (int i = 1; i <= metaData.getColumnCount(); i++) {

                // 字段名
                String columnName = metaData.getColumnName(i);
                // 字段的值
                Object value = resultSet.getObject(columnName);

                //使用反射或者內(nèi)省馒稍,根據(jù)數(shù)據(jù)庫(kù)表和實(shí)體的對(duì)應(yīng)關(guān)系皿哨,完成封裝
                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\. 注冊(cè)驅(qū)動(dòng),獲取連接
        Connection connection = configuration.getDataSource().getConnection();

        // 2\. 獲取sql語(yǔ)句 : select * from user where id = #{id} and username = #{username}
        //轉(zhuǎn)換sql語(yǔ)句: select * from user where id = ? and username = ? 纽谒,轉(zhuǎn)換的過(guò)程中证膨,還需要對(duì)#{}里面的值進(jìn)行解析存儲(chǔ)
        String sql = mappedStatement.getSql();
        BoundSql boundSql = getBoundSql(sql);

        // 3.獲取預(yù)處理對(duì)象:preparedStatement
        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());

        // 4\. 設(shè)置參數(shù)
        //獲取到了 參數(shù)的全路徑
        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);
                //暴力訪問(wèn)
                declaredField.setAccessible(true);
                o = declaredField.get(params[0]);
            }

            preparedStatement.setObject(i+1,o);

        }

        // 5\. 執(zhí)行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;

    }

    /**
     * 完成對(duì)#{}的解析工作:1.將#{}使用?進(jìn)行代替佛舱,2.解析出#{}里面的值進(jìn)行存儲(chǔ)
     * @param sql
     * @return
     */
    private BoundSql getBoundSql(String sql) {
        //標(biāo)記處理類:配置標(biāo)記解析器來(lái)完成對(duì)占位符的解析處理工作
        ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
        GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
        //解析出來(lái)的sql
        String parseSql = genericTokenParser.parse(sql);
        //#{}里面解析出來(lái)的參數(shù)名稱
        List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();

        BoundSql boundSql = new BoundSql(parseSql,parameterMappings);
        return boundSql;

    }
}

7.測(cè)試類

@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問(wèn)題

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中點(diǎn)擊 projrct Structure =>點(diǎn)擊module 設(shè)置項(xiàng)目的編譯版本
3.srtting=> builder=>compiler=>java compiler設(shè)置編譯版本

3.mybatis快速啟動(dòng)

3.1導(dǎo)依賴

    <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坐標(biāo)-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</version>
        </dependency>
        <!--mysql驅(qū)動(dòng)坐標(biāo)-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
            <scope>runtime</scope>
        </dependency>
        <!--單元測(cè)試坐標(biāo)-->
        <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,//攔截那個(gè)接口
                    method = "prepare",//這個(gè)接口內(nèi)的那個(gè)方法名
                    args={Connection.class,Integer.class})//攔截方法的入?yún)⒆道绻椒ㄖ剌d,通過(guò)方法名跟參數(shù)確定唯一
    })
    public class MyPlugin implements Interceptor {
        //這里是每次執(zhí)行操作的時(shí)候请祖,都會(huì)進(jìn)行這個(gè)方法
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
            System.out.println("增強(qiáng)了");
            return invocation.proceed();
        }

        //把這個(gè)攔截器生成一個(gè)代理放到攔截器鏈中
        @Override
        public Object plugin(Object target) {
            System.out.println("將要包裝的目標(biāo)對(duì)象"+target);
            return Plugin.wrap(target,this);
        }
        //插件初始化時(shí)候調(diào)用订歪,只調(diào)用一次  獲取配置文件的屬性
        @Override
        public void setProperties(Properties properties) {
            System.out.println("獲取配置文件的屬性"+properties);
        }
    }

mybatis 攔截的類

一、executor肆捕,executor類可以說(shuō)是執(zhí)行sql的全過(guò)程刷晋,如組裝參數(shù),sql改造慎陵,結(jié)果處理眼虱,比較廣泛,但實(shí)際用的不多

二席纽、StatementHandler捏悬,這個(gè)是執(zhí)行sql的過(guò)程,可以獲取到待執(zhí)行的sql润梯,可用來(lái)改造sql过牙,如分頁(yè),分表纺铭,最常攔截的類

三寇钉、paremeterHandler,這個(gè)用來(lái)攔截sql的參數(shù)舶赔,可以自定義參數(shù)組裝規(guī)則

四扫倡、resultHandler,這個(gè)用來(lái)處理結(jié)果

5 緩存

一級(jí)緩存

二級(jí)緩存

二級(jí)緩存是序列化存儲(chǔ) 需要實(shí)現(xiàn)POJO需要實(shí)現(xiàn)Serializable接口

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

問(wèn)題

1.緩存使用順序

先到二級(jí)緩存當(dāng)中查找
如果二級(jí)緩存中沒(méi)有,就去找一級(jí)緩存
如果一級(jí)緩存中也沒(méi)有就去到數(shù)據(jù)庫(kù)當(dāng)中查詢

2.二級(jí)Chache使用原則

1.只能在一個(gè)命名空間下使用二級(jí)緩存
由于二級(jí)緩存中的數(shù)據(jù)是基于namespace的竟纳,即不同namespace中的數(shù)據(jù)互不干擾撵溃。在多個(gè)namespace中若均存在對(duì)同一個(gè)表的操作,那么這多個(gè)namespace中的數(shù)據(jù)可能就會(huì)出現(xiàn)不一致現(xiàn)象蚁袭。
2.在單表上使用二級(jí)緩存
如果一個(gè)表與其它表有關(guān)聯(lián)關(guān)系征懈,那么久非常有可能存在多個(gè)namespace對(duì)同一數(shù)據(jù)的操作。而不同namespace中的數(shù)據(jù)互補(bǔ)干擾揩悄,所以就有可能出現(xiàn)多個(gè)namespace中的數(shù)據(jù)不一致現(xiàn)象卖哎。
3.查詢多于修改時(shí)使用二級(jí)緩存
在查詢操作遠(yuǎn)遠(yuǎn)多于增刪改操作的情況下可以使用二級(jí)緩存。因?yàn)槿魏卧鰟h改操作都將刷新二級(jí)緩存,對(duì)二級(jí)緩存的頻繁刷新將降低系統(tǒng)性能亏娜。

java補(bǔ)充

1.java類型 T焕窝,E,K维贺,V它掂,?

泛型中的通配符

溯泣?表示不確定的 java 類型
虐秋?無(wú)界通配符
例子 List<? extends Animal> listAnimals

T (type) 表示具體的一個(gè)java類型
 List<T>是確定的某一個(gè)類型

K V (key value) 分別代表java鍵值中的Key Value

E (element) 代表Element
上界通配符 < ? extends E>
下界通配符 < ? super E>

T和?運(yùn)用的地方有點(diǎn)不同,
    ? 是定義在引用變量上
    T 是類上或方法上

2. List<T.?.Object>區(qū)別

ArrayList<T> arrayList = new ArrayList<T>(); 指定集合元素類型只可以是T類型
ArrayList<?>arrayList = new ArrayList<?>(); 集合元素可以是任意類型垃沦,一般是方法中為了說(shuō)明方法
ArrayList<? extends E> arrayList = new ArrayList<? extends E>();泛型的限定:客给? extends E:接受E類型或者E的子類型、肢簿? super E :接受E類型或者E的父類型

3.java的內(nèi)省與反射

//https://www.cnblogs.com/winclpt/articles/7405271.html

1.內(nèi)省是先得到屬性描述器PropertyDecriptor后再進(jìn)行各種操作靶剑,內(nèi)省(Introspector)是Java語(yǔ)言對(duì)JavaBean類屬性池充、事件的處理方法
2.反射則是先得到類的字節(jié)碼Class后再進(jìn)行各種操作的桩引。對(duì)任意一個(gè)類,能夠獲取得到這個(gè)類的所有屬性和方法收夸;
代碼:
 反射:
     Field f = user.getClass().getDeclaredField("name");
     f.setAccessible(true);
     f.set(user, "mld");//設(shè)置屬性值
 內(nèi)省
     //操作單個(gè)屬性
     PropertyDescriptor pd = new PropertyDescriptor("name", User.class);
     Method w = pd.getWriteMethod();//獲取屬性的setter方法
     w.invoke(user, "winclpt");

4.java的代理

1.靜態(tài)代理

靜態(tài)代理:由程序員創(chuàng)建或特定工具自動(dòng)生成源代碼坑匠,也就是在編譯時(shí)就已經(jīng)將接口,被代理類卧惜,代理類等確定下來(lái)笛辟。在程序運(yùn)行之前,代理類的.class文件就已經(jīng)生成

2.動(dòng)態(tài)代理

實(shí)現(xiàn)InvocationHandler接口
動(dòng)態(tài)代理就是要生成一個(gè)包裝類對(duì)象序苏,由于代理的對(duì)象是動(dòng)態(tài)的,所以叫動(dòng)態(tài)代理捷凄。由于我們需要增強(qiáng)忱详,這個(gè)增強(qiáng)是需要留給開(kāi)發(fā)人員開(kāi)發(fā)代碼的,因此代理類不能直接包含被代理對(duì)象跺涤,而是一個(gè)InvocationHandler匈睁,該InvocationHandler包含被代理對(duì)象,并負(fù)責(zé)分發(fā)請(qǐng)求給被代理對(duì)象桶错,分發(fā)前后均可以做增強(qiáng)航唆。從原理可以看出,JDK動(dòng)態(tài)代理是“對(duì)象”的代理院刁。
原文鏈接:https://blog.csdn.net/flyfeifei66/article/details/81481222 **

       // 使用JDK動(dòng)態(tài)代理來(lái)為Dao接口生成代理對(duì)象糯钙,并返回
//        loader: 用哪個(gè)類加載器去加載代理對(duì)象
//
//        interfaces:動(dòng)態(tài)代理類需要實(shí)現(xiàn)的接口
//
//        h:動(dòng)態(tài)代理方法在執(zhí)行時(shí),會(huì)調(diào)用h里面的invoke方法去執(zhí)行
        Object proxyInstance = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
            //proxy 當(dāng)前代理對(duì)象的應(yīng)用
            //method 當(dāng)前被調(diào)用方法的引用
            //args 傳遞的參數(shù)
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 底層都還是去執(zhí)行JDBC代碼 //根據(jù)不同情況,來(lái)調(diào)用selctList或者selectOne
                // 準(zhǔn)備參數(shù) 1:statmentid :sql語(yǔ)句的唯一標(biāo)識(shí):namespace.id= 接口全限定名.方法名
                // 方法名:findAll
                String methodName = method.getName();
                String className = method.getDeclaringClass().getName();

                String statementId = className+"."+methodName;

                // 準(zhǔn)備參數(shù)2:params:args
                // 獲取被調(diào)用方法的返回值類型
                Type genericReturnType = method.getGenericReturnType();
                // 判斷是否進(jìn)行了 泛型類型參數(shù)化
                if(genericReturnType instanceof ParameterizedType){
                    List<Object> objects = selectList(statementId, args);
                    return objects;
                }

                return selectOne(statementId,args);

            }
        });

        return (T) proxyInstance;
    }

5.newInstance與new的區(qū)別

new關(guān)鍵字能調(diào)用任何構(gòu)造方法任岸。
newInstance()只能調(diào)用無(wú)參構(gòu)造方法再榄。
newInstance()構(gòu)造對(duì)象的地方通過(guò)new關(guān)鍵字也可以創(chuàng)建對(duì)象.
在使用newInstance()方法的時(shí)候,必須保證這個(gè)類已經(jīng)加載并且已經(jīng)連接
適用:
使用newInstance()在通用性方面比較高享潜,className我們可以用配置文件進(jìn)行相關(guān)的配置困鸥。
String className = 從配置文件中讀取className;
A a = (A) Class.forName(className).newInstance();
再配合依賴注入的方法,就提高了軟件的可伸縮性剑按、可擴(kuò)展性疾就。框架的開(kāi)發(fā)中用的比較多艺蝴!

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 一定會(huì),邊界
Declaring 公然地,生命
Accessible 可進(jìn)入的
descrip 描述
relation 關(guān)系
example例子
Generic通用的
Security 安全的
Multiple 多樣的
extract提取
wrap包
signature簽名
Additional附加的 額外的

作業(yè)

簡(jiǎn)答題

一.

1.Mybatis動(dòng)態(tài)sql是做什么的吴趴?

1.動(dòng)態(tài)sql就是 根據(jù)條件標(biāo)簽動(dòng)態(tài)的拼接sql漆诽,包括判空,循環(huán)锣枝,拼接等

2.哪些動(dòng)態(tài)sql?

動(dòng)態(tài)sql大致有
1.: if是為了判斷傳入的值是否符合某種規(guī)則厢拭,比如是否不為空;
2.:where標(biāo)簽可以用來(lái)做動(dòng)態(tài)拼接查詢條件撇叁,當(dāng)和if標(biāo)簽配合的時(shí)候供鸠,不用顯示的聲明類似where 1=1這種無(wú)用的條件
3.:foreach標(biāo)簽可以把傳入的集合對(duì)象進(jìn)行遍歷,然后把每一項(xiàng)的內(nèi)容作為參數(shù)傳到sql語(yǔ)句中陨闹,
4.:include可以把大量重復(fù)的代碼整理起來(lái)楞捂,當(dāng)使用的時(shí)候直接include即可,減少重復(fù)代碼的編寫(xiě)趋厉;
5.:是一個(gè)格式化標(biāo)簽
6.choose寨闹、when、otherwise 標(biāo)簽類似于 Java 中的 switch君账、case繁堡、default。只有一個(gè)條件生效乡数,

<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. 簡(jiǎn)述一下動(dòng)態(tài)sql的執(zhí)行原理椭蹄?

1.在xmlMapperBuilder中 解析配置文件時(shí)

2.解析 <mapper /> 節(jié)點(diǎn)

3.解析 節(jié)點(diǎn)

    // 1.在xmlMapperBuilder中 解析配置文件時(shí)
    public void parse() {
            // 解析 `<mapper />` 節(jié)點(diǎn)
            configurationElement(parser.evalNode("/mapper"));
    }
    //2\. 解析 `<mapper />` 節(jié)點(diǎn)
    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");
            }
            // 設(shè)置 namespace 屬性
            builderAssistant.setCurrentNamespace(namespace);
            // 解析 <resultMap /> 節(jié)點(diǎn)們
            resultMapElements(context.evalNodes("/mapper/resultMap"));
            // 解析 <sql /> 節(jié)點(diǎn)們
            sqlElement(context.evalNodes("/mapper/sql"));
            // 解析 <select /> <insert /> <update /> <delete /> 節(jié)點(diǎn)們
            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 /> 節(jié)點(diǎn)們
    private void buildStatementFromContext(List<XNode> list) {
        if (configuration.getDatabaseId() != null) {
            buildStatementFromContext(list, configuration.getDatabaseId());
        }
        buildStatementFromContext(list, null);
        // 上面兩塊代碼,可以簡(jiǎn)寫(xiě)成 buildStatementFromContext(list, configuration.getDatabaseId());
    }
    private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
        //遍歷 <select /> <insert /> <update /> <delete /> 節(jié)點(diǎn)們
        for (XNode context : list) {
            // 創(chuàng)建 XMLStatementBuilder 對(duì)象净赴,執(zhí)行解析
            final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
            try {
                statementParser.parseStatementNode();
            } catch (IncompleteElementException e) {
                // 解析失敗绳矩,添加到 configuration 中
                configuration.addIncompleteStatement(statementParser);
            }
        }
    }

4.XMLStatementBuilder對(duì)象

public class XMLStatementBuilder extends BaseBuilder {
   /**
     * 執(zhí)行解析
     */
    public void parseStatementNode() {
        // 獲得 id 屬性,編號(hào)玖翅。
        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 對(duì)應(yīng)的 LanguageDriver 對(duì)象
        LanguageDriver langDriver = getLanguageDriver(lang);

        // 獲得 resultType 對(duì)應(yīng)的類
        Class<?> resultTypeClass = resolveClass(resultType);
        // 獲得 resultSet 對(duì)應(yīng)的枚舉值
        String resultSetType = context.getStringAttribute("resultSetType");
        ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
        // 獲得 statementType 對(duì)應(yīng)的枚舉值
        StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));

}

5.LanguageDriver的實(shí)現(xiàn)類XMLLanguageDriver的方法解析方法createSqlSource

public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
        // 創(chuàng)建 XMLScriptBuilder 對(duì)象割以,執(zhí)行解析
        XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
        return builder.parseScriptNode();
    }

6.解析

XMLScriptBuilder標(biāo)簽解析
/**
 * XML 動(dòng)態(tài)語(yǔ)句( SQL )構(gòu)建器,負(fù)責(zé)將 SQL 解析成 SqlSource 對(duì)象
 *
 * @author Clinton Begin
 */
public class XMLScriptBuilder extends BaseBuilder {

    /**
     * 當(dāng)前 SQL 的 XNode 對(duì)象
     */
    private final XNode context;
    /**
     * 是否為動(dòng)態(tài) 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());
    }

    /**
     * 負(fù)責(zé)將 SQL 解析成 SqlSource 對(duì)象
     *
     * @return SqlSource 對(duì)象
     */
    public SqlSource parseScriptNode() {
        // 解析 SQL
        MixedSqlNode rootSqlNode = parseDynamicTags(context);
        // 創(chuàng)建 SqlSource 對(duì)象
        SqlSource sqlSource;
        if (isDynamic) {
            sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
        } else {
            sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
        }
        return sqlSource;
    }

    /**
     * 解析 SQL 成 MixedSqlNode 對(duì)象
     *
     * @param node XNode 節(jié)點(diǎn)
     * @return MixedSqlNode
     */
    protected MixedSqlNode parseDynamicTags(XNode node) {
        // 創(chuàng)建 SqlNode 數(shù)組
        List<SqlNode> contents = new ArrayList<>();
        // 遍歷 SQL 節(jié)點(diǎn)的所有子節(jié)點(diǎn)
        NodeList children = node.getNode().getChildNodes();
        for (int i = 0; i < children.getLength(); i++) {
            // 當(dāng)前子節(jié)點(diǎn)
            XNode child = node.newXNode(children.item(i));
            // 如果類型是 Node.CDATA_SECTION_NODE 或者 Node.TEXT_NODE 時(shí)
            if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
                // 獲得內(nèi)容
                String data = child.getStringBody("");
                // 創(chuàng)建 TextSqlNode 對(duì)象
                TextSqlNode textSqlNode = new TextSqlNode(data);
                // 如果是動(dòng)態(tài)的 TextSqlNode 對(duì)象
                if (textSqlNode.isDynamic()) {
                    // 添加到 contents 中
                    contents.add(textSqlNode);
                    // 標(biāo)記為動(dòng)態(tài) SQL
                    isDynamic = true;
                // 如果是非動(dòng)態(tài)的 TextSqlNode 對(duì)象
                } else {
                    // 創(chuàng)建 StaticTextSqlNode 添加到 contents 中
                    contents.add(new StaticTextSqlNode(data));
                }
            // 如果類型是 Node.ELEMENT_NODE
            } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
                // 根據(jù)子節(jié)點(diǎn)的標(biāo)簽写妥,獲得對(duì)應(yīng)的 NodeHandler 對(duì)象
                String nodeName = child.getNode().getNodeName();
                NodeHandler handler = nodeHandlerMap.get(nodeName);
                if (handler == null) { // 獲得不到拳球,說(shuō)明是未知的標(biāo)簽,拋出 BuilderException 異常
                    throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
                }
                // 執(zhí)行 NodeHandler 處理
                handler.handleNode(child, contents);
                // 標(biāo)記為動(dòng)態(tài) SQL
                isDynamic = true;
            }
        }
        // 創(chuàng)建 MixedSqlNode 對(duì)象
        return new MixedSqlNode(contents);
    }

    /**
     * Node 處理器接口
     */
    private interface NodeHandler {

        /**
         * 處理 Node
         *
         * @param nodeToHandle 要處理的 XNode 節(jié)點(diǎn)
         * @param targetContents 目標(biāo)的 SqlNode 數(shù)組珍特。實(shí)際上祝峻,被處理的 XNode 節(jié)點(diǎn)會(huì)創(chuàng)建成對(duì)應(yīng)的 SqlNode 對(duì)象,添加到 targetContents 中
         */
        void handleNode(XNode nodeToHandle, List<SqlNode> targetContents);

    }

    /**
     * `<bind />` 標(biāo)簽的處理器
     *
     * @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");
            // 創(chuàng)建 VarDeclSqlNode 對(duì)象
            final VarDeclSqlNode node = new VarDeclSqlNode(name, expression);
            // 添加到 targetContents 中
            targetContents.add(node);
        }
    }

    /**
     * `<trim />` 標(biāo)簽的處理器
     *
     * @see TrimSqlNode
     */
    private class TrimHandler implements NodeHandler {

        public TrimHandler() {
            // Prevent Synthetic Access
        }

        @Override
        public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
            // 解析內(nèi)部的 SQL 節(jié)點(diǎn)莱找,成 MixedSqlNode 對(duì)象
            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");
            // 創(chuàng)建 TrimSqlNode 對(duì)象
            TrimSqlNode trim = new TrimSqlNode(configuration, mixedSqlNode, prefix, prefixOverrides, suffix, suffixOverrides);
            // 添加到 targetContents 中
            targetContents.add(trim);
        }

    }

    /**
     * `<where />` 標(biāo)簽的處理器
     *
     * @see WhereSqlNode
     */
    private class WhereHandler implements NodeHandler {

        public WhereHandler() {
            // Prevent Synthetic Access
        }

        @Override
        public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
            // 解析內(nèi)部的 SQL 節(jié)點(diǎn),成 MixedSqlNode 對(duì)象
            MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
            // 創(chuàng)建 WhereSqlNode 對(duì)象
            WhereSqlNode where = new WhereSqlNode(configuration, mixedSqlNode);
            // 添加到 targetContents 中
            targetContents.add(where);
        }

    }

    /**
     * `<set />` 標(biāo)簽的處理器
     *
     * @see SetSqlNode
     */
    private class SetHandler implements NodeHandler {

        public SetHandler() {
            // Prevent Synthetic Access
        }

        @Override
        public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
            // 解析內(nèi)部的 SQL 節(jié)點(diǎn)骨宠,成 MixedSqlNode 對(duì)象
            MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
            // 創(chuàng)建 SetSqlNode 對(duì)象
            SetSqlNode set = new SetSqlNode(configuration, mixedSqlNode);
            // 添加到 targetContents 中
            targetContents.add(set);
        }

    }

    /**
     * `<foreach />` 標(biāo)簽的處理器
     *
     * @see ForEachSqlNode
     */
    private class ForEachHandler implements NodeHandler {

        public ForEachHandler() {
            // Prevent Synthetic Access
        }

        @Override
        public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
            // 解析內(nèi)部的 SQL 節(jié)點(diǎn)浮定,成 MixedSqlNode 對(duì)象
            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");
            // 創(chuàng)建 ForEachSqlNode 對(duì)象
            ForEachSqlNode forEachSqlNode = new ForEachSqlNode(configuration, mixedSqlNode, collection, index, item, open, close, separator);
            // 添加到 targetContents 中
            targetContents.add(forEachSqlNode);
        }

    }

    /**
     * `<if />` 標(biāo)簽的處理器
     *
     * @see IfSqlNode
     */
    private class IfHandler implements NodeHandler {

        public IfHandler() {
            // Prevent Synthetic Access
        }

        @Override
        public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
            // 解析內(nèi)部的 SQL 節(jié)點(diǎn)方灾,成 MixedSqlNode 對(duì)象
            MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
            // 獲得 test 屬性
            String test = nodeToHandle.getStringAttribute("test");
            // 創(chuàng)建 IfSqlNode 對(duì)象
            IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test);
            // 添加到 targetContents 中
            targetContents.add(ifSqlNode);
        }

    }

    /**
     * `<otherwise />` 標(biāo)簽的處理器
     */
    private class OtherwiseHandler implements NodeHandler {

        public OtherwiseHandler() {
            // Prevent Synthetic Access
        }

        @Override
        public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
            // 解析內(nèi)部的 SQL 節(jié)點(diǎn),成 MixedSqlNode 對(duì)象
            MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
            // 添加到 targetContents 中
            targetContents.add(mixedSqlNode);
        }

    }

    /**
     * `<choose />` 標(biāo)簽的處理器
     *
     * @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 />` 的節(jié)點(diǎn)們
            handleWhenOtherwiseNodes(nodeToHandle, whenSqlNodes, otherwiseSqlNodes);
            // 獲得 `<otherwise />` 的節(jié)點(diǎn)
            SqlNode defaultSqlNode = getDefaultSqlNode(otherwiseSqlNodes);
            // 創(chuàng)建 ChooseSqlNode 對(duì)象
            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 />` 標(biāo)簽的情況
                    handler.handleNode(child, ifSqlNodes);
                } else if (handler instanceof OtherwiseHandler) { // 處理 `<otherwise />` 標(biāo)簽的情況
                    handler.handleNode(child, defaultSqlNodes);
                }
            }
        }

        // 至多允許有一個(gè) SqlNode 節(jié)點(diǎn)
        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是否支持延遲加載裕偿?如果支持,它的實(shí)現(xiàn)原理是什么痛单?

延遲加載主要通過(guò)動(dòng)態(tài)代理實(shí)現(xiàn)嘿棘,通過(guò)代理攔截指定方法沒(méi)執(zhí)行數(shù)據(jù)加載。

javassisProxyFactory會(huì)創(chuàng)建一個(gè)User代理對(duì)象旭绒,所有調(diào)用User對(duì)象方法蔫巩,都會(huì)經(jīng)過(guò)EnhancedResultObjectProxyImpl.invoke()方法的攔截。快压、

于是當(dāng)調(diào)用User.getOrder()方法時(shí),才真正去執(zhí)行查詢Order的動(dòng)作并把結(jié)果賦值

<settings>
    <!-- 開(kāi)啟全局配置的懶加載 -->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!-- 關(guān)閉積極加載 -->
    <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執(zhí)行器

BaseExecutor 簡(jiǎn)單執(zhí)行器垃瞧,是 MyBatis 中默認(rèn)使用的執(zhí)行器蔫劣,每執(zhí)行一次 update 或 select,就開(kāi)啟一個(gè) Statement 對(duì)象个从,用完就直接關(guān)閉 Statement 對(duì)象(可以是 Statement 或者是 PreparedStatment 對(duì)象)
BatchExecutor 批處理執(zhí)行器脉幢,用于將多個(gè)SQL一次性輸出到數(shù)據(jù)庫(kù)
simpleExexutor 每執(zhí)行一次update或select歪沃,就開(kāi)啟一個(gè)Statement對(duì)象,用完立刻關(guān)閉Statement對(duì)象嫌松。
ReuseExecutor 執(zhí)行update或select沪曙,以sql作為key查找Statement對(duì)象,存在就使用萎羔,不存在就創(chuàng)建液走,用完后,不關(guān)閉Statement對(duì)象贾陷,而是放置于Map內(nèi)缘眶,供下一次使用。簡(jiǎn)言之髓废,就是重復(fù)使用Statement對(duì)象巷懈。

cachingExecutor 更新緩存

2.它們之間的區(qū)別是什么?

作用范圍:Executor的這些特點(diǎn)慌洪,都嚴(yán)格限制在SqlSession生命周期范圍內(nèi)顶燕。

默認(rèn)是SimplExcutor,需要配置在創(chuàng)建SqlSession對(duì)象的時(shí)候指定執(zhí)行器的類型即可冈爹。

1.一級(jí)緩存

Mybatis的一級(jí)緩存是指SqlSession級(jí)別的涌攻,作用域是SqlSession,Mybatis默認(rèn)開(kāi)啟一級(jí)緩存犯助,在同一個(gè)SqlSession中癣漆,相同的Sql查詢的時(shí)候,第一次查詢的時(shí)候剂买,就會(huì)從緩存中取惠爽,如果發(fā)現(xiàn)沒(méi)有數(shù)據(jù),那么就從數(shù)據(jù)庫(kù)查詢出來(lái)瞬哼,并且緩存到HashMap中婚肆,如果下次還是相同的查詢,就直接從緩存中查詢坐慰,就不在去查詢數(shù)據(jù)庫(kù)较性,對(duì)應(yīng)的就不在去執(zhí)行SQL語(yǔ)句。當(dāng)查詢到的數(shù)據(jù)结胀,進(jìn)行增刪改的操作的時(shí)候赞咙,緩存將會(huì)失效

2. 二級(jí)緩存

MyBatis的二級(jí)緩存是基于Mapper級(jí)別的,也就是說(shuō)多個(gè)SqlSession去使用某個(gè)Mapper的查詢語(yǔ)句時(shí)糟港,得到的緩存數(shù)據(jù)是可共用的攀操。第一次調(diào)用mapper下的sql 的時(shí)候去查詢信息,查詢到的信息會(huì)存放到該mapper對(duì)應(yīng)的二級(jí)緩存區(qū)域秸抚,第二次調(diào)用namespace下的mapper映射文件中速和,相同的SQL去查詢歹垫,回去對(duì)應(yīng)的二級(jí)緩存內(nèi)取結(jié)果。二級(jí)緩存開(kāi)啟后颠放,查詢就會(huì)走二級(jí)緩存排惨,沒(méi)查到直接查庫(kù)。MyBatis默認(rèn)不開(kāi)啟二級(jí)緩存

簡(jiǎn)述Mybatis的插件運(yùn)行原理碰凶,以及如何編寫(xiě)一個(gè)插件暮芭?

插件運(yùn)行原理

實(shí)現(xiàn)Mybatis的Interceptor接口并復(fù)寫(xiě)intercept()方法,然后在給插件編寫(xiě)注解痒留,指定要攔截哪一個(gè)接口的哪些方法即可

實(shí)現(xiàn)Interceptor接口 在定義StatementHandler處理器的時(shí)候攔截prepare方法也就是準(zhǔn)備的方法

//1.configuration.newStatementHandler()獲取對(duì)象
  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());
      //執(zhí)行
      return handler.update(stmt);

  }
  //2.獲取執(zhí)行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);
    //代理對(duì)象
    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;
  }
  }

如何編寫(xiě)插件

實(shí)現(xiàn)Interceptor接口谴麦,Interceptors注解表明要攔截的類,方法伸头,參數(shù)

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,//攔截那個(gè)接口
                    method = "prepare",//這個(gè)接口內(nèi)的那個(gè)方法名
                    args={Connection.class,Integer.class})//攔截方法的入?yún)⒇倚В绻椒ㄖ剌d,通過(guò)方法名跟參數(shù)確定唯一
    })
    public class MyPlugin implements Interceptor {
        //這里是每次執(zhí)行操作的時(shí)候恤磷,都會(huì)進(jìn)行這個(gè)方法
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
            System.out.println("增強(qiáng)了");
            return invocation.proceed();
        }

        //把這個(gè)攔截器生成一個(gè)代理放到攔截器鏈中
        @Override
        public Object plugin(Object target) {
            System.out.println("將要包裝的目標(biāo)對(duì)象"+target);
            return Plugin.wrap(target,this);
        }
        //插件初始化時(shí)候調(diào)用面哼,只調(diào)用一次  獲取配置文件的屬性
        @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;
  }
  }

如何編寫(xiě)插件

實(shí)現(xiàn)Interceptor接口,Interceptors注解表明要攔截的類扫步,方法魔策,參數(shù)

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,//攔截那個(gè)接口
                    method = "prepare",//這個(gè)接口內(nèi)的那個(gè)方法名
                    args={Connection.class,Integer.class})//攔截方法的入?yún)ⅲ绻椒ㄖ剌d河胎,通過(guò)方法名跟參數(shù)確定唯一
    })
    public class MyPlugin implements Interceptor {
        //這里是每次執(zhí)行操作的時(shí)候闯袒,都會(huì)進(jìn)行這個(gè)方法
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
            System.out.println("增強(qiáng)了");
            return invocation.proceed();
        }

        //把這個(gè)攔截器生成一個(gè)代理放到攔截器鏈中
        @Override
        public Object plugin(Object target) {
            System.out.println("將要包裝的目標(biāo)對(duì)象"+target);
            return Plugin.wrap(target,this);
        }
        //插件初始化時(shí)候調(diào)用,只調(diào)用一次  獲取配置文件的屬性
        @Override
        public void setProperties(Properties properties) {
            System.out.println("獲取配置文件的屬性"+properties);
        }
    }

結(jié)尾

本文到這里就結(jié)束了游岳,感謝看到最后的朋友政敢,都看到最后了,點(diǎn)個(gè)贊再走啊胚迫,如有不對(duì)之處還請(qǐng)多多指正喷户。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市访锻,隨后出現(xiàn)的幾起案子褪尝,更是在濱河造成了極大的恐慌,老刑警劉巖期犬,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件河哑,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡龟虎,警方通過(guò)查閱死者的電腦和手機(jī)璃谨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)遣总,“玉大人睬罗,你說(shuō)我怎么就攤上這事⌒癯猓” “怎么了容达?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)垂券。 經(jīng)常有香客問(wèn)我花盐,道長(zhǎng),這世上最難降的妖魔是什么菇爪? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任算芯,我火速辦了婚禮,結(jié)果婚禮上凳宙,老公的妹妹穿的比我還像新娘熙揍。我一直安慰自己,他們只是感情好氏涩,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布届囚。 她就那樣靜靜地躺著,像睡著了一般是尖。 火紅的嫁衣襯著肌膚如雪意系。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天饺汹,我揣著相機(jī)與錄音蛔添,去河邊找鬼。 笑死兜辞,一個(gè)胖子當(dāng)著我的面吹牛迎瞧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播弦疮,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼夹攒,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了胁塞?” 一聲冷哼從身側(cè)響起咏尝,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎啸罢,沒(méi)想到半個(gè)月后编检,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡扰才,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年允懂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片衩匣。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蕾总,死狀恐怖粥航,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情生百,我是刑警寧澤递雀,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站蚀浆,受9級(jí)特大地震影響缀程,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜市俊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一杨凑、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧摆昧,春花似錦撩满、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至勇吊,卻和暖如春曼追,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背汉规。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工礼殊, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人针史。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓晶伦,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親啄枕。 傳聞我的和親對(duì)象是個(gè)殘疾皇子婚陪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345