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

mybatis-plus的批量新增/批量更新以及問題

 更新時間:2023年04月10日 14:21:18   作者:Filwaod  
這篇文章主要介紹了Mybatis-Plus實現(xiàn)批量新增與批量更新以及出現(xiàn)的問題,文章中有詳細的代碼示例,感興趣的同學可以參考一下

現(xiàn)工作中有需求要進行批量新增和修改

實現(xiàn)了以下幾種方式

  • 代碼中foreach insert/update
  • 多線程foreach insert/update
  • mybatis xml中foreach
  • mybatis-plus擴展

第一種就不說了,重復的IO連接與斷開效率極低,性能很差,不考慮
第二種使用多線程進行批量插入/修改,時間會大大降低,但還會有頻繁建立斷開IO,性能不好
第三種其實就是在拼sql,但是不同業(yè)務要拼不同的sql,復用性很差
第四種本質也是拼sql,但是通過簡單的配置就可以達到不同業(yè)務的復用

 1.代碼中foreach insert/update

 for(int i=0;i<insertList.size();i++){
     offerMapper.insert(offerDO);
 }

更新同理

2.多線程foreach insert/update

工作中也使用過多線程批量更新,新增同理

//定義線程池
private static final Long KEEP_ALIVE_TIME = 60L;
private static final int APS = Runtime.getRuntime().availableProcessors();
private static final ThreadPoolExecutor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(
        APS * 2,
        APS * 4,
        KEEP_ALIVE_TIME,
        TimeUnit.SECONDS,
        new LinkedBlockingDeque<>(256),
        new ThreadFactoryBuilder().setNameFormat("分揀出庫-pool-%d").build(),
        new ThreadPoolExecutor.CallerRunsPolicy()
);

//使用
try {
    taskExecute(list, yearList);
} catch (Exception e) {
    log.error("分揀出庫更新失敗:{}", e);
}

// 處理單個任務數(shù)據(jù)(year是分庫分表用的)
private void taskExecute(List<SortingOutboundProductDetailDO> list, List<Integer> yearList) throws Exception {
    if (CollectionUtils.isEmpty(list)) {
        return;
    }

    final CountDownLatch latch = new CountDownLatch(list.size());
    for (SortingOutboundProductDetailDO data : list) {

        THREAD_POOL_EXECUTOR.submit(() -> {
            try {
                //更新從表
                sortingOutboundProductDetailMapper.update(null,
                        new LambdaUpdateWrapper<SortingOutboundProductDetailDO>()
                                .eq(SortingOutboundProductDetailDO::getId, data.getId())
                                .in(SortingOutboundProductDetailDO::getYear, yearList)
                                .set(SortingOutboundProductDetailDO::getOutboundNumber, data.getOutboundNumber())
                );
            } finally {
                if (latch != null) {
                    latch.countDown();
                }
            }
        });
    }
    latch.await();
}

3.mybatis xml中foreach

批量新增

//不用關注里面的業(yè)務代碼
private int insert(OfferSaveRequest request){
    List<OfferDO> insertOffer = request.getOfferList().stream().map(obj -> {
        OfferDO offerDO = new OfferDO();
        offerDO.setId(IdWorker.getId());
        offerDO.setFirstSubjectId(request.getFirstSubjectId());
        offerDO.setWarehouseNum(request.getWarehouseNum());
        offerDO.setExpressCompany(obj.getExpressCompany());
        offerDO.setExpressCompanyName(obj.getExpressCompanyName());
        offerDO.setArea(obj.getArea());
        offerDO.setExpensesItemName(obj.getExpensesItemName());
        offerDO.setUnit(obj.getUnit());
        offerDO.setFees(obj.getFees());
        offerDO.setDescription(obj.getDescription());
        offerDO.setTransportType(generateTransportType(obj.getExpensesItemName()));
        offerDO.setCreateTime(new Date());
        offerDO.setCreateUserId(1L);
        offerDO.setCreateUserName("管理員");
        return offerDO;
    }).collect(Collectors.toList());
    return offerMapper.batchInsert(insertOffer);
}

xml

