最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

MyBatis-plus批量插入的通用方法使用

 更新時(shí)間:2023年04月10日 14:16:59   作者:談?wù)?974  
mybatis-plus的IService接口默認(rèn)提供saveBatch批量插入,也是唯一一個(gè)默認(rèn)批量插入,在數(shù)據(jù)量不是很大的情況下可以直接使用,本文帶你詳細(xì)了解MyBatis-plus 批量插入的通用方法及使用方法,需要的朋友可以參考一下

1. MyBatis-plus 的批量保存方法

MyBatis-plus 中默認(rèn)提供了一個(gè)批量保存數(shù)據(jù)到數(shù)據(jù)庫的方法,也就是 IService#saveBatch() 接口方法。這個(gè)方法的實(shí)現(xiàn)為 ServiceImpl#saveBatch(),其源碼實(shí)際處理的關(guān)鍵如下,從中可以知道 IService#saveBatch() 并不是一個(gè)真正的批量插入數(shù)據(jù)的方法

  1. 調(diào)用 ServiceImpl#sqlStatement() 使用 SqlMethod.INSERT_ONE 枚舉結(jié)合實(shí)體類確定一個(gè)全路徑方法名稱,這個(gè)名稱將用于匹配實(shí)體對應(yīng)的庫表的單個(gè)插入方法的 MappedStatement 對象
  2. 調(diào)用 ServiceImpl#executeBatch() 方法遍歷 Entity 的集合,使用單個(gè)插入的方法為每個(gè)實(shí)體組裝一個(gè) INSERT INTO 語句,遍歷結(jié)束后 flush,一次性將所有生成的 INSERT INTO 語句推給數(shù)據(jù)庫執(zhí)行

舉例來說,如果調(diào)用 IService#saveBatch() 方法保存有2個(gè)元素的實(shí)體集合 List<Node> 數(shù)據(jù)到數(shù)據(jù)庫,其執(zhí)行的 SQL 語句如下

存在 2 條:
INSERT INTO node (name, version) VALUES (‘nathan’,1);
INSERT INTO node (name, version) VALUES (‘bob’,1);

而如果是數(shù)據(jù)庫批量插入,其執(zhí)行的 SQL 語句應(yīng)該如下

只有 1 條:
INSERT INTO node (name, version) VALUES (‘nathan’,1), (‘bob’,1);

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean saveBatch(Collection<T> entityList, int batchSize) {
        String sqlStatement = sqlStatement(SqlMethod.INSERT_ONE);
        return executeBatch(entityList, batchSize, (sqlSession, entity) -> sqlSession.insert(sqlStatement, entity));
    }
    
    protected String sqlStatement(SqlMethod sqlMethod) {
        return SqlHelper.table(entityClass).getSqlStatement(sqlMethod.getMethod());
    }
    
    protected <E> boolean executeBatch(Collection<E> list, int batchSize, BiConsumer<SqlSession, E> consumer) {
        Assert.isFalse(batchSize < 1, "batchSize must not be less than one");
        return !CollectionUtils.isEmpty(list) && executeBatch(sqlSession -> {
            int size = list.size();
            int i = 1;
            for (E element : list) {
                consumer.accept(sqlSession, element);
                if ((i % batchSize == 0) || i == size) {
                    sqlSession.flushStatements();
                }
                i++;
            }
        });
    }

2. MyBatis-plus 的批量插入方法

2.1 通用批量插入方法 InsertBatchSomeColumn

事實(shí)上 MyBatis-plus 提供了真正的批量插入方法 InsertBatchSomeColumn,只不過這個(gè)方法只在 MySQL 數(shù)據(jù)庫下測試過,所以沒有將其作為默認(rèn)通用方法添加到 SqlMethod 中

從其源碼實(shí)現(xiàn)不難看出,InsertBatchSomeColumn 其實(shí)就是提供了一個(gè)使用 foreach 標(biāo)簽的 SQL 腳本,不了解這個(gè)標(biāo)簽的讀者參考自定義批量插入大致理解即可

