JPA批量插入(saveAll)

有時(shí)候要從第三方導(dǎo)入數(shù)據(jù)隅居,一般量都比較大祖灰,除了方法用異步線程@Async之外尿招,如果每條記錄都調(diào)用一次save顯然對(duì)數(shù)據(jù)庫(kù)壓力很大诀豁±椅遥可以使用JPA的批量保存方法saveAll(Iterable<S> entities)
由于JPA的批量保存和批量修改是同一個(gè)方法且叁,所以本文也適用批量修改操作都哭。

一、Entity改造

增加3個(gè)注解逞带,方便在Controller類build方式構(gòu)造對(duì)象欺矫。

@Builder
@NoArgsConstructor
@AllArgsConstructor

完整注解:

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ApiModel("休息日,休息日可能不處理業(yè)務(wù)展氓,備用")
@Entity
@Table(name = "bz_setup_restday", schema = "bankrouter")
public class BzSetupRestdayEntity implements Serializable {

......    

二穆趴、Repository

package com.pay.payee.repository;

import com.pay.payee.entity.BzSetupRestdayEntity;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Date;

/**
 * 
休息日,休息日可能不處理業(yè)務(wù)遇汞,備用(BzSetupRestday)表數(shù)據(jù)庫(kù)訪問(wèn)層
 *
 * @author 郭秀志 jbcode@126.com
 * @since 2020-05-08 23:50:43
 */
public interface BzSetupRestdayRepository extends JpaRepository<BzSetupRestdayEntity, Date> {

}

三未妹、Service實(shí)現(xiàn)類

關(guān)鍵方法saveAll(Iterable<S> entities)

package com.pay.payee.service.impl;

import com.pay.payee.entity.BzSetupRestdayEntity;
import com.pay.payee.repository.BzSetupRestdayRepository;
import com.pay.payee.service.IBzSetupRestdayService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Optional;

/**
 * 
休息日空入,休息日可能不處理業(yè)務(wù)络它,備用(BzSetupRestday)表服務(wù)實(shí)現(xiàn)類
 *
 * @author 郭秀志 jbcode@126.com
 * @since 2020-05-08 23:50:43
 */
@Service("bzSetupRestdayService")
public class BzSetupRestdayServiceImpl implements IBzSetupRestdayService {
    @Autowired
    private BzSetupRestdayRepository bzSetupRestdayRepository;
    
    @Override
    public void save(BzSetupRestdayEntity bzSetupRestdayEntity) {
        bzSetupRestdayRepository.save(bzSetupRestdayEntity);
    }

    public <S extends BzSetupRestdayEntity> List<S> saveAll(Iterable<S> entities) {
        return bzSetupRestdayRepository.saveAll(entities);
    }
}

四、Controller

60000條數(shù)據(jù)使用方法saveAll(Iterable<S> entities)進(jìn)行批量保存歪赢。

package com.pay.payee.controller;

import com.pay.payee.entity.BzSetupRestdayEntity;
import com.pay.payee.service.IBzSetupRestdayService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 休息日化戳,休息日可能不處理業(yè)務(wù),備用(BzSetupRestday)表控制層
 *
 * @author 郭秀志 jbcode@126.com
 * @since 2020-05-08 23:50:43
 */
@RestController
@RequestMapping("/bzSetupRestday")
public class BzSetupRestdayController {
    /**
     * 服務(wù)對(duì)象
     */
    @Resource
    private IBzSetupRestdayService bzSetupRestdayService;

    /*
     * @Description 批量保存
     * @Param [entities]
     * @return java.util.List<S>
     */
    @GetMapping("/saveAll")
    public <S extends BzSetupRestdayEntity> List<S> saveAll() {
        long begin = System.currentTimeMillis();
        List<BzSetupRestdayEntity> list = new ArrayList<BzSetupRestdayEntity>();
        for (int i = 0; i < 60000; i++) {
            BzSetupRestdayEntity build = BzSetupRestdayEntity.builder().groupId("1").restDate(new Date()).useType("2").build();
            list.add(build);
        }
        List<S> sList = (List<S>) bzSetupRestdayService.saveAll(list);
        long end = System.currentTimeMillis();
        System.out.println("時(shí)長(zhǎng):" + (end - begin));
        return sList;
    }
}

五埋凯、調(diào)用url測(cè)試

http://localhost:8555/bzSetupRestday/saveAll
控制臺(tái)輸出耗時(shí)(毫秒):時(shí)長(zhǎng):15958

網(wǎng)上個(gè)別人遇到saveAll速度慢点楼,添加了如下配置信息解決。我實(shí)測(cè)速度無(wú)差別白对。

spring:
  jpa:
    properties:
      hibernate:
        #打印執(zhí)行時(shí)間統(tǒng)計(jì)信息
        generate_statistics: true
        jdbc:
          #每批500條提交
          batch_size: 500
          batch_versioned_data: true
        order_inserts: true
        order_updates: true

六掠廓、批量保存優(yōu)化

6.1 背景

有次實(shí)踐是有20萬(wàn)左右的數(shù)據(jù)要批量的插入,速度非常慢甩恼,發(fā)現(xiàn)打印出來(lái)的sql是先select一次蟀瞧,再insert狰域。

6.2 速度慢原因

原生的saveAll()方法可以保證程序的正確性,但是如果數(shù)據(jù)量比較大效率低黄橘,看下源碼就知道其原理是 for 循環(huán)每一條數(shù)據(jù),然后先select一次屈溉,如果數(shù)據(jù)庫(kù)存在塞关,則update。如果不存在子巾,則insert帆赢。

6.3. saveAll源碼

SimpleJpaRepositorysaveAll(Iterable<S> entities)方法源碼如下:

