MyBatis实现MySql的批量插入,以及flushCache、useCache的配置

  • 上次遇到一个插入效率的问题,同一个事务下,插入1000条数据,主键为递增序列,下面演示:

1、新增两张表

  • 记录序列表
CREATE TABLE `sequence`  (
 `name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
 `current_value` int(11) NOT NULL,
 `increment_value` int(11) NOT NULL DEFAULT 1,
 PRIMARY KEY (`name`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
  • 测试插入记录表
CREATE TABLE `test_batch`  (
  `ID` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `NAME` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `GMT_CREATE` timestamp(0) NULL DEFAULT NULL,
  PRIMARY KEY (`ID`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
  • 自定义MySql的主键增长,通过函数来写:
CREATE DEFINER=`goms`@`%` FUNCTION `currval`(seq_name VARCHAR(50)) RETURNS int(11)
BEGIN
     DECLARE VALUE INTEGER;
      SET VALUE = 0;
      SELECT current_value INTO VALUE
      FROM sequence
      WHERE NAME = seq_name;
      RETURN VALUE;
    END
CREATE DEFINER=`goms`@`%` FUNCTION `nextval`(seq_name VARCHAR(50)) RETURNS int(11)
BEGIN
       UPDATE  sequence
       SET  current_value = CASE current_value WHEN 999999999 THEN 100000000 ELSE current_value + increment_value END
       WHERE NAME = seq_name;
       RETURN currval(seq_name);
END

2、搭建测试项目

  • 在此不多说,这是我平时写demo的一个项目
image.png

image.png

image.png

3、基础代码

public interface GomsSequenceMapper {
    long getSeq4ID();
}
<?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="org.tools.dao.test.GomsSequenceMapper">

<!-- flushCache="true" useCache="false" -->
    <select id="getSeq4ID" resultType="java.lang.Long" >
        SELECT nextval('SEQ_ID');
    </select>
</mapper>
public interface TestBatchMapper {
  int insertSelective(TestBatch record);
  void insertBatch(List<TestBatch> list);
}
public class TestBatch {
    private String id;

    private String name;

    private Date gmtCreate;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id == null ? null : id.trim();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name == null ? null : name.trim();
    }

    public Date getGmtCreate() {
        return gmtCreate;
    }

    public void setGmtCreate(Date gmtCreate) {
        this.gmtCreate = gmtCreate;
    }
}
<?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="org.tools.dao.test.TestBatchMapper" >
  <resultMap id="BaseResultMap" type="org.tools.domain.order.dto.TestBatch" >
    <id column="ID" property="id" jdbcType="VARCHAR" />
    <result column="NAME" property="name" jdbcType="VARCHAR" />
    <result column="GMT_CREATE" property="gmtCreate" jdbcType="TIMESTAMP" />
  </resultMap>

 <insert id="insertSelective" parameterType="org.tools.domain.order.dto.TestBatch" >
    insert into test_batch
    <trim prefix="(" suffix=")" suffixOverrides="," >
      <if test="id != null" >
        ID,
      </if>
      <if test="name != null" >
        NAME,
      </if>
      <if test="gmtCreate != null" >
        GMT_CREATE,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides="," >
      <if test="id != null" >
        #{id,jdbcType=VARCHAR},
      </if>
      <if test="name != null" >
        #{name,jdbcType=VARCHAR},
      </if>
      <if test="gmtCreate != null" >
        #{gmtCreate,jdbcType=TIMESTAMP},
      </if>
    </trim>
  </insert>

<!-- 批量插入数据 -->
    <insert id="insertBatch" parameterType="java.util.List" >
        insert into test_batch (ID, NAME, GMT_CREATE)
        values 
        <foreach collection="list" item="item" index="" separator=",">   
            (#{item.id,jdbcType=VARCHAR}, #{item.name,jdbcType=VARCHAR}, NOW())
        </foreach>
    </insert>
 
</mapper>

4、进入主题

@Service
public class TestBatchInsert {

    @Autowired
    private GomsSequenceMapper gomsSequenceDAO;
    
    @Autowired
    private TestBatchMapper testBatchMapper;

    final class Interval{
        public long start;
        public long end;
        public long interval;
    }
    

    @Transactional
    public void test1() {
        try {
            Interval timeout = new Interval();
            timeout.start = System.currentTimeMillis();
            System.err.println(new Date(timeout.start));
            for (int i = 0 ; i < 100; i++) {
                long id = gomsSequenceDAO.getSeq4ID();
                
                TestBatch test = new TestBatch();
                test.setId(id+"");
                test.setName(id+"----");
                
                testBatchMapper.insertSelective(test);
            }
            timeout.end = System.currentTimeMillis();
            timeout.interval = timeout.end-timeout.start;
            System.err.println(String.valueOf(timeout.interval));
            System.err.println(new Date(timeout.end));
        } catch (Exception e) {
            e.printStackTrace();
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }

@Transactional
    public void test2() {
        try {
            Interval timeout = new Interval();
            timeout.start = System.currentTimeMillis();
            System.err.println(new Date(timeout.start));
            List<TestBatch> lists = new ArrayList<TestBatch>();
            
            for (int i = 0 ; i < 100; i++) {
                long id = gomsSequenceDAO.getSeq4ID();
                
                TestBatch test = new TestBatch();
                test.setId(id+"");
                test.setName(id+"----");
                
                lists.add(test);
            }
            testBatchMapper.insertBatch(lists);
            timeout.end = System.currentTimeMillis();
            timeout.interval = timeout.end-timeout.start;
            System.err.println(String.valueOf(timeout.interval));
            System.err.println(new Date(timeout.end));
        } catch (Exception e) {
            e.printStackTrace();
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }
}

- 执行junittest:

image.png

执行test1,大家注意这个是注释的:

image.png

image.png

Tue Oct 15 15:36:31 CST 2019
168
Tue Oct 15 15:36:31 CST 2019
- 执行test2:

image.png

com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException就是违反数据库完整性约束

  • 在MyBatis中有flushCache、useCache这两个配置属性,分为下面几种情况:
    (1)当为select语句时:
    flushCache默认为false,表示任何时候语句被调用,都不会去清空本地缓存和二级缓存。
    useCache默认为true,表示会将本条语句的结果进行二级缓存。
    (2)当为insert、update、delete语句时:
    flushCache默认为true,表示任何时候语句被调用,都会导致本地缓存和二级缓存被清空。
    useCache属性在该情况下没有。
  • 我们来改下xml,不缓存,flushCache="true", useCache="false"


    image.png
image.png

Tue Oct 15 15:35:29 CST 2019
126
Tue Oct 15 15:35:29 CST 2019

  • 结果很明显,一条一条插入使用了168ms,而批量插入用了126ms。
  • 关注公众号"双城人",搬砖过程遇到的问题,大家一起探讨,资源共享


    image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335