/**
 * 批量新增數(shù)據(jù),自選字段 insert
 * <p> 不同的數(shù)據(jù)庫支持度不一樣!!!  只在 mysql 下測試過!!!  只在 mysql 下測試過!!!  只在 mysql 下測試過!!! </p>
 * <p> 除了主鍵是 <strong> 數(shù)據(jù)庫自增的未測試 </strong> 外理論上都可以使用!!! </p>
 * <p> 如果你使用自增有報(bào)錯(cuò)或主鍵值無法回寫到entity,就不要跑來問為什么了,因?yàn)槲乙膊恢?!! </p>
 * <p>
 * 自己的通用 mapper 如下使用:
 * <pre>
 * int insertBatchSomeColumn(List<T> entityList);
 * </pre>
 * </p>
 *
 * <li> 注意: 這是自選字段 insert !!,如果個(gè)別字段在 entity 里為 null 但是數(shù)據(jù)庫中有配置默認(rèn)值, insert 后數(shù)據(jù)庫字段是為 null 而不是默認(rèn)值 </li>
 *
 * <p>
 * 常用的 {@link Predicate}:
 * </p>
 *
 * <li> 例1: t -> !t.isLogicDelete() , 表示不要邏輯刪除字段 </li>
 * <li> 例2: t -> !t.getProperty().equals("version") , 表示不要字段名為 version 的字段 </li>
 * <li> 例3: t -> t.getFieldFill() != FieldFill.UPDATE) , 表示不要填充策略為 UPDATE 的字段 </li>
 *
 * @author miemie
 * @since 2018-11-29
 */
@NoArgsConstructor
@AllArgsConstructor
public class InsertBatchSomeColumn extends AbstractMethod {

    /**
     * 字段篩選條件
     */
    @Setter
    @Accessors(chain = true)
    private Predicate<TableFieldInfo> predicate;

    @SuppressWarnings("Duplicates")
    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        KeyGenerator keyGenerator = new NoKeyGenerator();
        SqlMethod sqlMethod = SqlMethod.INSERT_ONE;
        List<TableFieldInfo> fieldList = tableInfo.getFieldList();
        String insertSqlColumn = tableInfo.getKeyInsertSqlColumn(false) +
            this.filterTableFieldInfo(fieldList, predicate, TableFieldInfo::getInsertSqlColumn, EMPTY);
        String columnScript = LEFT_BRACKET + insertSqlColumn.substring(0, insertSqlColumn.length() - 1) + RIGHT_BRACKET;
        String insertSqlProperty = tableInfo.getKeyInsertSqlProperty(ENTITY_DOT, false) +
            this.filterTableFieldInfo(fieldList, predicate, i -> i.getInsertSqlProperty(ENTITY_DOT), EMPTY);
        insertSqlProperty = LEFT_BRACKET + insertSqlProperty.substring(0, insertSqlProperty.length() - 1) + RIGHT_BRACKET;
        String valuesScript = SqlScriptUtils.convertForeach(insertSqlProperty, "list", null, ENTITY, COMMA);
        String keyProperty = null;
        String keyColumn = null;
        // 表包含主鍵處理邏輯,如果不包含主鍵當(dāng)普通字段處理
        if (StringUtils.isNotBlank(tableInfo.getKeyProperty())) {
            if (tableInfo.getIdType() == IdType.AUTO) {
                /* 自增主鍵 */
                keyGenerator = new Jdbc3KeyGenerator();
                keyProperty = tableInfo.getKeyProperty();
                keyColumn = tableInfo.getKeyColumn();
            } else {
                if (null != tableInfo.getKeySequence()) {
                    keyGenerator = TableInfoHelper.genKeyGenerator(getMethod(sqlMethod), tableInfo, builderAssistant);
                    keyProperty = tableInfo.getKeyProperty();
                    keyColumn = tableInfo.getKeyColumn();
                }
            }
        }
        String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), columnScript, valuesScript);
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        return this.addInsertMappedStatement(mapperClass, modelClass, getMethod(sqlMethod), sqlSource, keyGenerator, keyProperty, keyColumn);
    }

    @Override
    public String getMethod(SqlMethod sqlMethod) {
        // 自定義 mapper 方法名
        return "insertBatchSomeColumn";
    }
}

2.2 InsertBatchSomeColumn 的使用

由于InsertBatchSomeColumn 是框架已經(jīng)定義好的通用方法,所以使用者只要引入即可,簡單來說只需要進(jìn)行以下幾個(gè)步驟:

  • 新增 SQL 注入器
  • 新增配置類將 SQL 注入器添加到容器
  • 新增基類 Mapper,注意這個(gè)基類中的批量插入方法名稱要和 InsertBatchSomeColumn#getMethod() 方法返回的字符串一致,也就是 insertBatchSomeColumn

