MyBatis:批量添加记录

news2024/11/15 19:41:28

        MyBatis,一款优秀的ORM映射框架,可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。同时,MyBatis提供了动态SQL特性,这为我们编写代码按条件拼接SQL提供了极大地方便。但是,这也并不意味着我们可以随意使用这种特性来应对不同的业务逻辑。

        以批量添加记录为例,我们通常会按照如下的思路去做:

        ①创建数据表,并编写Pojo实体类;

        ②编写Mapper接口,提供XML配置文件,通过<insert>标签+动态SQL实现批量添加数据;

        ③编写Service层接口及其实现子类;

        ④编写Controller层接口,提供接口访问路径,进行接口测试。

        在数据量较小的场景下,上述思路是没有问题的,但是当批量添加的数据量达到十几万、几十万、上百万时,那就要开始考虑是否会导致JVM堆内存溢出(java.lang.OutOfMemoryError: Java heap space)的问题了。——毕竟JVM可利用的堆区内存有限,然后瞬间的高额内存输出必然会导致内存溢出。

        如何更为优雅的实现批量添加数据呢?以下,以批量添加People人员信息记录为例,提供个人应对方案,也欢迎大家补充指正(PS:数据库为PostGreSQL;后端使用MyBatis持久层框架)。

数据表设计tb_people

        数据表的创建SQL语句如下(geometry对应几何类型),

-- Table: public.tb_people

-- DROP TABLE IF EXISTS public.tb_people;

CREATE TABLE IF NOT EXISTS public.tb_people
(
    id integer NOT NULL DEFAULT nextval('tb_people_gid_seq'::regclass),
    name character varying COLLATE pg_catalog."default",
    id_card character varying(18) COLLATE pg_catalog."default",
    age integer,
    gender character(1) COLLATE pg_catalog."default",
    occupation character varying COLLATE pg_catalog."default",
    tel character varying(11) COLLATE pg_catalog."default",
    house_id integer,
    floor integer,
    house_card character varying COLLATE pg_catalog."default",
    geom geometry,
    is_holder integer,
    CONSTRAINT tb_people_primary_key PRIMARY KEY (id)
)
WITH (
    OIDS = FALSE
)
TABLESPACE pg_default;

ALTER TABLE IF EXISTS public.tb_people
    OWNER to postgres;

COMMENT ON TABLE public.tb_people
    IS '人员信息表';

Pojo实体类编写

        实体类编写如下,

package com.xwd.pojo.business;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.Point;

import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import java.io.Serializable;

/**
 * @className Family
 * @description: com.xwd.pojo.business
 * @auther: xiwd
 * @date: 2023-01-29 - 01 - 29 - 15:36
 * @version: 1.0
 * @jdk: 1.8
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(value = "人员信息实体")
public class People implements Serializable {
    private static final long serialVersionUID = 946928072194669586L;
    //properties
    @ApiModelProperty(value = "主键ID",required = true)
    private Long id;

    @ApiModelProperty(value = "姓名",required = false)
    private String name;

    @ApiModelProperty(value = "身份证号",required = false)
    private String idCard;

    @ApiModelProperty(value = "年龄",required = false)
    @Min(0)
    @Max(120)
    private Integer age;

    @ApiModelProperty(value = "房屋编码",required = false)
    private String houseCard;


    @ApiModelProperty(value = "性别",required = false)
    private String gender;

    @ApiModelProperty(value = "职业",required = false)
    private String occupation;

    @ApiModelProperty(value = "联系方式",required = false)
    private String tel;

    @ApiModelProperty(value = "房屋id",required = false)
    private Long houseId;

    @ApiModelProperty(value = "所在房屋层数",required = false)
    private Integer floor;

    @ApiModelProperty(value = "是否为户主",required = false)
    private Integer isHolder;
    //methods

    @ApiModelProperty(value = "位置",required = false)
    private Geometry location;
}

Mapper层接口比编写&XML配置

Mapper接口实现

package com.xwd.mapper;

import com.xwd.pojo.business.People;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @className PeopleMapper
 * @description: com.xwd.mapper
 * @auther: xiwd
 * @date: 2023-01-29 - 01 - 29 - 17:41
 * @version: 1.0
 * @jdk: 1.8
 */
@Mapper
public interface PeopleMapper {
    //properties

    //methods

    int insertBatch(@Param(value = "list")List<People> peopleList);
}

XML配置与SQL编写

        XML配置文件中,基于MyBatis提供的动态SQL特性,实现批量添加数据。

