亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Mybatis-Plus注入SQL原理分析

 更新時(shí)間:2022年07月08日 09:54:36   作者:養(yǎng)歌  
本文主要介紹了Mybatis-Plus注入SQL原理分析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

前言

MyBatis-Plus 是一個(gè) MyBatis 的增強(qiáng)工具,在 MyBatis 的基礎(chǔ)上只做增強(qiáng)不做改變,為簡(jiǎn)化開(kāi)發(fā)、提高效率而生。

那么 MyBatis-Plus 是怎么加強(qiáng)的呢?其實(shí)就是封裝好了一些 crud 方法,開(kāi)發(fā)人員不需要再寫(xiě) SQL 了,間接調(diào)用方法就可以獲取到封裝好的 SQL 語(yǔ)句。

特性:

  • 無(wú)侵入:只做增強(qiáng)不做改變,引入它不會(huì)對(duì)現(xiàn)有工程產(chǎn)生影響,如絲般順滑
  • 損耗小:?jiǎn)?dòng)即會(huì)自動(dòng)注入基本 CURD,性能基本無(wú)損耗,直接面向?qū)ο蟛僮?/li>
  • 強(qiáng)大的 CRUD 操作:內(nèi)置通用 Mapper、通用 Service,僅僅通過(guò)少量配置即可實(shí)現(xiàn)單表大部分 CRUD 操作,更有強(qiáng)大的條件構(gòu)造器,滿足各類(lèi)使用需求
  • 支持 Lambda 形式調(diào)用:通過(guò) Lambda 表達(dá)式,方便的編寫(xiě)各類(lèi)查詢條件,無(wú)需再擔(dān)心字段寫(xiě)錯(cuò)
  • 支持主鍵自動(dòng)生成:支持多達(dá) 4 種主鍵策略(內(nèi)含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解決主鍵問(wèn)題
  • 支持 ActiveRecord 模式:支持 ActiveRecord 形式調(diào)用,實(shí)體類(lèi)只需繼承 Model 類(lèi)即可進(jìn)行強(qiáng)大的 CRUD 操作
  • 支持自定義全局通用操作:支持全局通用方法注入( Write once, use anywhere )
  • 內(nèi)置代碼生成器:采用代碼或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 層代碼,支持模板引擎,更有超多自定義配置等您來(lái)使用
  • 內(nèi)置分頁(yè)插件:基于 MyBatis 物理分頁(yè),開(kāi)發(fā)者無(wú)需關(guān)心具體操作,配置好插件之后,寫(xiě)分頁(yè)等同于普通 List 查詢
  • 分頁(yè)插件支持多種數(shù)據(jù)庫(kù):支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多種數(shù)據(jù)庫(kù)
  • 內(nèi)置性能分析插件:可輸出 Sql 語(yǔ)句以及其執(zhí)行時(shí)間,建議開(kāi)發(fā)測(cè)試時(shí)啟用該功能,能快速揪出慢查詢
  • 內(nèi)置全局?jǐn)r截插件:提供全表 delete 、 update 操作智能分析阻斷,也可自定義攔截規(guī)則,預(yù)防誤操作

支持的數(shù)據(jù)庫(kù):

  • MySQL、 Oracle 、 db2 、PostgreSQL 、 SqlServer 等等。

案例

下面我們先從一個(gè)簡(jiǎn)單的 demo 入手,來(lái)感受一下 MyBatis-plus 的便捷性。

MP封裝的 BaseMapper 接口

public interface BaseMapper<T> extends Mapper<T> {

    /**
     * 插入一條記錄
     *
     * @param entity 實(shí)體對(duì)象
     */
    int insert(T entity);


    /**
     * 根據(jù) entity 條件,刪除記錄
     *
     * @param wrapper 實(shí)體對(duì)象封裝操作類(lèi)(可以為 null)
     */
    int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);

    /**
     * 根據(jù) whereEntity 條件,更新記錄
     *
     * @param entity        實(shí)體對(duì)象 (set 條件值,可以為 null)
     * @param updateWrapper 實(shí)體對(duì)象封裝操作類(lèi)(可以為 null,里面的 entity 用于生成 where 語(yǔ)句)
     */
    int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);

    /**
     * 根據(jù) ID 查詢
     *
     * @param id 主鍵ID
     */
    T selectById(Serializable id);
}