    @Transactional
    public <S extends T> List<S> saveAll(Iterable<S> entities) {
        Assert.notNull(entities, "Entities must not be null!");
        List<S> result = new ArrayList();
        Iterator var3 = entities.iterator();

        while(var3.hasNext()) {
            S entity = var3.next();
            result.add(this.save(entity));//save方法是核心邏輯
        }

        return result;
    }
    @Transactional
    public <S extends T> S save(S entity) {
        if (this.entityInformation.isNew(entity)) {
            this.em.persist(entity);
            return entity;
        } else {
            return this.em.merge(entity);
        }
    }

6.4 解決方案

6.4.1 批量插入

解決方案是自己用em進(jìn)行持久化插入,省了一步查詢操作线梗。

    @PersistenceContext
    private EntityManager entityManager;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addBatch(List<ProjectApplyDO> list) {
        for (ProjectApplyDO projectApplyDO : list) {
            entityManager.persist(projectApplyDO);//insert插入操作
        }
        entityManager.flush();
        entityManager.clear();
    }
6.4.2 批量更新

在確保數(shù)據(jù)已經(jīng)存在的情況下椰于,如果是批量更新可以如下代碼代替上面的entityManager.persist(projectApplyDO);語(yǔ)句:

entityManager.merge(projectApplyDO);//update更新操作
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市仪搔,隨后出現(xiàn)的幾起案子瘾婿,更是在濱河造成了極大的恐慌,老刑警劉巖烤咧,帶你破解...
    沈念sama閱讀 218,525評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件偏陪,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡煮嫌,警方通過(guò)查閱死者的電腦和手機(jī)笛谦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)昌阿,“玉大人饥脑,你說(shuō)我怎么就攤上這事∨潮” “怎么了灶轰?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,862評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)刷钢。 經(jīng)常有香客問(wèn)我框往,道長(zhǎng),這世上最難降的妖魔是什么闯捎? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,728評(píng)論 1 294
  • 正文 為了忘掉前任椰弊,我火速辦了婚禮,結(jié)果婚禮上瓤鼻,老公的妹妹穿的比我還像新娘秉版。我一直安慰自己,他們只是感情好茬祷,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布清焕。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪秸妥。 梳的紋絲不亂的頭發(fā)上滚停,一...
    開(kāi)封第一講書(shū)人閱讀 51,590評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音粥惧,去河邊找鬼键畴。 笑死,一個(gè)胖子當(dāng)著我的面吹牛突雪,可吹牛的內(nèi)容都是我干的起惕。 我是一名探鬼主播,決...
    沈念sama閱讀 40,330評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼咏删,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼惹想!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起督函,我...
    開(kāi)封第一講書(shū)人閱讀 39,244評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤嘀粱,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后辰狡,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體草穆,經(jīng)...
    沈念sama閱讀 45,693評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評(píng)論 3 336
  • 正文 我和宋清朗相戀三年搓译,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了悲柱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,001評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡些己,死狀恐怖豌鸡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情段标,我是刑警寧澤涯冠,帶...
    沈念sama閱讀 35,723評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站逼庞,受9級(jí)特大地震影響蛇更,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜赛糟,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評(píng)論 3 330
  • 文/蒙蒙 一派任、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧璧南,春花似錦掌逛、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,919評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)篓像。三九已至,卻和暖如春皿伺,著一層夾襖步出監(jiān)牢的瞬間员辩,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,042評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工鸵鸥, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留奠滑,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,191評(píng)論 3 370
  • 正文 我出身青樓脂男,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親种呐。 傳聞我的和親對(duì)象是個(gè)殘疾皇子宰翅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評(píng)論 2 355