本文主要是介绍【Springcloud微服务】MybatisPlus上篇,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
🔥 本文由 程序喵正在路上 原创,CSDN首发!
💖 系列专栏:Springcloud微服务
🌠 首发时间:2024年6月4日
🦋 欢迎关注🖱点赞👍收藏🌟留言🐾
目录
- 资料下载
- 微服务
- MybatisPlus介绍
- 快速入门
- 入门案例
- 常见注解
- 常见配置
- 总结
- 核心功能
- 条件构造器
- 基于QueryWrapper的查询
- 基于UpdateWrapper的查询
- LambdaQueryWrapper
- 自定义SQL
- 基本用法
- 多表查询
- Service接口
- CRUD
- 基本用法
- 基于Restful风格实现接口
- Lambda
- 批量新增
资料下载
点击此处进入下载资料
微服务
微服务是一种软件架构风格,它是以专注于单一职责的很多小型项目为基础,组合出复杂的大型应用。
MybatisPlus介绍
官网:https://baomidou.com/
快速入门
入门案例
目标:
- 学会 MP 的基本用法
- 体会 MP 的无侵入和方便快捷的特点
需求:基于资料提供的项目,实现下列功能:
- 新增用户功能
- 根据 id 查询用户
- 根据 id 批量查询用户
- 根据 id 更新用户
- 根据 id 删除用户
具体步骤:
-
引入 MybatisPlus 的起步依赖
MyBatisPlus 官方提供了 starter,其中集成了 Mybatis 和 MybatisPlus 的所有功能,并且实现了自动装配效果。因此我们可以用 MybatisPlus 的 starter 代替 Mybatis 的 starter:
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.1</version> </dependency>
可将 UserMapper 里的方法和 UserMapper.xml 中的 SQL 语句删除,因为我们不再需要了
-
自定义 Mapper 继承 BaseMapper 接口
自定义的 Mapper 需要继承 MybatisPlus 提供的 BaseMapper 接口:
public interface UserMapper extends BaseMapper<User> {}
-
将测试类中调用的方法改为 MybatisPlus 自带的方法:
-
自行测试一下即可
常见注解
MyBatisPlus 通过扫描实体类,并基于反射获取实体类信息作为数据库表信息。
public class User {private Long id; //用户idprivate String username; //用户名private String password; //密码private String phone; //注册手机号private String info; //详细信息private Integer status; //使用状态(1正常 2冻结)private Integer balance; //账户余额private LocalDateTime createTime;//创建时间private LocalDateTime updateTime;//更新时间
}
MybatisPlus 是如何获取实现 CRUD 的数据库表信息的?
-
默认以类名驼峰转下划线作为表名
-
默认将名为 id 的字段作为主键
-
默认将变量名驼峰转下划线作为表的字段名
只要我们定义的类符合规则,MP 就能自动获取到对应的数据库表信息,从而帮我们生成 SQL 语句。如果不一样,我们就需要通过注解来指定。
MybatisPlus 中比较常用的几个注解如下:
@TableName
:用来指定表名@TableId
:用来指定表中的主键字段信息@TableField
:用来指定表中的普通字段信息
IdType枚举:
AUTO
:数据库自增长INPUT
:通过set方法自行输入ASSIGN_ID
:分配 ID,接口IdentifierGenerator
的方法nextId
来生成id
,默认实现类为DefaultIdentifierGenerator
雪花算法
使用 @TableField
的常见场景:
- 成员变量名与数据库字段名不一致
- 成员变量名以
is
开头,且是布尔值 - 成员变量名与数据库关键字冲突
- 成员变量不是数据库字段
例子:
有一个用户表:
我们定义的实体类:
public class User {private Long id;private String name;private Boolean isMarried;private Integer order;private String address;
}
加上注解后:
@TableName("tb_user")
public class User {@TableId(value = "id", type = IdType.AUTO) //自增private Long id;@TableField("username") //不一致private String name;@TableField("is_married") //遇到布尔型且以 is开头的变量,MP会自动去掉 is,即为 marriedprivate Boolean isMarried;@TableField("'order'") //和 sql关键字一样private Integer order;@TableField(exist = false) //表中没有该字段private String address;
}
将我们项目中定义的实体类修改一下:
public class User {@TableId(type = IdType.AUTO) //不指定的话,默认为随机生成id,也就是第三种方式private Long id; //用户idprivate String username; //用户名private String password; //密码private String phone; //注册手机号private String info; //详细信息private Integer status; //使用状态(1正常 2冻结)private Integer balance; //账户余额private LocalDateTime createTime;//创建时间private LocalDateTime updateTime;//更新时间
}
常见配置
MyBatisPlus 的配置项继承了 MyBatis 原生配置和一些自己特有的配置。例如:
mybatis-plus:type-aliases-package: com.itheima.mp.domain.po # 别名扫描包mapper-locations: "classpath*:/mapper/**/*.xml" # Mapper.xml文件地址,默认值configuration:map-upderscore-to-camel-case: true # 是否开启下换线和驼峰的映射cache-enabled: false # 是否开启二级缓存global-config:db-config:id-type: assign_id # id为雪花算法生成update-strategy: not_null # 更新策略:只更新非空字段
别看上面代码这么多,其实除了第一个,其他都是默认的,可以不用配置,除非你要自己设置为别的值。至于其他的配置,需要用到的时候,可以百度搜索或者参考官方文档:使用配置 | MyBatis-Plus。
总结
MyBatisPlus 使用的基本流程是什么?
- 引入起步依赖
- 自定义
Mapper
基础BaseMapper
- 在实体类上添加注解声明表信息
- 在
application.yml
中根据需要添加配置
核心功能
条件构造器
MyBatisPlus 支持各种复杂的 where
条件,可以满足日常开发的所有需求。在 MyBatisPlus 中,Wrapper
类是构建查询和更新条件的核心工具。
Wrapper
类及其子类:
BaseMapper
中很多方法都是支持用 Wrapper
对象作为参数的:
AbstractWrapper
类:
QueryWrapper
类:
UpdateWrapper
类:
基于QueryWrapper的查询
需求:
-
查询出名字中带 o 的,存款大于等于 1000 元的人的 id、username、info、balance字段
sql 语句这样写:
select id,username,info,balance from user where username like ? and balance >= ?
QueryWrapper 这样写:
@Test void testQueryWrapper() {//构建查询条件QueryWrapper<User> wrapper = new QueryWrapper<User>().select("id", "username", "info", "balance").like("username", "o").ge("balance", 1000);//查询userMapper.selectList(wrapper); }
-
更新用户名为 jack 的用户的余额为 2000
sql 语句这样写:
update user set balance = 2000 where (username = "jack")
QueryWrapper 这样写:
@Test void testUpdateByQueryWrapper() {//准备要更新的数据User user = new User();user.setBalance(2000);//更新的条件QueryWrapper<User> wrapper = new QueryWrapper<User>().eq("username", "jack");//执行更新userMapper.update(user, wrapper); }
基于UpdateWrapper的查询
-
需求:更新 id 为 1,2,4 的用户的余额,扣 200
sql 语句这样写:
update user set balance = balance - 200 where id in (1, 2, 4)
UpdateWrapper 这样写:
@Test void testUpdateWrapper() {//准备数据和更新条件List<Long> ids = List.of(1L, 2L, 4L);UpdateWrapper<User> wrapper = new UpdateWrapper<User>().setSql("balance = balance - 200").in("id", ids);//执行更新userMapper.update(null, wrapper); }
LambdaQueryWrapper
一个基于 Lambda 表达式的查询条件构造器,它通过 Lambda
表达式来引用实体类的属性,从而避免了硬编码字段名。这种方式提高了代码的可读性和可维护性,尤其是在字段名可能发生变化的情况下。
我们用 LambdaQueryWrapper 改进 QueryWrapper 的第一个需求的代码,如下:
@Test
void testLambdaQueryWrapper() {//构建查询条件LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>().select(User::getId, User::getUsername, User::getInfo, User::getBalance).like(User::getUsername, "o").ge(User::getBalance, 1000);//查询userMapper.selectList(wrapper);
}
LambdaUpdateWrapper 的用法类似。
总结
条件构造器的用法:
QueryWrapper
和LambdaQueryWrapper
通常用来构建select
、delete
、update
的where
条件部分UpdateWrapper
和LambdaUpdateWrapper
通常只有在set
语句比较特殊才使用- 尽量使用
LambdaQueryWrapper
和LambdaUpdateWrapper
,避免硬编码
自定义SQL
在演示 UpdateWrapper 的案例中,我们在代码中编写了更新的 SQL 语句。
@Test
void testUpdateWrapper() {//准备数据和更新条件List<Long> ids = List.of(1L, 2L, 4L);UpdateWrapper<User> wrapper = new UpdateWrapper<User>().setSql("balance = balance - 200").in("id", ids);//执行更新userMapper.update(null, wrapper);
}
这种写法在某些企业也是不允许的,因为 SQL 语句最好都维护在持久层,而不是业务层。就当前案例来说,由于条件是 in 语句,只能将 SQL 写在 Mapper.xml
文件,利用 foreach
来生成动态 SQL,但是这实在是太麻烦了。假如查询条件更复杂,动态 SQL 的编写也会更加复杂。
所以,MybatisPlus 提供了自定义 SQL 功能,可以让我们利用 Wrapper
生成查询条件部分,再结合我们自己在 Mapper.xml
编写 SQL
。
基本用法
改写上面那个例子:
-
基于
Wrapper
构建where
条件@Test void testCustomWrapper() {//准备查询条件List<Long> ids = List.of(1L, 2L, 4L);LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>().in(User::getId, ids);int amount = 200;//调用mapper的自定义方法,直接传递mapperuserMapper.deductBalanceByIds(wrapper, amount); }
-
在
mapper
方法参数中用Param
注解声明wrapper
变量名称,必须是ew
-
自定义
SQL
,并使用Wrapper
条件,注解或者xml
文件皆可@Mapper public interface UserMapper extends BaseMapper<User> {@Update("update user set balance = balance - #{amount} ${ew.customSqlSegment}")void deductBalanceByIds(@Param("ew") LambdaQueryWrapper<User> wrapper, @Param("amount") int amount); }
多表查询
理论上来讲 MyBatisPlus 是不支持多表查询的,不过我们可以利用 Wrapper
中自定义条件结合自定义 SQL 来实现多表查询的效果。
例如,我们要查询出所有收货地址在北京的并且用户 id 在 1、2、4 之中的用户。
@Test
void testCustomJoinWrapper() {//准备查询条件QueryWrapper<User> wrapper = new QueryWrapper<User>().in("u.id", List.of(1L, 2L, 4L)).eq("a.city", "北京");//调用mapper的自定义方法List<User> users = userMapper.queryUserByWrapper(wrapper);users.forEach(System.out::println);
}
@Select("select u.* from user u inner join address a on u.id = a.user_id ${ew.customSqlSegment}")
List<User> queryUserByWrapper(@Param("ew")QueryWrapper<User> wrapper);
Service接口
MybatisPlus 不仅提供了 BaseMapper,还提供了通用的 Service 接口及默认实现,封装了一些常用的 service 模板方法。
通用接口为 IService,默认实现为 ServiceImpl,其中封装的方法可以分为以下几类:
- save:新增
- remove:删除
- update:更新
- get:查询单个结果
- list:查询集合结果
- count:计数
- page:分页查询
CRUD
新增:
save
是新增单个元素saveBatch
是批量新增saveOrUpdate
是根据 id 判断,如果数据存在就更新,不存在则新增saveOrUpdateBatch
是批量的新增或修改
删除:
removeById
:根据 id 删除removeByIds
:根据 id 批量删除removeByMap
:根据 Map 中的键值对为条件删除remove(Wrapper<T>)
:根据 Wrapper 条件删除
修改:
updateById
:根据id修改update(Wrapper<T>)
:根据UpdateWrapper修改,Wrapper中包含set和where部分update(T,Wrapper<T>)
:按照T内的数据修改与Wrapper匹配到的数据updateBatchById
:根据id批量修改
查询:
getById
:根据id查询1条数据getOne(Wrapper<T>)
:根据Wrapper查询1条数据getBaseMapper
:获取Service内的BaseMapper实现,某些时候需要直接调用Mapper内的自定义SQL时可以用这个方法获取到Mapper
批量查询:
listByIds
:根据id批量查询list(Wrapper<T>)
:根据Wrapper条件查询多条数据list()
:查询所有
计数:
count()
:统计所有数量count(Wrapper<T>)
:统计符合Wrapper条件的数据数量
getBaseMapper
:当我们在 service 中要调用 Mapper 中的自定义 SQL 时,就必须获取 service 对应的 Mapper,就可以通过这个方法。
基本用法
由于 Service 中经常需要定义与业务有关的自定义方法,因此我们不能直接使用 IService,而是自定义 Service 接口,然后继承 IService 以拓展方法。同时,让自定义的 Service 实现类继承 ServiceImpl,这样就不用自己实现 IService 中的接口了。
首先,定义 IUserService,继承 IService:
import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.mp.domain.po.User;public interface IUserService extends IService<User> {
}
然后,编写 UserServiceImpl 类,继承 ServiceImpl,实现 UserService:
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.mapper.UserMapper;
import com.itheima.mp.service.IUserService;
import org.springframework.stereotype.Service;@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
}
项目结构如下:
双击选中 IUserService 右键选择 Generate → \rightarrow → Test → \rightarrow → Ok 生成测试类:
调用 IService 的一些方法,测试是否成功:
import com.itheima.mp.domain.po.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.time.LocalDateTime;
import java.util.List;@SpringBootTest
class IUserServiceTest {@Autowiredprivate IUserService userService;@Testvoid testSaveUser() {User user = new User();user.setId(5L);user.setUsername("Lucy");user.setPassword("123");user.setPhone("18688990011");user.setBalance(200);user.setInfo("{\"age\": 24, \"intro\": \"英文老师\", \"gender\": \"female\"}");user.setCreateTime(LocalDateTime.now());user.setUpdateTime(LocalDateTime.now());userService.save(user);}@Testvoid testQuery() {List<User> users = userService.listByIds(List.of(1L, 2L, 4L));users.forEach(System.out::println);}
}
基于Restful风格实现接口
需求:基于 Restful 风格实现下面的接口:
首先,我们在项目中引入几个依赖:
<!--swagger-->
<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi2-spring-boot-starter</artifactId><version>4.1.0</version>
</dependency>
<!--web-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
然后需要配置 swagger 信息:
knife4j:enable: trueopenapi:title: 用户管理接口文档description: "用户管理接口文档"email: zhanghuyi@itcast.cnconcat: 嘻嘻url: https://www.itcast.cnversion: v1.0.0group:default:group-name: defaultapi-rule: packageapi-rule-resources:- com.itheima.mp.controller
然后,接口需要两个实体:
- UserFormDTO:代表新增时的用户表单
- UserVO:代表查询的返回结果
首先是 UserFormDTO:
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;@Data
@ApiModel(description = "用户表单实体")
public class UserFormDTO {@ApiModelProperty("id")private Long id;@ApiModelProperty("用户名")private String username;@ApiModelProperty("密码")private String password;@ApiModelProperty("注册手机号")private String phone;@ApiModelProperty("详细信息,JSON风格")private String info;@ApiModelProperty("账户余额")private Integer balance;
}
然后是 UserVO:
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;@Data
@ApiModel(description = "用户VO实体")
public class UserVO {@ApiModelProperty("用户id")private Long id;@ApiModelProperty("用户名")private String username;@ApiModelProperty("详细信息")private String info;@ApiModelProperty("使用状态(1正常 2冻结)")private Integer status;@ApiModelProperty("账户余额")private Integer balance;
}
项目结构如下:
最后,按照 Restful 风格编写 Controller 接口方法:
import cn.hutool.core.bean.BeanUtil;
import com.itheima.mp.domain.dto.UserFormDTO;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.domain.vo.UserVO;
import com.itheima.mp.service.IUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;import java.util.List;@RestController
@RequestMapping("/users")
@Api(tags = "用户管理接口")
@RequiredArgsConstructor //注入对象所需
public class UserController {//spring不推荐使用@Autoweird这种方式注入对象,而是推荐使用构造器的方式private final IUserService userService;/*** 新增用户** @param userFormDTO*/@PostMapping@ApiOperation("新增用户接口")public void saveUser(@RequestBody UserFormDTO userFormDTO) {//转换DTO为POUser user = BeanUtil.copyProperties(userFormDTO, User.class);userService.save(user);}/*** 删除用户** @param userId*/@DeleteMapping("/{id}")@ApiOperation("删除用户接口")public void deleteById(@ApiParam("用户id") @PathVariable("id") Long userId) {userService.removeById(userId);}/*** 根据id查询用户** @param userId* @return*/@GetMapping("/{id}")@ApiOperation("根据id查询用户")public UserVO queryUserById(@ApiParam("用户id") @PathVariable("id") Long userId) {//查询User user = userService.getById(userId);return BeanUtil.copyProperties(user, UserVO.class);}/*** 根据id集合查询用户** @param ids* @return*/@GetMapping@ApiOperation("根据id集合查询用户")public List<UserVO> queryUserByIds(@RequestParam("ids") List<Long> ids) {List<User> users = userService.listByIds(ids);return BeanUtil.copyToList(users, UserVO.class);}
}
可以看到上述接口都直接在 controller 即可实现,无需编写任何 service 代码,非常方便。
不过,一些带有业务逻辑的接口则需要在 service 中自定义实现了,最后一个接口就是。
根据 id 扣减用户余额,这看起来是个简单的修改功能,只要修改用户余额即可。其实这个业务包含一些业务逻辑处理:
- 判断用户状态是否正常
- 判断用户余额是否充足
这些业务逻辑都要在 service 层来做,另外更新余额需要自定义 SQL,要在 mapper 中来实现。因此,我们除了要编写 controller 以外,具体的业务还要在 service 和 mapper 中编写,我们工作中遇到的接口也是很复杂的。
首先在 UserController 中定义一个方法:
/*** 根据id扣减用户余额** @param id* @param money*/
@PutMapping("/{id}/deduction/{money}")
@ApiOperation("扣减用户余额接口")
public void deductBalance(@ApiParam("用户id") @PathVariable("id") Long id,@ApiParam("扣减的金额") @PathVariable("money") Integer money
) {userService.deductBalance(id, money);
}
然后是 UserService 接口:
/*** 根据id扣减用户余额** @param id* @param money*/
void deductBalance(Long id, Integer money);
UserServiceImpl 实现类:
/*** 根据id扣减用户余额** @param id* @param money*/
public void deductBalance(Long id, Integer money) {// 1.查询用户User user = getById(id);// 2.校验用户状态if (user == null || user.getStatus() == 2) {throw new RuntimeException("用户状态异常!");}// 3.校验余额是否充足if (user.getBalance() < money) {throw new RuntimeException("用户余额不足!");}// 4.扣减余额baseMapper.deductBalance(id, money); //继承的ServiceImpl已经注入了mapper,直接使用即可
}
最后是 mapper:
@Update("update user set balance = balance - #{money} where id = #{id}")
void deductBalance(@Param("id") Long id, @Param("money") Integer money);
启动服务,浏览器访问 http://localhost:8080/doc.html 接口文档,自行测试一下。
Lambda
IService 中还提供了 Lambda 功能来简化我们的复杂查询及更新功能。我们通过两个案例来学习一下。
案例一:实现一个根据复杂条件查询用户的接口,查询条件如下:
- name:用户名关键字,可以为空
- status:用户状态,可以为空
- minBalance:最小余额,可以为空
- maxBalance:最大余额,可以为空
可以理解成一个用户的后台管理界面,管理员可以自己选择条件来筛选用户,因此上述条件不一定存在,需要做判断。
我们首先需要定义一个查询条件实体,用来接口前端传过来的数据,UserQuery 实体:
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;@Data
@ApiModel(description = "用户查询条件实体")
public class UserQuery {@ApiModelProperty("用户名关键字")private String name;@ApiModelProperty("用户状态:1-正常,2-冻结")private Integer status;@ApiModelProperty("余额最小值")private Integer minBalance;@ApiModelProperty("余额最大值")private Integer maxBalance;
}
在 UserController 中定义一个 controller 方法:
/*** 根据复杂条件查询用户** @param query* @return*/
@GetMapping("/list")
@ApiOperation("根据复杂条件查询用户接口")
public List<UserVO> queryUsers(UserQuery query) {// 1.查询用户POList<User> users = userService.queryUsers(query.getName(), query.getStatus(), query.getMinBalance(), query.getMaxBalance());// 2.把PO拷贝到VOreturn BeanUtil.copyToList(users, UserVO.class);
}
在 IUserService 接口中定义方法:
/*** 根据复杂条件查询用户** @param name* @param status* @param minBalance* @param maxBalance* @return*/
List<User> queryUsers(String name, Integer status, Integer minBalance, Integer maxBalance);
在 UserServiceImpl 中实现方法:
Service 中对 LambdaQueryWrapper 和 LambdaUpdateWrapper 的用法进一步做了简化。我们无需自己通过 new 的方式来创建 Wrapper,而是直接调用 lambdaQuery 和 lambdaUpdate 方法即可实现。
/*** 根据复杂条件查询用户** @param name* @param status* @param minBalance* @param maxBalance* @return*/
public List<User> queryUsers(String name, Integer status, Integer minBalance, Integer maxBalance) {return lambdaQuery().like(name != null, User::getUsername, name).eq(status != null, User::getStatus, status).ge(minBalance != null, User::getBalance, minBalance).le(maxBalance != null, User::getBalance, maxBalance).list();
}
可以发现 lambdaQuery 方法中除了可以构建条件,还需要在链式编程的最后添加一个 list(),这是在告诉 MP 我们的调用结果需要是一个 list 集合。这里不仅可以用 list(),可选的方法有:
.one()
:最多1个结果.list()
:返回集合结果.count()
:返回计数结果
MybatisPlus 会根据链式编程的最后一个方法来判断最终的返回结果。
测试一下:
与 lambdaQuery 方法类似,IService 中的 lambdaUpdate 方法可以非常方便的实现复杂更新业务。
案例二:改造根据 id 修改用户余额的接口,要求如下
- 如果扣减后余额为 0,则将用户 status 修改为冻结状态(2)
/*** 根据id扣减用户余额** @param id* @param money*/
@Transactional
public void deductBalance(Long id, Integer money) {// 1.查询用户User user = getById(id);// 2.校验用户状态if (user == null || user.getStatus() == 2) {throw new RuntimeException("用户状态异常!");}// 3.校验余额是否充足if (user.getBalance() < money) {throw new RuntimeException("用户余额不足!");}// 4.扣减余额int remainBalance = user.getBalance() - money;lambdaUpdate().set(User::getBalance, remainBalance).set(remainBalance == 0, User::getStatus, 2).eq(User::getId, id).eq(User::getBalance, user.getBalance()) //乐观锁,防止到更新这一步前,有另外的进程也同时在请求扣减用于余额导致余额不对.update();
}
批量新增
需求:如果我们往数据库中批量插入 10 万条用户数据,并对以下三种方式作出对比:
- 使用普通 for 循环插入
- 使用 IService 的批量插入
- 配置 MySQL 的 rewriteBatchedStatements=true 参数
方式一:
@Test
void testSaveOneByOne() {long b = System.currentTimeMillis();for (int i = 1; i <= 100000; i++) {userService.save(buildUser(i));}long e = System.currentTimeMillis();System.out.println("耗时:" + (e - b));
}private User buildUser(int i) {User user = new User();user.setUsername("user_" + i);user.setPassword("123");user.setPhone("" + (18688190000L + i));user.setBalance(2000);user.setInfo("{\"age\": 24, \"intro\": \"英文老师\", \"gender\": \"female\"}");user.setCreateTime(LocalDateTime.now());user.setUpdateTime(user.getCreateTime());return user;
}
耗时 3 分钟多,可以看到速度非常慢。
方式二:
@Test
void testSaveBatch() {// 准备10万条数据List<User> list = new ArrayList<>(1000);long b = System.currentTimeMillis();for (int i = 1; i <= 100000; i++) {list.add(buildUser(i));// 每1000条批量插入一次if (i % 1000 == 0) {userService.saveBatch(list);list.clear();}}long e = System.currentTimeMillis();System.out.println("耗时:" + (e - b));
}
将前面插入的数据删除后,我们进行方式二的测试:
耗时 31 秒,性能还可以。
方式三:
MySQL 的客户端连接参数中有这样的一个参数:rewriteBatchedStatements
。顾名思义,就是重写批处理的 statement 语句。参考文档:https://dev.mysql.com/doc/connector-j/en/connector-j-connp-props-performance-extensions.html
配置该参数后,可以将 MybatisPlus 批处理的 SQL 语言合并成一句,从而大大提升性能。
在配置文件中,数据库的 url 后面加上 &rewriteBatchedStatements=true
即可:
现在,使用 MybatisPlus 的批处理只耗时 8 秒钟,性能极大提升,而且随着数据量的增大效果会越明显。
这篇关于【Springcloud微服务】MybatisPlus上篇的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!