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

Mybatis-Plus批量插入用法詳解

 更新時間:2023年02月15日 09:55:24   作者:魚找水需要時間  
mybatis-plus的IService接口默認(rèn)提供saveBatch批量插入,也是唯一一個默認(rèn)批量插入,在數(shù)據(jù)量不是很大的情況下可以直接使用,但這種是一條一條執(zhí)行的效率上會有一定的瓶頸,今天我們就來研究研究mybatis-plus中的批量插入

mybatis-plusIService接口默認(rèn)提供saveBatch批量插入,也是唯一一個默認(rèn)批量插入,在數(shù)據(jù)量不是很大的情況下可以直接使用,但這種是一條一條執(zhí)行的效率上會有一定的瓶頸,今天我們就來研究研究mybatis-plus中的批量插入。

1. 準(zhǔn)備測試環(huán)境

新建一個測試表,用插入5000條數(shù)據(jù)來測試

CREATE TABLE `users` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
  `user_name` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  `address` varchar(255) DEFAULT NULL,
  `account` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=60204 DEFAULT CHARSET=utf8;

pom依賴

 <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.7.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>2.7.2</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.10</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.13</version>
        </dependency>
    </dependencies>

yml配置

spring:
  application:
    name: example-server
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/my_user?useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&serverTimezone=Asia/Shanghai
    username: root
    password: root
  main:
    allow-circular-references: true
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #開啟SQL語句打印

實體類

/**
 * @description: 默認(rèn)駝峰轉(zhuǎn)換
 * @author: yh
 * @date: 2022/8/29
 */
