欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

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

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

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

舉例來說,如果調(diào)用 IService#saveBatch() 方法保存有2個元素的實(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,只不過這個方法只在 MySQL 數(shù)據(jù)庫下測試過,所以沒有將其作為默認(rèn)通用方法添加到 SqlMethod 中

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

/**
 * 批量新增數(shù)據(jù),自選字段 insert
 * <p> 不同的數(shù)據(jù)庫支持度不一樣!!!  只在 mysql 下測試過!!!  只在 mysql 下測試過!!!  只在 mysql 下測試過!!! </p>
 * <p> 除了主鍵是 <strong> 數(shù)據(jù)庫自增的未測試 </strong> 外理論上都可以使用!!! </p>
 * <p> 如果你使用自增有報錯或主鍵值無法回寫到entity,就不要跑來問為什么了,因?yàn)槲乙膊恢?!! </p>
 * <p>
 * 自己的通用 mapper 如下使用:
 * <pre>
 * int insertBatchSomeColumn(List<T> entityList);
 * </pre>
 * </p>
 *
 * <li> 注意: 這是自選字段 insert !!,如果個別字段在 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)行以下幾個步驟:

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

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

 經(jīng)過以上配置,最終具體的業(yè)務(wù)類 Mapper 只要繼承新增的基類 Mapper 就具備了批量插入的功能,筆者習(xí)慣將 Mapper 封裝在一個 RepositoryService 中對外提供能力,則各個業(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 記錄時需要注意,調(diào)用批量插入的方法一定要保證確實(shí)是要插入多條數(shù)據(jù),如果調(diào)用批量插入的方法只插入了單條數(shù)據(jù),非常有可能遇到非 NULL 字段插入 NULL 值的錯誤:

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

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

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

  • 如果是批量插入多條數(shù)據(jù),則會將 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 解決方法

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

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

舉例來說,如 Node 含有兩個屬性,分別是 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)文章

  • SpringBean依賴和三級緩存的案例講解

    SpringBean依賴和三級緩存的案例講解

    這篇文章主要介紹了SpringBean依賴和三級緩存的案例講解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • 詳解IDEA中SpringBoot整合Servlet三大組件的過程

    詳解IDEA中SpringBoot整合Servlet三大組件的過程

    這篇文章主要介紹了詳解IDEA中SpringBoot整合Servlet三大組件的過程,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-11-11
  • Spring CGLlB動態(tài)代理實(shí)現(xiàn)過程解析

    Spring CGLlB動態(tài)代理實(shí)現(xiàn)過程解析

    這篇文章主要介紹了Spring CGLlB動態(tài)代理實(shí)現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-10-10
  • Java編程實(shí)現(xiàn)中英混合字符串?dāng)?shù)組按首字母排序的方法

    Java編程實(shí)現(xiàn)中英混合字符串?dāng)?shù)組按首字母排序的方法

    這篇文章主要介紹了Java編程實(shí)現(xiàn)中英混合字符串?dāng)?shù)組按首字母排序的方法,涉及Java字符串操作及拼音轉(zhuǎn)換的相關(guān)使用技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-11-11
  • 深入淺析JSON在java中的使用

    深入淺析JSON在java中的使用

    這篇文章主要介紹了JSON在java中的使用,包括javaBean和json的互轉(zhuǎn),List 和 json 的互轉(zhuǎn)及map 和 json 的互轉(zhuǎn),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2021-04-04
  • SpringBoot啟用GZIP壓縮的代碼工程

    SpringBoot啟用GZIP壓縮的代碼工程

    經(jīng)常我們都會與服務(wù)端進(jìn)行大數(shù)據(jù)量的文本傳輸,例如?JSON?就是常見的一種格式,通過?REST?API?接口進(jìn)行?GET?和?POST?請求,可能會有大量的文本格式數(shù)據(jù)提交、返回,壓縮和解壓在提升網(wǎng)絡(luò)帶寬的同時,會帶來?CPU?資源的損耗,本文介紹了SpringBoot啟用GZIP壓縮的代碼工程
    2024-08-08
  • Java中零拷貝和深拷貝的原理及實(shí)現(xiàn)探究(代碼示例)

    Java中零拷貝和深拷貝的原理及實(shí)現(xiàn)探究(代碼示例)

    深拷貝和零拷貝是兩個在 Java 中廣泛使用的概念,它們分別用于對象復(fù)制和數(shù)據(jù)傳輸優(yōu)化,下面將詳細(xì)介紹這兩個概念的原理,并給出相應(yīng)的 Java 代碼示例,感興趣的朋友一起看看吧
    2023-12-12
  • Java函數(shù)式編程(十一):遍歷目錄

    Java函數(shù)式編程(十一):遍歷目錄

    這篇文章主要介紹了Java函數(shù)式編程(十一):遍歷目錄,本文是系列文章的第11篇,其它文章請參閱本文底部的相關(guān)文章,需要的朋友可以參考下
    2014-09-09
  • Spring Boot日志的打印與持久化詳細(xì)解析

    Spring Boot日志的打印與持久化詳細(xì)解析

    Spring Boot默認(rèn)使用SLF4J+Logback 記錄日志,并提供了默認(rèn)配置,即使我們不進(jìn)行任何額外配,也可以使用SLF4J+Logback進(jìn)行日志輸出
    2022-07-07
  • java.lang.FileNotFoundException 異常的正確解決方法(親測有效)

    java.lang.FileNotFoundException 異常的正確解決方法(親測有效)

    java.io.FileNotFoundException是一個在文件操作過程中常見的異常,它屬于IOException的一個子類,這篇文章主要介紹了java.lang.FileNotFoundException 異常的正確解決方法(親測有效),需要的朋友可以參考下
    2024-01-01

最新評論