具體做法讀者請參考 MyBatis-plus 自定義通用方法及其實(shí)現(xiàn)原理,本文不再贅述

 經(jīng)過以上配置,最終具體的業(yè)務(wù)類 Mapper 只要繼承新增的基類 Mapper 就具備了批量插入的功能,筆者習(xí)慣將 Mapper 封裝在一個(gè) RepositoryService 中對外提供能力,則各個(gè)業(yè)務(wù)類只需要實(shí)現(xiàn)類似如下的 NodeRepositoryServiceImpl#insertBatch() 方法即可以對外提供批量插入的功能

    @Override
    public int insertBatch(List<Node> entityList) {
        if (CollectionUtils.isEmpty(entityList)) {
            return 0;
        }
        return getBaseMapper().insertBatchSomeColumn(entityList);
    }

3. 批量插入 MySQL 數(shù)據(jù)庫的坑

3.1 MySQL 對非 NULL 字段插入 NULL 值的處理

使用 MyBatis-plus 批量插入的方法插入 MySQL 記錄時(shí)需要注意,調(diào)用批量插入的方法一定要保證確實(shí)是要插入多條數(shù)據(jù),如果調(diào)用批量插入的方法只插入了單條數(shù)據(jù),非常有可能遇到非 NULL 字段插入 NULL 值的錯(cuò)誤:

com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'xxx_name' cannot be null

這是因?yàn)槲覀兘柚?Entity 插入數(shù)據(jù)時(shí)經(jīng)常會(huì)忽略一些表中有默認(rèn)值的非 NULL 字段對應(yīng)的屬性的賦值,而從批量插入的 SQL 語句的執(zhí)行角度來看,這樣做也就是往非 NULL 字段插入了 NULL 值。實(shí)際上 MySQL 對于非 NULL 字段插入 NULL 值是有兼容處理的,感興趣的讀者可前往 官方傳送門,本文摘錄如下:

簡單來說,對于插入 NULL 值到非 NULL 字段的情況分為兩種處理方式:

  • 如果是批量插入多條數(shù)據(jù),則會(huì)將 NULL 值轉(zhuǎn)化為默認(rèn)值插到非 NULL 字段(也就是本文批量插入方法插入多條數(shù)據(jù)的情形)
  • 如果是單條數(shù)據(jù)插入,則拋出異常,失敗結(jié)束(對應(yīng)本文批量插入方法只插入了單條數(shù)據(jù)的情形)
Inserting NULL into a column that has been declared NOT NULL. For multiple-row INSERT statements or 
INSERT INTO ... SELECT statements, the column is set to the implicit default value for the column
data type. This is 0 for numeric types, the empty string ('') for string types, and the “zero” value
for date and time types. INSERT INTO ... SELECT statements are handled the same way as multiple-row
inserts because the server does not examine the result set from the SELECT to see whether it returns
a single row. (For a single-row INSERT, no warning occurs when NULL is inserted into a NOT NULL column.
Instead, the statement fails with an error.)

3.2 解決方法

解決方法很簡單,只要在批量插入的時(shí)候判斷一下 Entity 集合的大小即可,如果集合中只有一條數(shù)據(jù),則調(diào)用插入單條數(shù)據(jù)的方法

  • MyBatis-plus 單條數(shù)據(jù)插入之所以不會(huì)有往非 NULL 字段插入 NULL 值的問題,是因?yàn)槠鋯螚l插入數(shù)據(jù)的 SQL 腳本能根據(jù) Entity 的屬性賦值情況動(dòng)態(tài)調(diào)整,對于 Entity 中值為 NULL 的屬性,默認(rèn)不會(huì)將其對應(yīng)的字段添加到執(zhí)行的 SQL 語句中

舉例來說,如 Node 含有兩個(gè)屬性,分別是 name 和 version,則對于屬性值不同的情況最終執(zhí)行的 SQL 語句也不一樣

1. version 為 NULL
INSERT INTO node (name) VALUES (‘nathan’);
2. version 不為 NULL
INSERT INTO node (name, version) VALUES (‘nathan’,1);

    @Override
    public int insertBatch(List<Node> entityList) {
        if (CollectionUtils.isEmpty(entityList)) {
            return 0;
        }
        if (1 == entityList.size()) {
            return getBaseMapper().insert(entityList.get(0));
        }
        return getBaseMapper().insertBatchSomeColumn(entityList);
    }