@Data
public class Users extends Model<Users> {
    /**
     * id自增
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    private LocalDateTime createTime;
    private String userName;
    private Integer age;
    private String email;
    private String address;
    private String account;
    private String password;
}

UsersMapper

public interface UsersMapper extends BaseMapper<Users> {

}

UsersMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UsersMapper">

</mapper>

service接口

public interface UsersService extends IService<Users> {

}

service實現(xiàn)

@Service
public class UsersServiceImpl extends ServiceImpl<UsersMapper, Users> implements UsersService {
   
}

配置掃描

@MapperScan("com.example.mapper")
@SpringBootApplication
public class SpringExampleApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringExampleApplication.class, args);
    }
}

環(huán)境準(zhǔn)備完成。

2. saveBatch

@RequestMapping(value = "/api")
@RestController
@Slf4j
public class ExampleController {
    @Autowired
    private UsersService usersService;

    @RequestMapping(value = "/load", method = RequestMethod.GET)
    public void load() {
        List<Users> list = new ArrayList<>();
        for (int i = 0; i < 5000; i++) {
            Users users = new Users();

            users.setUserName("yy" + i);
            users.setAge(18);
            users.setEmail("123@qq.com");
            users.setAddress("臨汾" + i);
            users.setAccount("account" + i);
            users.setPassword("password" + i);
            list.add(users);
        }
        long start = System.currentTimeMillis();
        usersService.saveBatch(list);
        long end = System.currentTimeMillis();
        System.out.println("5000條數(shù)據(jù)插入,耗時:" + (end - start));
    }
}

執(zhí)行過程:

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

5000條數(shù)據(jù)被分成了5次執(zhí)行,每次1000條,整體是一個事務(wù)

在這里插入圖片描述

在這里插入圖片描述

耗時:48.5

2.1 分析

點進(jìn)saveBatch方法,看看內(nèi)部是怎么實現(xiàn)的

在這里插入圖片描述

注意看,方法有一個事務(wù)注解,說明插入整批數(shù)據(jù)會作為一個事務(wù)進(jìn)行

在這里插入圖片描述

默認(rèn)1000條一次,怪不得執(zhí)行時每隔1000條會處于一個準(zhǔn)備執(zhí)行狀態(tài),等待幾秒后才會往下執(zhí)行(這里等待的時間就是1000個單條的insert語句執(zhí)行的時間)

再往下點是一個saveBatch接口,參數(shù)分別是插入的對象集合、插入批次數(shù)量也就是默認(rèn)的1000

在這里插入圖片描述

查看它的實現(xiàn)類

在這里插入圖片描述

這里也有一個事務(wù)的注解,這是因為saveBatch是一個重載方法,插入的時候也可以指定插入批次數(shù)量調(diào)用

在這里插入圖片描述

繼續(xù)往下進(jìn)入executeBatch

在這里插入圖片描述

再往下

在這里插入圖片描述

這里就是真正執(zhí)行的方法了,idxLimit會對比DEFAULT_BATCH_SIZE和集合長度兩個數(shù)中的最小數(shù),作為批量大小,也就是說當(dāng)集合長度不夠1000,那么執(zhí)行的時候批量大小就是集合的長度,就執(zhí)行一次。

for循環(huán)中的consumer:對應(yīng)的類型是一個函數(shù)式接口,代表一個接受兩個輸入?yún)?shù)且不返回任何內(nèi)容的操作符。意思是給定兩個參數(shù)sqlSession、循環(huán)中當(dāng)前element對象,執(zhí)行一次傳遞過來的consumer匿名函數(shù)。也就是上邊源碼里的插入匿名函數(shù)。

在這里插入圖片描述

當(dāng)i == indLimit時:執(zhí)行一次預(yù)插入,并重新計算idxLimit的值

ifidxLimit計算規(guī)則:當(dāng)前idxLimitbatchSize(默認(rèn)1000) 和 集合長度 取最小值,計算出來的結(jié)果肯定不會超過集合的長度,最后的批次時idxLimit等于集合的長度,將這個值作為下一次執(zhí)行預(yù)插入的時間點。

sqlSession.flushStatements():當(dāng)有處于事務(wù)中的時候,起到一種預(yù)插入的作用,執(zhí)行了這行代碼之后,要插入的數(shù)據(jù)會鎖定數(shù)據(jù)庫的一行記錄,并把數(shù)據(jù)庫默認(rèn)返回的主鍵賦值給插入的對象,這樣就可以把該對象的主鍵賦值給其他需要的對象中去了,這里不是事務(wù)提交啊。

最后方法執(zhí)行完后@Transactional注解會默認(rèn)提交事務(wù),如果調(diào)用的方法上還有@Transactional注解,默認(rèn)的事務(wù)傳播類型是Propagation.REQUIRED,不會新開啟事務(wù),如果沒有@Transactional注解才會新開起事務(wù)

解析spring事務(wù)管理@Transactional為什么要添加rollbackFor=Exception.class

3. insert循環(huán)插入

把數(shù)據(jù)庫中的數(shù)據(jù)清空,還原到空表狀態(tài)

@Service
public class UsersServiceImpl extends ServiceImpl<UsersMapper, Users> implements UsersService {
    @Override
    public void insertList() {
        List<Users> list = new ArrayList<>();
        for (int i = 0; i < 5000; i++) {
            Users users = new Users();

            users.setUserName("yy" + i);
            users.setAge(18);
            users.setEmail("123@qq.com");
            users.setAddress("臨汾" + i);
            users.setAccount("1" + i);
            users.setPassword("pw" + i);
            list.add(users);
        }
        long start = System.currentTimeMillis();
        for (int i = 0; i < list.size(); i++) {
            baseMapper.insert(list.get(i));
        }
        long end = System.currentTimeMillis();
        System.out.println("5000條數(shù)據(jù)插入,耗時:" + (end - start));
    }
}

在這里插入圖片描述

5000條數(shù)據(jù)每條都是一個單獨的事務(wù)。就是一條條插入,成功失敗都不會互相影響
耗時:161.2

4. 自定義sql插入

UsersMapper.xml

    <insert id="insertList">
        insert into users(user_name, age, email, address, account, password) values
        <foreach collection="list" item="it" separator=",">
            (#{it.userName}, #{it.age}, #{it.email}, #{it.address}, #{it.account}, #{it.password})
        </foreach>
    </insert>

UsersMapper

 void insertList(@Param("list") List<Users> list);
@Service
public class UsersServiceImpl extends ServiceImpl<UsersMapper, Users> implements UsersService {
    @Override
    public void insertList() {
        List<Users> list = new ArrayList<>();
        for (int i = 0; i < 5000; i++) {
            Users users = new Users();

            users.setUserName("yy" + i);
            users.setAge(18);
            users.setEmail("123@qq.com");
            users.setAddress("臨汾" + i);
            users.setAccount("1" + i);
            users.setPassword("pw" + i);
            list.add(users);
        }
        long start = System.currentTimeMillis();
        baseMapper.insertList(list);
        long end = System.currentTimeMillis();
        System.out.println("5000條數(shù)據(jù)插入,耗時:" + (end - start));
    }
}

在這里插入圖片描述

這里是把要插入的數(shù)據(jù)拼接在一個insert語句后面執(zhí)行

INSERT INTO users (user_name,age,email,address,account,password) 
VALUES (?,?,?,?,?,?) , (?,?,?,?,?,?) , (?,?,?,?,?,?).....

這種效率是最高的,但是這種需要我們在每個批量插入對應(yīng)的xml中取寫sql語句,有點不太符合現(xiàn)在提倡的免sql開發(fā),下面介紹一下它的升級版

5. insertBatchSomeColumn

mybatis-plus提供了InsertBatchSomeColumn批量insert方法。通過SQL 自動注入器接口 ISqlInjector注入通用方法 SQL 語句 然后繼承 BaseMapper 添加自定義方法,全局配置 sqlInjector 注入 MP 會自動將類所有方法注入到 mybatis 容器中。我們需要通過這種方式注入下。

MySqlInjector.java

/**
 * 自定義Sql注入
 * @author:  yh
 * @date:  2022/8/30
 */
public class MySqlInjector extends DefaultSqlInjector {

    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
        List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo);
        //增加自定義方法,字段注解上不等于FieldFill.DEFAULT的字段才會插入
        methodList.add(new InsertBatchSomeColumn(i -> i.getFieldFill() != FieldFill.DEFAULT));
        return methodList;
    }
}