實(shí)體類(lèi)對(duì)象

/**
 * 實(shí)體類(lèi)
 *
 * @author Chill
 */
@Data
@TableName("user")
@EqualsAndHashCode(callSuper = true)
public class User extends TenantEntity {

	private static final long serialVersionUID = 1L;

	/**
	 * 用戶編號(hào)
	 */
	private String code;
	/**
	 * 賬號(hào)
	 */
	private String account;
	/**
	 * 密碼
	 */
	private String password;
	/**
	 * 昵稱(chēng)
	 */
	private String name;
}

UserMapper 繼承 BaseMapper 接口

/**
 * Mapper 接口
 *
 * @author Chill
 */
public interface UserMapper extends BaseMapper<User> {
}

測(cè)試

@Override
	public User getById(String id){
		User user = userMapper.selectById(id);
		return null;
	}

最終查詢的 SQL 語(yǔ)句如下圖:

從打印的日志我們可以知道,MyBatis-Plus 最終為我們自動(dòng)生成了 SQL 語(yǔ)句。根據(jù)上述操作分析:UserMapper 繼承了 BaseMapper,擁有了 selectById 的方法,但是 MyBatis-Plus 是基于 mybatis 的增強(qiáng)版,關(guān)鍵在于最終仍然需要提供具體的SQL語(yǔ)句,來(lái)進(jìn)行數(shù)據(jù)庫(kù)操作。

下面我們 DEBUG 跟蹤 MyBatis-Plus 是如何生成業(yè)務(wù) sql 以及自動(dòng)注入的,如下圖所示:

發(fā)現(xiàn) SQL 語(yǔ)句在 MappedStatement 對(duì)象中,而 sqlSource 存的就是相關(guān)的 SQL 語(yǔ)句,基于上面的分析,我們想要知道 SQL 語(yǔ)句是什么時(shí)候獲取到的,就是要找到 mappedStatement 被添加的位置。追蹤到 AbstractMethod 的抽象方法中。

原理解析

Mybatis-Plus 在啟動(dòng)后會(huì)將 BaseMapper 中的一系列的方法注冊(cè)到 meppedStatements 中,那么究竟是如何注入的呢?下面我們一起來(lái)分析下。

在 Mybatis-Plus 中,ISqlInjector 負(fù)責(zé) SQL 的注入工作,它是一個(gè)接口,AbstractSqlInjector 是它的實(shí)現(xiàn)類(lèi),SqlInjector SQL 自動(dòng)注入器接口的相關(guān) UML 圖如下:

找到了下面我們所講到的都基于這幾個(gè)類(lèi)實(shí)現(xiàn),接著上一個(gè)問(wèn)題,追蹤到 AbstractMethod 的抽象方法中,

下面我們繼續(xù) DEBUG 跟蹤代碼是怎么注入的。

首先跳進(jìn)來(lái) AbstractSqlInjector 抽象類(lèi)執(zhí)行 inspectInject 方法

@Override
    public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
        Class<?> modelClass = extractModelClass(mapperClass);
        if (modelClass != null) {
            String className = mapperClass.toString();
            Set<String> mapperRegistryCache = GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration());
            if (!mapperRegistryCache.contains(className)) {
            //獲取 CRUD 實(shí)現(xiàn)類(lèi)列表
                List<AbstractMethod> methodList = this.getMethodList(mapperClass);
                if (CollectionUtils.isNotEmpty(methodList)) {
                    TableInfo tableInfo = TableInfoHelper.initTableInfo(builderAssistant, modelClass);
                    // 循環(huán)注入自定義方法,這里開(kāi)始注入 sql
                    methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo));
                } else {
                    logger.debug(mapperClass.toString() + ", No effective injection method was found.");
                }
                mapperRegistryCache.add(className);
            }
        }
    }