<insert id="batchInsert" parameterType="com.model.OfferDO">
    INSERT INTO offer(
    id,
    first_subject_id,
    warehouse_num,
    express_company,
    express_company_name,
    area,
    expenses_item_name,
    unit,
    fees,
    description,
    create_time,
    create_user_id,
    create_user_name
    )
    values
    <foreach collection="offerList" separator="," item="offer">
    (
    #{offer.id},
    #{offer.firstSubjectId},
    #{offer.warehouseNum},
    #{offer.expressCompany},
    #{offer.expressCompanyName},
    #{offer.area},
    #{offer.expensesItemName},
    #{offer.unit},
    #{offer.fees},
    #{offer.description},
    #{offer.createTime},
    #{offer.createUserId},
    #{offer.createUserName}
    )
    </foreach>
</insert>

批量修改

//不用關注里面的業(yè)務代碼
List<OfferSaveRequest.Offer> updateList = request.getOfferList().stream()
    .filter(obj -> obj.getId() != null).collect(Collectors.toList());
if (updateList.size() > 0) {
    List<OfferDO> updateOffer = updateList.stream().map(obj -> {
        OfferDO offerDO = new OfferDO();
        offerDO.setId(obj.getId());
        offerDO.setArea(obj.getArea());
        offerDO.setFees(obj.getFees());
        offerDO.setDescription(obj.getDescription());
        offerDO.setUpdateTime(new Date());
        offerDO.setUpdateUserId(1L);
        offerDO.setUpdateUserName("管理員");
        return offerDO;
    }).collect(Collectors.toList());
    offerMapper.batchUpdate(updateOffer);
}

xml

<update id="batchUpdate" parameterType="com.model.OfferDO">
    <foreach collection="offerList" item="offer" separator=";">
        update
        offer
        set
        <if test="offer.area!=null and offer.area!=''">
            area=#{offer.area},
        </if>
        <if test="offer.fees!=null">
            fees=#{offer.fees},
        </if>
        <if test="offer.description!=null and offer.description!=''">
            description=#{offer.description},
        </if>
        update_time=#{offer.updateTime},
        update_user_id=#{offer.updateUserId},
        update_user_name=#{offer.updateUserName}
        where
        id = #{offer.id}
    </foreach>
</update>

批量修改還需要在配置文件中配置&allowMultiQueries=true,否則報錯

spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8&allowMultiQueries=true

4.mybatis-plus擴展

1.創(chuàng)建sql注入器

/**
 * 自定義方法SQL注入器
 * 【注意】這個類名,可以隨便命名
 */
public class MyInjector extends DefaultSqlInjector  {

    /**
     * 如果只需增加方法,保留MyBatis plus自帶方法,
     * 可以先獲取super.getMethodList(),再添加add
     */
    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
        // 注意:此SQL注入器繼承了DefaultSqlInjector(默認注入器),調用了DefaultSqlInjector的getMethodList方法,保留了mybatis-plus的自帶方法
        List<AbstractMethod> methodList = super.getMethodList(mapperClass);
        methodList.add(new InsertBatchMethod());
        methodList.add(new UpdateBatchMethod());
        return methodList;
    }
 
}

2.注入容器

@Configuration
@MapperScan("com.yida.mapper")
public class MybatisPlusPageConfig {

    @Bean
    public MyInjector myInjector(){
        return new MyInjector();
    }
}

3.定義通用mapper

/**
 * 公共mapper
 * 要實現(xiàn)批量新增/修改 繼承此類
 *
 * @param <T>
 */
public interface CommonMapper<T> extends BaseMapper<T> {

    /**
     * 自定義批量插入
     * 如果要自動填充,@Param(xx) xx參數(shù)名必須是 list/collection/array 3個的其中之一
     */
    int insertBatch(@Param("list") List<T> list);

    /**
     * 自定義批量更新,條件為主鍵
     * 如果要自動填充,@Param(xx) xx參數(shù)名必須是 list/collection/array 3個的其中之一
     */
    int updateBatch(@Param("list") List<T> list);
}

4.新增/修改

/**
 * 批量新增
 */
