MyBatis-plus的批量插入方式对比分析

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

MyBatis-plus的批量插入方式对比分析

  【摘要】Mybatis批量插入一直是开发者重点关注的问题本文列举了Mybatis的五种插入方式进行对比分析验证了五种批量插入的方式的优先级。

1 准备工作

1.1 新建spring项目

  略。

1.2 导入pom.xml依赖

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

<!--Mybatis依赖-->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.2</version>
</dependency>

<!--Mybatis-Plus依赖-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.2</version>
</dependency>

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

1.3 配置yml文件

server:
  port: 8080
 
spring:
  datasource:
    username: mysql用户名
    password: mysql密码
    url: jdbc:mysql://localhost:3306/数据库名字?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
    driver-class-name: com.mysql.cj.jdbc.Driver
 
mybatis:
  mapper-locations: classpath:mapping/*.xml

1.4 创建插入模型

@Data
public class User {
    private int id;
    private String username;
    private String password;
}

2 测试

2.1 Mybatis利用For循环批量插入

1、编写UserService服务类测试一万条数据的耗时情况

@Service
public class UserService {
 
    @Resource
    private UserMapper userMapper;
 
    public void InsertUsers(){
        long start = System.currentTimeMillis();
        for(int i = 0 ;i < 10000; i++) {
            User user = new User();
            user.setUsername("name" + i);
            user.setPassword("password" + i);
            userMapper.insertUsers(user);
        }
        long end = System.currentTimeMillis();
        System.out.println("一万条数据总耗时" + (end-start) + "ms" );
    }
 
}

2、编写UserMapper接口

@Mapper
public interface UserMapper {
 
    Integer insertUsers(User user);
}

3、编写UserMapper.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.ithuang.demo.mapper.UserMapper">
    <insert id="insertUsers">
        INSERT INTO user (username, password)
        VALUES(#{username}, #{password})
    </insert>
</mapper>

4、进行单元测试

@SpringBootTest
class DemoApplicationTests {
 
    @Resource
    private UserService userService;
 
    @Test
    public void insert(){
        userService.InsertUsers();
    }
}

5、输出结果
  一万条数据耗时26348ms

2.2 MyBatis的手动批量提交

1、其他保持不变Service层作稍微的变化

@Service
public class UserService {
 
    @Resource
    private UserMapper userMapper;
 
    @Resource
    private SqlSessionTemplate sqlSessionTemplate;
 
    public void InsertUsers(){
        //关闭自动提交
        SqlSession sqlSession = sqlSessionTemplate.getSqlSessionFactory().openSession(ExecutorType.BATCH, false);
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        long start = System.currentTimeMillis();
        for(int i = 0 ;i < 10000; i++) {
            User user = new User();
            user.setUsername("name" + i);
            user.setPassword("password" + i);
            userMapper.insertUsers(user);
        }
        sqlSession.commit();
        long end = System.currentTimeMillis();
        System.out.println("一万条数据总耗时" + (end-start) + "ms" );
    }
}

2、结果输出
  一万条数据总耗时24516ms。

2.3 Mybatis以集合方式批量新增

1、编写UserService服务类

@Service
public class UserService {
    @Resource
    private UserMapper userMapper;
    public void InsertUsers(){
        long start = System.currentTimeMillis();
        List<User> userList = new ArrayList<>();
        User user;
        for(int i = 0 ;i < 10000; i++) {
            user = new User();
            user.setUsername("name" + i);
            user.setPassword("password" + i);
            userList.add(user);
        }
        userMapper.insertUsers(userList);
        long end = System.currentTimeMillis();
        System.out.println("一万条数据总耗时" + (end-start) + "ms" );
    }
}

2、编写UserMapper接口

@Mapper
public interface UserMapper {
    Integer insertUsers(List<User> userList);
}

3、编写UserMapper.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.ithuang.demo.mapper.UserMapper">
    <insert id="insertUsers">
        INSERT INTO user (username, password)
        VALUES
        <foreach collection ="userList" item="user" separator =",">
            (#{user.username}, #{user.password})
        </foreach>
    </insert>
</mapper>

4、输出结果
  一万条数据总耗时521ms

2.4 MyBatis-Plus提供的SaveBatch方法

1、编写UserService服务

@Service
public class UserService extends ServiceImpl<UserMapper, User> implements IService<User> {
 
    public void InsertUsers(){
        long start = System.currentTimeMillis();
        List<User> userList = new ArrayList<>();
        User user;
        for(int i = 0 ;i < 10000; i++) {
            user = new User();
            user.setUsername("name" + i);
            user.setPassword("password" + i);
            userList.add(user);
        }
        saveBatch(userList);
        long end = System.currentTimeMillis();
        System.out.println("一万条数据总耗时" + (end-start) + "ms" );
    }
}

2、编写UserMapper接口

@Mapper
public interface UserMapper extends BaseMapper<User> {
 
}

3、单元测试结果

  一万条数据总耗时24674ms

2.5 MyBatis-Plus提供的InsertBatchSomeColumn方法

1、编写EasySqlInjector 自定义类

public class EasySqlInjector extends DefaultSqlInjector {
 
 
    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
        // 注意此SQL注入器继承了DefaultSqlInjector(默认注入器)调用了DefaultSqlInjector的getMethodList方法保留了mybatis-plus的自带方法
        List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo);
        methodList.add(new InsertBatchSomeColumn(i -> i.getFieldFill() != FieldFill.UPDATE));
        return methodList;
    }
 
}

2、定义核心配置类注入此Bean

@Configuration
public class MybatisPlusConfig {

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

}
3、编写UserService服务类

public class UserService{
 
    @Resource
    private UserMapper userMapper;
    public void InsertUsers(){
        long start = System.currentTimeMillis();
        List<User> userList = new ArrayList<>();
        User user;
        for(int i = 0 ;i < 10000; i++) {
            user = new User();
            user.setUsername("name" + i);
            user.setPassword("password" + i);
            userList.add(user);
        }
        userMapper.insertBatchSomeColumn(userList);
        long end = System.currentTimeMillis();
        System.out.println("一万条数据总耗时" + (end-start) + "ms" );
    }
}

4、编写EasyBaseMapper接口

public interface EasyBaseMapper<T> extends BaseMapper<T> {
    /**
     * 批量插入 仅适用于mysql
     *
     * @param entityList 实体列表
     * @return 影响行数
     */
    Integer insertBatchSomeColumn(Collection<T> entityList);
}