MybatisPlusConfig.java

@Configuration
public class MybatisPlusConfig {

    @Bean
    public MySqlInjector sqlInjector() {
        return new MySqlInjector();
    }
}

自定義MyBaseMapper

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

    int insertBatchSomeColumn(List<T> entityList);
}

mapper繼承的這里也改下

public interface UsersMapper extends MyBaseMapper<Users> {

}

插入的時候過濾字段,需要配置屬性

@Data
public class Users extends Model<Users> {
    /**
     * id
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    // 插入的時候過濾這個字段,默認(rèn)值就是FieldFill.DEFAULT
    @TableField(fill = FieldFill.DEFAULT)
    private LocalDateTime createTime;

    @TableField(fill = FieldFill.INSERT)
    private String userName;

    @TableField(fill = FieldFill.INSERT)
    private Integer age;
    @TableField(fill = FieldFill.INSERT)
    private String email;
    @TableField(fill = FieldFill.INSERT)
    private String address;
    @TableField(fill = FieldFill.INSERT)
    private String account;
    @TableField(fill = FieldFill.INSERT)
    private String password;
}

修改一下service調(diào)用

@Service
public class UsersServiceImpl extends ServiceImpl<UsersMapper, Users> implements UsersService {
    @Autowired
    private SqlSessionFactory sqlSessionFactory;
    @Override
    public void insertList() {
        List<Users> list = new ArrayList<>();
        for (int i = 0; i < 5000; i++) {
            Users users = new Users();

            users.setUserName("yy" + i);
            users.setAge(18);
            users.setEmail("123@qq.com");
            users.setAddress("臨汾" + i);
            users.setAccount("1" + i);
            users.setPassword("pw" + i);
            list.add(users);
        }
        long start = System.currentTimeMillis();
        baseMapper.insertBatchSomeColumn(list);
        long end = System.currentTimeMillis();
        System.out.println("5000條數(shù)據(jù)插入,耗時:" + (end - start));
    }
}

運行結(jié)果:

在這里插入圖片描述

createTime字段被過濾掉了

在這里插入圖片描述

在這里插入圖片描述

執(zhí)行時間和上邊自定義sql一樣,在1秒內(nèi)浮動,如果量太大幾十萬條建議多線程分批處理。

參考文章

Java實現(xiàn)多線程大批量同步數(shù)據(jù)(分頁)

到此這篇關(guān)于Mybatis-Plus批量插入應(yīng)該怎么用的文章就介紹到這了,更多相關(guān)Mybatis-Plus批量插入內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論