@Slf4j
public class InsertBatchMethod extends AbstractMethod {
    /**
     * insert into user(id, name, age) values (1, "a", 17), (2, "b", 18);
     <script>
     insert into user(id, name, age) values
     <foreach collection="list" item="item" index="index" open="(" separator="),(" close=")">
          #{item.id}, #{item.name}, #{item.age}
     </foreach>
     </script>
     */
    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        final String sql = "<script>insert into %s %s values %s</script>";
        final String fieldSql = prepareFieldSql(tableInfo);
        final String valueSql = prepareValuesSql(tableInfo);
        final String sqlResult = String.format(sql, tableInfo.getTableName(), fieldSql, valueSql);
        log.debug("sqlResult----->{}", sqlResult);
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sqlResult, modelClass);
        // 第三個參數(shù)必須和RootMapper的自定義方法名一致
        return this.addInsertMappedStatement(mapperClass, modelClass, "insertBatch", sqlSource, new NoKeyGenerator(), null, null);
    }
  
    private String prepareFieldSql(TableInfo tableInfo) {
        StringBuilder fieldSql = new StringBuilder();
        fieldSql.append(tableInfo.getKeyColumn()).append(",");
        tableInfo.getFieldList().forEach(x -> {
            //新增時修改字段不填充
            if (!("update_time".equals(x.getColumn()))
                    &&!("update_user_id".equals(x.getColumn()))
                    &&!("update_user_name".equals(x.getColumn()))){
                fieldSql.append(x.getColumn()).append(",");
            }
        });
        fieldSql.delete(fieldSql.length() - 1, fieldSql.length());
        fieldSql.insert(0, "(");
        fieldSql.append(")");
        return fieldSql.toString();
    }
  
    private String prepareValuesSql(TableInfo tableInfo) {
        final StringBuilder valueSql = new StringBuilder();
        valueSql.append("<foreach collection=\"list\" item=\"item\" index=\"index\" open=\"(\" separator=\"),(\" close=\")\">");
        valueSql.append("#{item.").append(tableInfo.getKeyProperty()).append("},");
        tableInfo.getFieldList().forEach(x -> {
            if (!("updateTime".equals(x.getProperty()))
                    &&!("updateUserId".equals(x.getProperty()))
                    &&!("updateUserName".equals(x.getProperty()))){
                valueSql.append("#{item.").append(x.getProperty()).append("},");
            }
        });
        valueSql.delete(valueSql.length() - 1, valueSql.length());
        valueSql.append("</foreach>");
        return valueSql.toString();
    }
}
/**
 * 批量更新方法實現(xiàn),條件為主鍵,選擇性更新
 */
@Slf4j
public class UpdateBatchMethod extends AbstractMethod {
    /**
     * update user set name = "a", age = 17 where id = 1;
     * update user set name = "b", age = 18 where id = 2;
     <script>
        <foreach collection="list" item="item" separator=";">
            update user
            <set>
                <if test="item.name != null and item.name != ''">
                    name = #{item.name,jdbcType=VARCHAR},
                </if>
                <if test="item.age != null">
                    age = #{item.age,jdbcType=INTEGER},
                </if>
            </set>
            where id = #{item.id,jdbcType=INTEGER}
        </foreach>
     </script>
     */
    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        String sql = "<script>\n<foreach collection=\"list\" item=\"item\" separator=\";\">\nupdate %s %s where %s=#{%s} %s\n</foreach>\n</script>";
        String additional = tableInfo.isWithVersion() ? tableInfo.getVersionFieldInfo().getVersionOli("item", "item.") : "" + tableInfo.getLogicDeleteSql(true, true);
        String setSql = sqlSet(tableInfo.isWithLogicDelete(), false, tableInfo, false, "item", "item.");
        String sqlResult = String.format(sql, tableInfo.getTableName(), setSql, tableInfo.getKeyColumn(), "item." + tableInfo.getKeyProperty(), additional);
        log.debug("sqlResult----->{}", sqlResult);
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sqlResult, modelClass);
        // 第三個參數(shù)必須和RootMapper的自定義方法名一致
        return this.addUpdateMappedStatement(mapperClass, modelClass, "updateBatch", sqlSource);
    }
  
}

5.使用,將原有的繼承BaseMapper的方法,改寫為繼承CommonMapper,后續(xù)批量操作,直接使用新增的兩個方法進行處理即可。

public interface OfferMapper extends CommonMapper<OfferDO> {
}

新增:offerMapper.insertBatch(insertOffer)
更新:offerMapper.updateBatch(updateOffer)

not null問題

在實際使用中發(fā)現(xiàn)一個問題,這個批量插入是在項目啟動后就進行拼接好的sql,然后調用的時候,進行值得替換,例