在這里我們找到 inject 方法,跳進(jìn)去

在跳進(jìn)去 injectMappedStatement 方法,選擇你執(zhí)行的 CRUD 操作,我這里以 slectById 為例

從這里我們找到了 addMappedStatement() 方法,可以看到,生成了 SqlSource 對(duì)象,再將 SQL 通過(guò) addSelectMappedStatement 方法添加到 meppedStatements 中。

那么實(shí)現(xiàn)類(lèi)是怎么獲取到的呢?

在 AbstractSqlInjector 抽象類(lèi) inspectInject 方法從 this.getMethodList 方法獲取,如下圖:

這里的 getMethodList 方法獲取 CRUD 實(shí)現(xiàn)類(lèi)列表

/**
 * SQL 默認(rèn)注入器
 *
 * @author hubin
 * @since 2018-04-10
 */
public class DefaultSqlInjector extends AbstractSqlInjector {

    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
        return Stream.of(
            new Insert(),
            new Delete(),
            new DeleteByMap(),
            new DeleteById(),
            new DeleteBatchByIds(),
            new Update(),
            new UpdateById(),
            new SelectById(),
            new SelectBatchByIds(),
            new SelectByMap(),
            new SelectOne(),
            new SelectCount(),
            new SelectMaps(),
            new SelectMapsPage(),
            new SelectObjs(),
            new SelectList(),
            new SelectPage()
        ).collect(toList());
    }
}

從上面的源碼可知,項(xiàng)目啟動(dòng)時(shí),首先由默認(rèn)注入器生成基礎(chǔ) CRUD 實(shí)現(xiàn)類(lèi)對(duì)象,其次遍歷實(shí)現(xiàn)類(lèi)列表,依次注入各自的模板 SQL,最后將其添加至 mappedstatement。

那么 SQL 語(yǔ)句是怎么生成的?此時(shí) SqlSource 通過(guò)解析 SQL 模板、以及傳入的表信息和主鍵信息構(gòu)建出了 SQL 語(yǔ)句,如下所示:

@Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
      /** 定義 mybatis xml method id, 對(duì)應(yīng) <id="xyz"> **/
        SqlMethod sqlMethod = SqlMethod.SELECT_BY_ID;
    /** 構(gòu)造 id 對(duì)應(yīng)的具體 xml 片段 **/
        SqlSource sqlSource = new RawSqlSource(configuration, String.format(sqlMethod.getSql(),
            sqlSelectColumns(tableInfo, false),
            tableInfo.getTableName(), tableInfo.getKeyColumn(), tableInfo.getKeyProperty(),
            tableInfo.getLogicDeleteSql(true, true)), Object.class);
         /** 將 xml method 方法添加到 mybatis 的 MappedStatement 中 **/
        return this.addSelectMappedStatementForTable(mapperClass, getMethod(sqlMethod), sqlSource, tableInfo);
    }

那么數(shù)據(jù)庫(kù)表信息是如何獲取的?主要根據(jù)AbstractSqlInjector抽象類(lèi)的 inspectInject 方法中的initTableInfo方法獲取,如下圖:

/**
     * <p>
     * 實(shí)體類(lèi)反射獲取表信息【初始化】
     * </p>
     *
     * @param clazz 反射實(shí)體類(lèi)
     * @return 數(shù)據(jù)庫(kù)表反射信息
     */
    public synchronized static TableInfo initTableInfo(MapperBuilderAssistant builderAssistant, Class<?> clazz) {
        TableInfo tableInfo = TABLE_INFO_CACHE.get(clazz);
        if (tableInfo != null) {
            if (builderAssistant != null) {
                tableInfo.setConfiguration(builderAssistant.getConfiguration());
            }
            return tableInfo;
        }

        /* 沒(méi)有獲取到緩存信息,則初始化 */
        tableInfo = new TableInfo(clazz);
        GlobalConfig globalConfig;
        if (null != builderAssistant) {
            tableInfo.setCurrentNamespace(builderAssistant.getCurrentNamespace());
            tableInfo.setConfiguration(builderAssistant.getConfiguration());
            globalConfig = GlobalConfigUtils.getGlobalConfig(builderAssistant.getConfiguration());
        } else {
            // 兼容測(cè)試場(chǎng)景
            globalConfig = GlobalConfigUtils.defaults();
        }

        /* 初始化表名相關(guān) */
        final String[] excludeProperty = initTableName(clazz, globalConfig, tableInfo);

        List<String> excludePropertyList = excludeProperty != null && excludeProperty.length > 0 ? Arrays.asList(excludeProperty) : Collections.emptyList();

        /* 初始化字段相關(guān) */
        initTableFields(clazz, globalConfig, tableInfo, excludePropertyList);

        /* 放入緩存 */
        TABLE_INFO_CACHE.put(clazz, tableInfo);

        /* 緩存 lambda */
        LambdaUtils.installCache(tableInfo);

        /* 自動(dòng)構(gòu)建 resultMap */
        tableInfo.initResultMapIfNeed();

        return tableInfo;
    }

分析 initTableName() 方法,獲取表名信息源碼中傳入了實(shí)體類(lèi)信息 class,其實(shí)就是通過(guò)實(shí)體上的@TableName 注解拿到了表名。

我們?cè)诙x實(shí)體類(lèi)的同時(shí),指定了該實(shí)體類(lèi)對(duì)應(yīng)的表名。

那么獲取到表名之后怎么獲取主鍵及其他字段信息呢?主要根據(jù)AbstractSqlInjector抽象類(lèi)的 inspectInject 方法中的initTableFields方法獲取,如下圖:

  /**
     * <p>
     * 初始化 表主鍵,表字段
     * </p>
     *
     * @param clazz        實(shí)體類(lèi)
     * @param globalConfig 全局配置
     * @param tableInfo    數(shù)據(jù)庫(kù)表反射信息
     */
    public static void initTableFields(Class<?> clazz, GlobalConfig globalConfig, TableInfo tableInfo, List<String> excludeProperty) {
        /* 數(shù)據(jù)庫(kù)全局配置 */
        GlobalConfig.DbConfig dbConfig = globalConfig.getDbConfig();
        ReflectorFactory reflectorFactory = tableInfo.getConfiguration().getReflectorFactory();
        //TODO @咩咩 有空一起來(lái)擼完這反射模塊.
        Reflector reflector = reflectorFactory.findForClass(clazz);
        List<Field> list = getAllFields(clazz);
        // 標(biāo)記是否讀取到主鍵
        boolean isReadPK = false;
        // 是否存在 @TableId 注解
        boolean existTableId = isExistTableId(list);
    
        List<TableFieldInfo> fieldList = new ArrayList<>(list.size());
        for (Field field : list) {
            if (excludeProperty.contains(field.getName())) {
                continue;
            }

            /* 主鍵ID 初始化 */
            if (existTableId) {
                TableId tableId = field.getAnnotation(TableId.class);
                if (tableId != null) {
                    if (isReadPK) {
                        throw ExceptionUtils.mpe("@TableId can't more than one in Class: \"%s\".", clazz.getName());
                    } else {
                        isReadPK = initTableIdWithAnnotation(dbConfig, tableInfo, field, tableId, reflector);
                        continue;
                    }
                }
            } else if (!isReadPK) {
                isReadPK = initTableIdWithoutAnnotation(dbConfig, tableInfo, field, reflector);
                if (isReadPK) {
                    continue;
                }
            }

            /* 有 @TableField 注解的字段初始化 */
            if (initTableFieldWithAnnotation(dbConfig, tableInfo, fieldList, field)) {
                continue;
            }

            /* 無(wú) @TableField 注解的字段初始化 */
            fieldList.add(new TableFieldInfo(dbConfig, tableInfo, field));
        }

        /* 檢查邏輯刪除字段只能有最多一個(gè) */
        Assert.isTrue(fieldList.parallelStream().filter(TableFieldInfo::isLogicDelete).count() < 2L,
            String.format("@TableLogic can't more than one in Class: \"%s\".", clazz.getName()));

        /* 字段列表,不可變集合 */
        tableInfo.setFieldList(Collections.unmodifiableList(fieldList));

        /* 未發(fā)現(xiàn)主鍵注解,提示警告信息 */
        if (!isReadPK) {
            logger.warn(String.format("Can not find table primary key in Class: \"%s\".", clazz.getName()));
        }
    }