5、编写UserMapper接口

@Mapper
public interface UserMapper<T> extends EasyBaseMapper<User> {
    
}

6、单元测试结果

  一万条数据总耗时575ms

2.6 JDBC原生的批量插入

1、编写JDBC池化工具类

public class JDBCDruidUtils {
    private static DataSource dataSource;

    private static Connection conn;

    /*
   创建数据Properties集合对象加载加载配置文件
    */
    static {
        Properties pro = new Properties();
        //加载数据库连接池对象
        try {
            //获取数据库连接池对象
            pro.load(JDBCDruidUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
            dataSource = DruidDataSourceFactory.createDataSource(pro);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /*
    获取连接
     */
    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }

    /**
     * 关闭conn,和 statement独对象资源
     *
     * @param connection
     * @param statement
     * @MethodName: close
     * @return: void
     */
    public static void close(Connection connection, Statement statement) {
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 关闭 conn , statement 和resultset三个对象资源
     *
     * @param connection
     * @param statement
     * @param resultSet
     * @MethodName: close
     * @return: void
     */
    public static void close(Connection connection, Statement statement, ResultSet resultSet) {
        close(connection, statement);
        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException e) {             e.printStackTrace();
            }
        }
    }
    /*
    获取连接池对象
     */
    public static DataSource getDataSource() {
        return dataSource;
    }
}
# druid.properties配置
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/数据库?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
username=用户名
password=密码
initialSize=10
maxActive=50
maxWait=60000

2、编写UserService服务类

public void InsertUsersByJdbc() {
        long start = System.currentTimeMillis();
        Connection connection = null;
        PreparedStatement ps = null;
        try {
            connection = JDBCDruidUtils.getConnection();
            //控制事务:默认不提交
            connection.setAutoCommit(false);
            String sql = "INSERT INTO user (username, password) VALUES (?, ?)";
            ps = connection.prepareStatement(sql);
            User user;
            for (int i = 0; i < 10000; i++) {
                user = new User();
                user.setUsername("name" + i);
                user.setPassword("password" + i);
                ps.setString(1, user.getUsername());
                ps.setString(2, user.getPassword());
                //将一组参数添加到此 PreparedStatement 对象的批处理命令中。
                ps.addBatch();
            }
            //执行批处理
            ps.executeBatch();
            //手动提交事务
            connection.commit();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            JDBCDruidUtils.close(connection, ps);
        }
        long end = System.currentTimeMillis();
        System.out.println("一万条数据总耗时" + (end - start) + "ms");
    }

3、输出结果
  1万数据总耗时19000ms。

3 总结

  大量数据的场景下性能对比InsertBatchSomeColumn>自定义xml以集合的方式>Jdbc原生>SaveBatch>手动for循环批量>自动for循环批量。
  网上很多人都说JDBC原生性能很好但是我发现其非常差有可能是我使用的是mybatis-plus依赖如果这是推论正确那就可以证明mybatis-plus在mybatis的基础上不仅增强了功能也增强了性能。所以可以得出结论开发中用mybatis-plus是没有错的如果想提高性能只能实施其他方案比如分库分表千万别想着JDBC原生性能更好。

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

“MyBatis-plus的批量插入方式对比分析” 的相关文章

vue.prototype和vue.use的区别和注意点有哪些 - 开发技术

本篇内容介绍了“vue.prototype和vue.use的区别和注意点有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!vue.prototype和vue.use的...

【CTF】CTF竞赛介绍以及刷题网址

CTFCapture The Flag中文一般译作夺旗赛在网络安全领域中指的是网络安全技术人员之间进行技术竞技的一种比赛形式。CTF起源于1996年DEFCON全球黑客大会以代替之前黑客们通过互相发起真实攻击进行技术比拼的方式。发展至今已经成为全球范围网络安全圈流行的竞赛形式2013年全球举办...

springboot启动时怎么指定spring.profiles.active - 开发技术

这篇文章主要介绍“springboot启动时怎么指定spring.profiles.active”,在日常操作中,相信很多人在springboot启动时怎么指定spring.profiles.active问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希...

php怎么将ymd格式的日期转为时间戳 - 编程语言

本文小编为大家详细介绍“php怎么将ymd格式的日期转为时间戳”,内容详细,步骤清晰,细节处理妥当,希望这篇“php怎么将ymd格式的日期转为时间戳”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。 一、 什么是时间...

Java基础编程题50道题

1、題目古典问题有一对兔子从出生后第3个月起每个月都生一对兔子小兔子长到第三个月后每个月又生一对兔子假如兔子都不死问每个月的兔子总数为多少 思路微笑刚开始真的无从下手这么难的怎么可以说是基础呢感觉这些应该是逻辑分析很强的第一个月只有一对兔子第二个月还是只有一对兔子第三个月就有两对了第四个月3对第五...

JavaScript中的垃圾回收机制怎么理解 - 开发技术

今天小编给大家分享一下JavaScript中的垃圾回收机制怎么理解的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。JavaScript中的垃圾回收机制负责...