<script>
INSERT INTO express (id,express_name,express_code,state,create_time,create_user_id,create_user_name) VALUES <foreach collection="list" item="et" separator=",">
(#{id},#{expressName},#{expressCode},#{state},#{createTime},#{createUserId},#{createUserName})
</foreach>
</script>

發(fā)現(xiàn)是全量新增,這樣也就產(chǎn)生了一個問題,當只想新增一部分數(shù)據(jù),剩下的一部分數(shù)據(jù)更新進去時,有時會有問題,這取決于你數(shù)據(jù)庫中字段設置是可以為null還是不可以為null。
當數(shù)據(jù)庫中字段設置為not null,而新增的時候傳一個null,就會觸發(fā)數(shù)據(jù)庫的not null校驗,報錯

然后就查資料,改代碼,測試,然后官網(wǎng)上是這樣說的

參考:https://blog.csdn.net/weixin_45505313/article/details/121574166

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.)

在聲明為NOT NULL的列中插入NULL。對于多行INSERT語句或
插入…SELECT語句時,該列被設置為該列的隱式默認值
數(shù)據(jù)類型。數(shù)值類型為0,字符串類型為空字符串("),值為" 0 "
用于日期和時間類型。插入…SELECT語句的處理方式與多行語句相同
插入,因為服務器不檢查SELECT的結果集,看它是否返回
單行。(對于單行INSERT,當NULL插入到NOT NULL列時,不會出現(xiàn)警告。
相反,語句失敗并報錯。)

也就是說mysql允許批量插入時,向not null字段插入null值,mysql會給其賦一個隱藏值
但是在我實測下發(fā)現(xiàn)并不行,然后又開始查資料,被我發(fā)現(xiàn)了這個

那么我就查了一下我的數(shù)據(jù)庫模式

select @@sql_mode;

結果:ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION

可以看出我的數(shù)據(jù)庫模式為嚴格模式,怪不得官網(wǎng)說可以插入null,而我的代碼一直報錯,排坑之旅任重道遠

解決方案:
1.關閉數(shù)據(jù)庫的嚴格模式(公司的數(shù)據(jù)庫沒有權限,這個直接pass掉)
2.手動拼批量插入的sql,拼成如下樣子,然后一次請求執(zhí)行,這樣只進行了一次數(shù)據(jù)庫連接,也可以實現(xiàn)批量插入的效果,但是不知道這種多個INSERT語句與單個INSERT和多個VALUES的性能怎么樣

INSERT INTO `oss` VALUES (1, -1, '', '測試用文件.docx', '', '', 0, '', '2022-12-08 16:21:33', 1, '系統(tǒng)管理員', NULL, -1, '');
INSERT INTO `oss` VALUES (2, -1, '', '測試用文件.docx', '', '', 0, '', '2022-12-08 16:32:32', 1, '系統(tǒng)管理員', NULL, -1, '');
INSERT INTO `oss` VALUES (3, -1, '', '測試用文件.docx', '', '', 0, '', '2022-12-08 16:33:17', 1, '系統(tǒng)管理員', NULL, -1, '');
INSERT INTO `oss` VALUES (4, -1, '', '測試用文件.docx', '', '', 0, '', '2022-12-08 16:44:30', 1, '系統(tǒng)管理員', NULL, -1, '');
INSERT INTO `oss` VALUES (5, -1, '', '測試用文件.docx', '', '', 0, '', '2022-12-08 16:45:28', 1, '系統(tǒng)管理員', NULL, -1, '');
INSERT INTO `oss` VALUES (6, -1, '', '合同導入測試.doc','', '', 0, '', '2022-12-08 16:47:03', 1, '系統(tǒng)管理員', NULL, -1, '');
INSERT INTO `oss` VALUES (7, -1, '', '合同導入測試.doc','', '', 0, '', '2022-12-08 16:48:03', 1, '系統(tǒng)管理員', NULL, -1, '');
INSERT INTO `oss` VALUES (8, -1, '', '測試用文件.docx', '', '', 1, '', '2022-12-08 16:49:35', 1, '系統(tǒng)管理員', NULL, -1, '');
INSERT INTO `oss` VALUES (9, -1, '', '新建文本文檔.doc','', '', 0, '', '2022-12-08 17:12:36', 1, '系統(tǒng)管理員', NULL, -1, '');

以上就是mybatis-plus的批量新增/批量更新以及問題的詳細內容,更多關于mybatis-plus的批量新增/和更新的資料請關注腳本之家其它相關文章!

相關文章

  • 探究springboot中的TomcatMetricsBinder

    探究springboot中的TomcatMetricsBinder

    springboot的TomcatMetricsBinder主要是接收ApplicationStartedEvent然后創(chuàng)建TomcatMetrics執(zhí)行bindTo進行注冊,TomcatMetrics主要注冊了globalRequest、servlet、cache、threadPool、session相關的指標,本文給大家介紹的非常詳細,需要的朋友參考下吧
    2023-11-11
  • java面向對象設計原則之迪米特法則分析詳解

    java面向對象設計原則之迪米特法則分析詳解

    這篇文章主要為大家介紹了java面向對象設計原則之迪米特法則的示例分析詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,學有所得
    2021-10-10
  • java連接數(shù)據(jù)庫(代碼分享)

    java連接數(shù)據(jù)庫(代碼分享)

    本文主要介紹了java連接數(shù)據(jù)庫的實現(xiàn)方法代碼。具有很好的參考價值。下面跟著小編一起來看下吧
    2017-03-03
  • SpringBoot集成WebServlet出現(xiàn)自定義servlet請求失敗的問題解決方案

    SpringBoot集成WebServlet出現(xiàn)自定義servlet請求失敗的問題解決方案

    SpringBoot中以Bean方式注冊Servlet時遇到的問題,通過了解DispatcherServlet的原理,發(fā)現(xiàn)默認路徑?jīng)_突是主要原因,本文介紹SpringBoot集成WebServlet出現(xiàn)自定義servlet請求失敗的問題解決方案,感興趣的朋友一起看看吧
    2025-03-03
  • spring定時任務執(zhí)行兩次及tomcat部署緩慢問題的解決方法

    spring定時任務執(zhí)行兩次及tomcat部署緩慢問題的解決方法

    這篇文章主要給大家介紹了關于spring定時任務執(zhí)行兩次及tomcat部署緩慢問題的解決方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面小編來一起學習學習吧。
    2018-01-01
  • 使用Hibernate根據(jù)實體類自動生成表的方法

    使用Hibernate根據(jù)實體類自動生成表的方法

    這篇文章主要介紹了使用Hibernate根據(jù)實體類自動生成表的方法,該篇提供了兩種方法,可以根據(jù)需要選擇其一,希望對你有所幫助,如有不對的地方還望指正
    2023-03-03
  • 淺談java switch如果case后面沒有break,會出現(xiàn)什么情況?

    淺談java switch如果case后面沒有break,會出現(xiàn)什么情況?

    這篇文章主要介紹了淺談java switch如果case后面沒有break,會出現(xiàn)什么情況?具有很好的參考價值,希望對大家有所幫助。一起跟隨想小編過來看看吧
    2020-09-09
  • Java JVM虛擬機運行機制

    Java JVM虛擬機運行機制

    JVM(Java虛擬機)一種用于計算設備的規(guī)范,可用不同的方式(軟件或硬件)加以實現(xiàn)。接下來通過本文給大家簡單介紹Java JVM虛擬機運行機制,感興趣的朋友一起看看吧
    2017-03-03
  • 自定義一個簡單的JDBC連接池實現(xiàn)方法

    自定義一個簡單的JDBC連接池實現(xiàn)方法

    下面小編就為大家分享一篇自定義一個簡單的JDBC連接池實現(xiàn)方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2017-12-12
  • Java?synchronized輕量級鎖實現(xiàn)過程淺析

    Java?synchronized輕量級鎖實現(xiàn)過程淺析

    這篇文章主要介紹了Java synchronized輕量級鎖實現(xiàn)過程,synchronized是Java里的一個關鍵字,起到的一個效果是"監(jiān)視器鎖",它的功能就是保證操作的原子性,同時禁止指令重排序和保證內存的可見性
    2023-02-02

最新評論

顺昌县| 上栗县| 万年县| 金坛市| 卢氏县| 岫岩| 北宁市| 清水河县| 汕尾市| 黎平县| 泽普县| 兴国县| 巴中市| 万盛区| 永善县| 芜湖市| 高安市| 南漳县| 南丰县| 镇江市| 定安县| 武冈市| 龙游县| 邳州市| 区。| 兴山县| 灵石县| 平定县| 定州市| 隆回县| 冀州市| 江孜县| 陈巴尔虎旗| 大埔县| 高雄市| 梓潼县| 河西区| 东明县| 墨竹工卡县| 临城县| 杭州市|