insert into user (username,address,password) values (#{username},#{address},#{password})
service 如下:
@Service
public class UserService extends ServiceImpl<UserMapper, User> implements IUserService {
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
@Autowired
UserMapper userMapper;
@Autowired
SqlSessionFactory sqlSessionFactory;
@Transactional(rollbackFor = Exception.class)
public void addUserOneByOne(List users) {
SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
UserMapper um = session.getMapper(UserMapper.class);
long startTime = System.currentTimeMillis();
for (User user : users) {
um.addUserOneByOne(user);
}
session.commit();
long endTime = System.currentTimeMillis();
logger.info(“一条条插入 SQL 耗费时间 {}”, (endTime - startTime));
}
}
这里我要说一下:
虽然是一条一条的插入,但是我们要开启批处理模式(BATCH),这样前前后后就只用这一个 SqlSession,如果不采用批处理模式,反反复复的获取 Connection 以及释放 Connection 会耗费大量时间,效率奇低,这种效率奇低的方式松哥就不给大家测试了。
接下来写一个简单的测试接口看下:
@RestController
public class HelloController {
private static final Logger logger = getLogger(HelloController.class);
@Autowired
UserService userService;
/**
- 一条一条插入
*/
@GetMapping(“/user2”)
public void user2() {
List users = new ArrayList<>();
for (int i = 0; i < 50000; i++) {
User u = new User();
u.setAddress(“广州:” + i);
u.setUsername(“张三:” + i);
u.setPassword(“123:” + i);
users.add(u);
}
userService.addUserOneByOne(users);
}
}
写个简单的单元测试:
/**
-
单元测试加事务的目的是为了插入之后自动回滚,避免影响下一次测试结果
-
一条一条插入
*/
@Test
@Transactional
void addUserOneByOne() {
List users = new ArrayList<>();
for (int i = 0; i < 50000; i++) {
User u = new User();
u.setAddress(“广州:” + i);
u.setUsername(“张三:” + i);
u.setPassword(“123:” + i);
users.add(u);
}
userService.addUserOneByOne(users);
}
可以看到,耗时 901 毫秒,5w 条数据插入不到 1 秒。
2.2 方案二测试
=========
方案二是生成一条 SQL 然后插入。
mapper 如下:
@Mapper
public interface UserMapper {
void addByOneSQL(@Param(“users”) List users);
}
对应的 SQL 如下:
insert into user (username,address,password) values
(#{user.username},#{user.address},#{user.password})
service 如下:
@Service
public class UserService extends ServiceImpl<UserMapper, User> implements IUserService {
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
@Autowired
UserMapper userMapper;
@Autowired
SqlSessionFactory sqlSessionFactory;
@Transactional(rollbackFor = Exception.class)
public void addByOneSQL(List users) {
long startTime = System.currentTimeMillis();
userMapper.addByOneSQL(users);
long endTime = System.currentTimeMillis();
logger.info(“合并成一条 SQL 插入耗费时间 {}”, (endTime - startTime));
}
}
然后在单元测试中调一下这个方法:
/**
- 合并成一条 SQL 插入
*/
@Test
@Transactional
void addByOneSQL() {
List users = new ArrayList<>();
for (int i = 0; i < 50000; i++) {
User u = new User();
u.setAddress(“广州:” + i);
u.setUsername(“张三:” + i);
u.setPassword(“123:” + i);
users.add(u);
}
userService.addByOneSQL(users);
}
可以看到插入 5 万条数据耗时 1805 毫秒。
可以看到,生成一条 SQL 的执行效率还是要差一点。
另外还需要注意,第二种方案还有一个问题,就是当数据量大的时候,生成的 SQL 将特别的长,MySQL 可能一次性处理不了这么大的 SQL,这个时候就需要修改 MySQL 的配置或者对待插入的数据进行分片处理了,这些操作又会导致插入时间更长。
2.3 对比分析
========
很明显,方案一更具优势。当批量插入十万、二十万数据的时候,方案一的优势会更加明显(方案二则需要修改 MySQL 配置或者对待插入数据进行分片)。
3. MP 怎么做的?
============
小伙伴们知道,其实 MyBatis Plus 里边也有一个批量插入的方法 saveBatch,我们来看看它的实现源码:
@Transactional(rollbackFor = Exception.class)
@Override
public boolean saveBatch(Collection entityList, int batchSize) {
String sqlStatement = getSqlStatement(SqlMethod.INSERT_ONE);
return executeBatch(entityList, batchSize, (sqlSession, entity) -> sqlSession.insert(sqlStatement, entity));
}
可以看到,这里拿到的 sqlStatement 就是一个 INSERT_ONE ,即一条一条插入。
再来看 executeBatch 方法,如下:
public static boolean executeBatch(Class<?> entityClass, Log log, Collection list, int batchSize, BiConsumer<SqlSession, E> consumer) {
Assert.isFalse(batchSize < 1, “batchSize must not be less than one”);
return !CollectionUtils.isEmpty(list) && executeBatch(entityClass, log, sqlSession -> {
int size = list.size();
int i = 1;
for (E element : list) {
consumer.accept(sqlSession, element);
if ((i % batchSize == 0) || i == size) {
sqlSession.flushStatements();
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Java)
线程、数据库、算法、JVM、分布式、微服务、框架、Spring相关知识
一线互联网P7面试集锦+各种大厂面试集锦
学习笔记以及面试真题解析
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
更新!**
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Java)
[外链图片转存中…(img-tOnQOS4j-1713786460655)]
线程、数据库、算法、JVM、分布式、微服务、框架、Spring相关知识
[外链图片转存中…(img-E9wNQ3YR-1713786460655)]
一线互联网P7面试集锦+各种大厂面试集锦
[外链图片转存中…(img-2FS5Scxi-1713786460655)]
学习笔记以及面试真题解析
[外链图片转存中…(img-K2vO1d8g-1713786460656)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!