到處我們知道 SQL 語(yǔ)句是怎么注入的了,如果想要更加深入了解的小伙伴,可以自己根據(jù)上面的源碼方法深入去了解。

到此這篇關(guān)于Mybatis-Plus注入SQL原理分析的文章就介紹到這了,更多相關(guān)Mybatis-Plus注入SQL內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java?對(duì)象深拷貝工具類(lèi)的實(shí)現(xiàn)

    Java?對(duì)象深拷貝工具類(lèi)的實(shí)現(xiàn)

    本文主要介紹了Java?對(duì)象深拷貝工具類(lèi)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • Java基礎(chǔ)教程之Map遍歷的5種方式

    Java基礎(chǔ)教程之Map遍歷的5種方式

    Map作為Java中的一種集合,以鍵值對(duì)的形式存放一批數(shù)據(jù),經(jīng)常會(huì)被我們應(yīng)用在項(xiàng)目中,這篇文章主要給大家介紹了關(guān)于Java基礎(chǔ)教程之Map遍歷的5種方式,需要的朋友可以參考下
    2024-01-01
  • JAVA 筆記 ClassLoader.getResourceAsStream() 與 Class.getResourceAsStream()的區(qū)別

    JAVA 筆記 ClassLoader.getResourceAsStream() 與 Class.getResourc

    這篇文章主要介紹了JAVA 筆記 ClassLoader.getResourceAsStream() 與 Class.getResourceAsStream()的區(qū)別,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-07-07
  • Java 反射調(diào)用靜態(tài)方法的簡(jiǎn)單實(shí)例

    Java 反射調(diào)用靜態(tài)方法的簡(jiǎn)單實(shí)例

    下面小編就為大家?guī)?lái)一篇Java 反射調(diào)用靜態(tài)方法的簡(jiǎn)單實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-06-06
  • Java使用Comparable解決排序問(wèn)題

    Java使用Comparable解決排序問(wèn)題

    這篇文章主要介紹了Java使用Comparable解決排序問(wèn)題的方法,涉及java針對(duì)文件的相關(guān)操作及排序的技巧,需要的朋友可以參考下
    2015-05-05
  • mybatis中的test語(yǔ)句失效處理方式

    mybatis中的test語(yǔ)句失效處理方式

    這篇文章主要介紹了mybatis中的test語(yǔ)句失效處理方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • 解決eclipse啟動(dòng)tomcat時(shí)不能加載web項(xiàng)目的問(wèn)題

    解決eclipse啟動(dòng)tomcat時(shí)不能加載web項(xiàng)目的問(wèn)題

    這篇文章主要介紹了解決eclipse啟動(dòng)tomcat時(shí)不能加載web項(xiàng)目的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • Spring容器中已經(jīng)存在的Bean替換示例

    Spring容器中已經(jīng)存在的Bean替換示例

    這篇文章主要為大家介紹了Spring容器中已經(jīng)存在的Bean替換示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • SpringBoot2.x中management.security.enabled=false無(wú)效的解決

    SpringBoot2.x中management.security.enabled=false無(wú)效的解決

    這篇文章主要介紹了SpringBoot2.x中management.security.enabled=false無(wú)效的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • java實(shí)現(xiàn)雪花算法ID生成器工具類(lèi)

    java實(shí)現(xiàn)雪花算法ID生成器工具類(lèi)

    本文主要介紹了java實(shí)現(xiàn)雪花算法ID生成器工具類(lèi),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07

最新評(píng)論