到此這篇關(guān)于MyBatis-plus批量插入的通用方法使用的文章就介紹到這了,更多相關(guān)MyBatis-plus批量插入方法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 手把手帶你掌握SpringBoot RabbitMQ延遲隊(duì)列

    手把手帶你掌握SpringBoot RabbitMQ延遲隊(duì)列

    RabbitMQ 是一個(gè)由Erlang語言開發(fā)的AMQP的開源實(shí)現(xiàn),支持多種客戶端。用于在分布式系統(tǒng)中存儲(chǔ)轉(zhuǎn)發(fā)消息,在易用性、擴(kuò)展性、高可用性等方面表現(xiàn)不俗,下文將帶你深入了解 RabbitMQ 延遲隊(duì)列
    2021-09-09
  • JAVA中安裝多個(gè)JDK的方法

    JAVA中安裝多個(gè)JDK的方法

    文章介紹了在Windows系統(tǒng)上安裝多個(gè)JDK版本的方法,包括下載、安裝路徑修改、環(huán)境變量配置(JAVA_HOME和Path),并說明如何通過調(diào)整JAVA_HOME在Path中的順序來切換默認(rèn)JDK版本,需重啟cmd生效,感興趣的朋友跟隨小編一起看看吧
    2025-07-07
  • 基于java構(gòu)造方法Vector查找元素源碼分析

    基于java構(gòu)造方法Vector查找元素源碼分析

    本篇文章是關(guān)于ava構(gòu)造方法Vector源碼分析系列文章,本文主要介紹了Vector查找元素的源碼分析,有需要的朋友可以借鑒參考下,希望可以有所幫助
    2021-09-09
  • SpringBoot整合HTTPS的項(xiàng)目實(shí)踐

    SpringBoot整合HTTPS的項(xiàng)目實(shí)踐

    HTTPS的主要作用是通過SSL證書保護(hù)用戶數(shù)據(jù)的安全與隱私,增加網(wǎng)站信任度,防止數(shù)據(jù)被竊取和篡改,保護(hù)網(wǎng)站免受釣魚攻擊,本文就來介紹一下,感興趣的可以了解一下
    2024-10-10
  • java表單提交中文亂碼的解決方法

    java表單提交中文亂碼的解決方法

    這篇文章主要介紹了java表單提交中文亂碼的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-10-10
  • JavaCV簡介與環(huán)境搭建詳細(xì)步驟

    JavaCV簡介與環(huán)境搭建詳細(xì)步驟

    JavaCV是一個(gè)開源的Java接口,它為幾個(gè)著名的計(jì)算機(jī)視覺庫(如OpenCV、FFmpeg)提供了Java封裝,這篇文章主要給大家介紹了關(guān)于JavaCV簡介與環(huán)境搭建的相關(guān)資料,需要的朋友可以參考下
    2024-04-04
  • JAVA異常處理捕獲與拋出原理解析

    JAVA異常處理捕獲與拋出原理解析

    這篇文章主要介紹了JAVA異常處理捕獲與拋出原理解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-09-09
  • spring @Component注解原理解析

    spring @Component注解原理解析

    這篇文章主要介紹了spring @Component注解原理解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-02-02
  • Spring Security6 最新版配置及實(shí)現(xiàn)動(dòng)態(tài)權(quán)限管理

    Spring Security6 最新版配置及實(shí)現(xiàn)動(dòng)態(tài)權(quán)限管理

    Spring Security 在最近幾個(gè)版本中配置的寫法都有一些變化,很多常見的方法都廢棄了,并且將在未來的 Spring Security7 中移除,因此又補(bǔ)充了一些新的內(nèi)容,重新發(fā)一下,供各位使用 Spring Security 的小伙伴們參考,需要的朋友可以參考下
    2024-03-03
  • java 注解默認(rèn)值操作

    java 注解默認(rèn)值操作

    這篇文章主要介紹了java 注解默認(rèn)值操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12

最新評論

沅江市| 瑞安市| 莫力| 交口县| 平乐县| 汪清县| 来凤县| 紫云| 铁力市| 钟山县| 肇源县| 凤凰县| 疏勒县| 合阳县| 晋中市| 仙居县| 澳门| 富源县| 军事| 灵丘县| 孙吴县| 宝山区| 固安县| 米脂县| 微山县| 德兴市| 塔河县| 达日县| 工布江达县| 延庆县| 禄劝| 平凉市| 淅川县| 遂昌县| 鄯善县| 莱芜市| 延庆县| 青川县| 桦川县| 香港| 龙岩市|