<?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.xwd.mapper.PeopleMapper">


    <insert id="insertBatch"  keyProperty="id" useGeneratedKeys="true" keyColumn="id">
        INSERT INTO public.tb_people(name, id_card, age, house_card, gender, occupation, tel, house_id, floor, is_holder, geom)
            VALUES
            <foreach collection="list" item="people" separator=",">
                (#{people.name},
                #{people.idCard},
                #{people.age},
                #{people.houseCard},
                #{people.gender},
                #{people.occupation},
                #{people.tel},
                #{people.houseId},
                #{people.floor},
                #{people.isHolder},
                #{people.location,typeHandler=com.xwd.typehandler.AbstractGeometryTypeHandler})
            </foreach>
    </insert>
</mapper>

        PS:AbstractGeometryTypeHandler是自定义的typeHandler,用于为MyBatis的类型映射提供拓展,使其支持Geometry字段到JTS-Geometry类型之间的自动转换。

Service接口&子类实现

 Service接口实现

package com.xwd.service;

import com.xwd.pojo.business.People;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @className PeopleService
 * @description: com.xwd.service
 * @auther: xiwd
 * @date: 2023-01-29 - 01 - 29 - 20:19
 * @version: 1.0
 * @jdk: 1.8
 */
public interface PeopleService {
    //methods

    long insertBatch(List<People> peopleList);

}

子类定义&分段批量添加

        为避免大量数据进行批量添加时会导致JVM内存溢出,此处将原始数据先分段,然后再执行批量添加。本地测试可支持100万条数据的批量添加操作。

package com.xwd.service.impl;

import com.xwd.mapper.PeopleMapper;
import com.xwd.pojo.business.People;
import com.xwd.service.PeopleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @className PeopleServiceImpl
 * @description: com.xwd.service.impl
 * @auther: xiwd
 * @date: 2023-01-29 - 01 - 29 - 20:19
 * @version: 1.0
 * @jdk: 1.8
 */
@Service
public class PeopleServiceImpl implements PeopleService {
    //properties
    @Autowired
    private PeopleMapper mapper;


    //methods
    @Override
    public long insertBatch(List<People> peopleList) {
        int total = peopleList.size();
        //分段更新
        int piece = 1000;
        //判断要批量更新的数据量
        if (total < piece)
            return mapper.insertBatch(peopleList);
        else{
            int executeCount = total % piece + 1;
            long affectedRows  = 0L;
            for (int i = 0; i < executeCount; i++) {
                List<People> pieceList = peopleList.subList(i * piece, (i + 1) * piece);
                affectedRows += mapper.insertBatch(pieceList);
            }
            return affectedRows;
        }
    }
}

        PS:Controller层接口略。

Service接口测试

        如下为Service层批量添加数据的方法测试,基于建筑数据模拟并随机生成约100万条人员数据,然后将其添加到PostGreSQL数据库中。

import com.xwd.PuDongApplication;
import com.xwd.common.GeneratorTools;
import com.xwd.pojo.business.People;
import com.xwd.pojo.geo.Geo_Building;
import com.xwd.service.Geo_BuildingService;
import com.xwd.service.PeopleService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.Point;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @className Geo_BuildingServiceTest
 * @description: PACKAGE_NAME
 * @auther: xiwd
 * @date: 2023-01-25 - 01 - 25 - 18:16
 * @version: 1.0
 * @jdk: 1.8
 */
@SpringBootTest(classes = PuDongApplication.class)
@RunWith(SpringRunner.class)
public class Geo_BuildingServiceTest {
    //properties
    @Autowired
    private Geo_BuildingService service;
    @Autowired
    private PeopleService peopleService;

    //methods
    @Test
    public void test_service() {
        List<Geo_Building> geo_buildings = service.selectList();
        //构造人口数据
        Long count = 1L;
        Random random = new Random();
        List<List<People>> resultList = geo_buildings.stream().map(geo_building -> {
            Long building_id = geo_building.getId();
            String areaCode = geo_building.getAreaCode();
            Geometry geometry = geo_building.getGeometry();
            Point centroid = geometry.getCentroid();
            List<People> peopleList = new ArrayList<>();
            Integer floor = geo_building.getFloor();
            for (int i = 0; i < floor; i++) {
                int familyNum = 3 + random.nextInt(4);
                boolean hasHolder = false;
                for (int k = 0; k < familyNum; k++) {
                    Integer age = GeneratorTools.randomAge();
                    String gender = GeneratorTools.randomGender();
                    boolean isHolder = GeneratorTools.randdomBoolean(age, hasHolder);
                    if (isHolder) {
                        hasHolder = true;
                    }
                    peopleList.add(
                            new People(
                                    count,
                                    GeneratorTools.randomName(),
                                    GeneratorTools.randomIDCard(gender),
                                    age,
                                    areaCode + building_id + floor,
                                    gender,
                                    age<18?(age<5)?"无":"学生":(age>65?"退休劳动者":GeneratorTools.randomOccupation()),
                                    age<18?null:GeneratorTools.randomTel(),
                                    building_id,
                                    floor,
                                    isHolder?1:0,
                                    centroid));
                }
            }
            return peopleList;
        }).collect(Collectors.toList());
        List<People> resultSet = new ArrayList<>();
        for (int i = 0; i < resultList.size(); i++) {
            List<People> people = resultList.get(i);
            for (int i1 = 0; i1 < people.size(); i1++) {
                resultSet.add(people.get(i1));
            }
        }
        System.out.println(resultSet.get(0).toString());
        System.out.println();
        //分段批量添加
        int piece = 1000;
        int total = resultSet.size();
        System.out.println(total);
        long batch = peopleService.insertBatch(resultSet);
        System.out.println("影响的总行数:"+batch);
}

          如下为约91万条数据下的测试结果,

 

          数据库中对于批量添加结果也是可查询的。

 

        

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/185002.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

梦熊杯-十二月月赛-白银组题解-C.永恒

C. Problem C.永恒&#xff08;eternity.cpp&#xff09; 内存限制&#xff1a;256 MiB 时间限制&#xff1a;1000 ms 标准输入输出 题目类型&#xff1a;传统 评测方式&#xff1a;文本比较 题目描述: 「稻妻」是「永恒」的国度。 巴尔泽布认为&#xff0c;如果一个数…

感知机与门电路

前言&#xff1a;简述单层感知机特征及三种表示方式&#xff0c;并用单层感知机描述门电路&#xff0c;借由单层感知机无法处理非线性空间的问题&#xff0c;引出多层感知机。 单层感知机 感知机&#xff08;preceptron&#xff09;接收多个输入信号&#xff0c;输出一个信号…

【Kubernetes 企业项目实战】05、基于云原生分布式存储 Ceph 实现 K8s 数据持久化(上)

目录 一、分布式存储 Ceph 基本介绍 1.1 块存储&#xff08;rbd&#xff09; 1.2 文件系统 cephfs 1.3 对象存储 1.4 分布式存储的优点 二、Ceph 核心组件介绍 三、准备安装 Ceph 高可用集群的实验环境 3.1 机器配置 3.2 初始化环境 3.3 配置互信 3.4 配置 Ceph 安…

【精选博客】反爬过程中 x-ca-nonce、x-ca-signature 参数的解密过程

本篇博客在 请求头 x-ca-key、x-ca-nonce、x-ca-signature 加密分析第一篇 的基础上继续编写&#xff0c;大家学习时可以从上一篇入手。 文章目录x-ca-nonce 代码实现python 实现 uuidx-ca-signature代码实现在上一篇博客我们已经捕获了参数的JS代码&#xff0c;这篇博客重点要…

Java设计模式-策略模式Strategy

介绍 策略模式&#xff08;Strategy Pattern&#xff09;中&#xff0c;定义算法族&#xff08;策略组&#xff09;&#xff0c;分别封装起来&#xff0c;让他们之间可以互相替换&#xff0c;此模式让算法的变化独立于使用算法的客户。这算法体现了几个设计原则&#xff0c;第…

这些学习技巧学起来

技巧一&#xff1a;组合多个对象 在PPT页面插入多个图形后&#xff0c;想要移动这些元素时&#xff0c;很多小伙伴会挨个拖动进行位置调整。其实&#xff0c;我们大可以使用快捷键【CtrlG】将多个同类的元素进行组合&#xff0c;使其成为一个图形元素&#xff0c;这样就可以方…

springboot整合mybatis-plus及mybatis-plus分页插件的使用

springboot整合mybatis-plus及mybatis-plus分页插件的使用1. mybatis-plus?2. 引入依赖3. 编写配置文件4. 编写sql表5. mapper层5.1 mybatis-plus做了什么&#xff1f;及创建mapper接口5.2 baseMapper源码6. service层及controller层6.1 service层6.2 controller层6.2.1 page对…

【html】超链接样式

超链接样式超链接样式超链接样式 根据超链接的类型&#xff0c;显示不同图片的前缀 根据 <!doctype html> <html> <head> <meta charset"utf-8"> <title></title> <style type"text/css"> body {background: …

C# 托管堆遭破坏问题溯源分析

一&#xff1a;背景 1. 讲故事 年前遇到了好几例托管堆被损坏的案例&#xff0c;有些运气好一些&#xff0c;从被破坏的托管堆内存现场能观测出大概是什么问题&#xff0c;但更多的情况下是无法做出准确判断的,原因就在于生成的dump是第二现场&#xff0c;借用之前文章的一张…

Exynos4412 移植针对Samsung的Linux-6.1(四)NandFlash卡驱动

系列文章目录 Exynos4412 移植针对Samsung的Linux-6.1&#xff08;一&#xff09;下载、配置、编译Linux-6.1Exynos4412 移植针对Samsung的Linux-6.1&#xff08;二&#xff09;SD卡驱动——解决无法挂载SD卡的根文件系统Exynos4412 移植针对Samsung的Linux-6.1&#xff08;三…

C++基础——C++ 循环

C基础——C 循环C 循环循环类型循环控制语句无限循环C 循环 有的时候&#xff0c;可能需要多次执行同一块代码。一般情况下&#xff0c;语句是顺序执行的&#xff1a;函数中的第一个语句先执行&#xff0c;接着是第二个语句&#xff0c;依此类推。 编程语言提供了允许更为复杂…

计算机自动和声分析

思路&#xff1a;信号→和声 通过计算特征值&#xff08;特征向量&#xff09;区分音频的关键信息 Chroma特征向量 (32 条消息) 什么是 Chroma Features&#xff1f; - 知乎 (zhihu.com) 基本思想&#xff1a;音高听感的周期性 音高每高一个八度&#xff0c;就回到相似的听…

计算机相关专业提升学历的解决方案(博士研究生)

文章目录1、正规全日制博士1.1 申请 - 考核制1.2 硕博连读与直博2、继续教育&#xff08;非全日制&#xff09;2.1 在职博士2.2 同等学力申博3、海外博士3.1 海外博士3.2 中外合作博士博士录取政策 国内博士&#xff0c;没有具体的政策&#xff0c;招生权力下放到各个高校。 往…

Spark 行动算子

文章目录Spark 行动算子1、reduce2、collect3、count4、first5、take6、takeOrdered7、代码示例8、aggregate9、fold10、countByValue & countByKey (wordcount重点)Spark 行动算子 所谓的行动算子&#xff0c;其实就是触发作业执行的方法&#xff0c;之前的转换算子是不能…

Lua 模块与包

Lua 模块与包 参考至菜鸟教程。 模块类似于一个封装库&#xff0c;从 Lua 5.1 开始&#xff0c;Lua 加入了标准的模块管理机制&#xff0c;可以把一些公用的代码放在一个文件里&#xff0c;以 API 接口的形式在其他地方调用&#xff0c;有利于代码的重用和降低代码耦合度。 Lua…

一起自学SLAM算法:11.3 路径规划

连载文章&#xff0c;长期更新&#xff0c;欢迎关注&#xff1a; 路径规划其实就是在回答图11-1中机器人提出的第3个问题“我该如何去”&#xff0c;不管是在已知地图上导航或是在未知环境下通过一边探索地图一边导航&#xff0c;路径规划其实就是在地图上寻找到一条从起点到目…

CMMI3-5级如何高效落地?——CMMI落地4大工具

为了助力CMMI3-5级高效落地&#xff0c;近日CoCode旗下Co-ProjectV3.0智能项目管理平台全面升级&#xff0c;CMMI落地4大工具正式上线&#xff1a;CMMI成熟度自测工具、量化管理工具&#xff08;组织级过程改进工具和量化项目管理工具&#xff09;、组织级过程资产库。 01、CMM…

年后创业,该如何选择适合年轻人的小成本创业项目?

2023年创业大潮即将来袭&#xff0c;疫情政策的放开&#xff0c;会让越来越多的年轻人选择创业。单纯的工作已经不能满足年轻人的生活需求&#xff0c;那无经验、无人脉的年轻人该如何选择适合自己的创业项目&#xff1f;小编在这里总结了几点&#xff0c;适合年轻人的小成本项…

Android Kotlin 多线程编程 server

参考: 《第一行代码 第三版》 10.1 service 是什么 Service是实现程序后台运行的解决方案&#xff0c;适合执行非交互&#xff0c;后台预先的任务&#xff0c;即使用户打开其他应用&#xff0c;Service也能够正常运行 Service需要内部手动创建子线程 10.2 多线程编程 用法&a…

Makefile学习⑨:Makefile中的等号和shell命令的使用

Makefile学习⑨&#xff1a;Makefile中的等号和shell命令的使用 Makefile中的等号 “” 普通赋值符号&#xff0c;命令格式如下 变量值注意&#xff1a;变量的最终值为该文件中的最后进行赋值操作所赋的值。 &#xff08;不管在当前文件的何处进行赋值&#xff0c;在使用该…