一、核心用法
1.sql求和
QueryWrapper<DeviceHisTelemetry> queryWrapper = Wrappers.query();
queryWrapper.lambda().in(DeviceHisTelemetry::getDeviceId, deviceIdList)
.in(DeviceHisTelemetry::getTimeSeriesKey, Lists.newArrayList(TimeSeriesConstant.RELAY_ON_DURATION));
queryWrapper.select("sum(IFNULL(long_v,0)) as long_v");
DeviceHisTelemetry hisTelemetrySum = deviceHisTelemetryService.getOne(queryWrapper);
long relayOnDurationSum = Optional.ofNullable(hisTelemetrySum).map(DeviceHisTelemetry::getLongV).orElse(0L);
如果只会返回一行数据,这里建议使用getOne方法,因为这样返回对象就是null好进行判断,如果用list方法,会返回一个size为1,但所有元素都是null的结果,容易导致代码出错
2.or拼接
or拼接复杂的语句
LambdaQueryWrapper<SysI18n> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.select(SysI18n::getId, SysI18n::getI18nKey, SysI18n::getLanguage, SysI18n::getI18nValue);
for (SysI18nExcelModel excelModel : cachedDataList) {
lambdaQueryWrapper.or(i -> i.eq(SysI18n::getI18nKey, excelModel.getI18nKey())
.eq(SysI18n::getLanguage, excelModel.getLanguage()));
}
具体编译执行后的结果和下面类似
SELECT id, i18n_key FROM sys_i18n WHERE (
( i18n_key = 'i18n.general.dept.AQ0101010101' AND LANGUAGE = 'zh-CN' )
OR ( i18n_key = 'i18n.general.dept.AQ0101010206' AND LANGUAGE = 'zh-CN')
)
二、性能分析
1.批量插入性能
1.批量插入性能差的原因
使用saveBatch()方法时, MySQL JDBC驱动在默认情况下会无视executeBatch()语句,把我们期望批量执行的一组sql语句拆散,一条一条地发给MySQL数据库,批量插入实际上是单条插入,直接造成较低的性能。
查看对应源码
public static <E> boolean executeBatch(Class<?> entityClass, Log log, Collection<E> list, int batchSize, BiConsumer<SqlSession, E> consumer) {
Assert.isFalse(batchSize < 1, "batchSize must not be less than one", new Object[0]);
return !CollectionUtils.isEmpty(list) && executeBatch(entityClass, log, (sqlSession) -> {
int size = list.size();
int i = 1;
for(Iterator var6 = list.iterator(); var6.hasNext(); ++i) {
E element = var6.next();
consumer.accept(sqlSession, element);
if (i % batchSize == 0 || i == size) {
sqlSession.flushStatements();
}
}
});
}
最终来到了executeBatch()方法,可以看到这很明显是在一条一条循环插入,通过sqlSession.flushStatements()将一个个单条插入的insert语句分批次进行提交,而且是同一个sqlSession,这相比遍历集合循环insert来说有一定的性能提升,但是这并不是sql层面真正的批量插入。
2.解决批量插入性能差方案
通过查阅相关文档后,发现mybatisPlus提供了sql注入器,我们可以自定义方法来满足业务的实际开发需求。
在扩展包下,mybatisPlus还为我们提供了可扩展的可注入方法:
AlwaysUpdateSomeColumnById: 根据Id更新每一个字段,全量更新不忽略null字段,解决mybatis-plus中updateById默认会自动忽略实体中null值字段不去更新的问题;
InsertBatchSomeColumn: 真实批量插入,通过单SQL的insert语句实现批量插入;
Upsert: 更新or插入,根据唯一约束判断是执行更新还是删除,相当于提供insert on duplicate key update支持。
那么现在开始解决批量插入性能差的问题
1.创建自定义sql注入器,并继承DefaultSqlInjector
/**
* @author zhmsky
* @date 2022/8/15 15:13
*/
public class MySqlInjector extends DefaultSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
List<AbstractMethod> methodList = super.getMethodList(mapperClass);
//更新时自动填充的字段,不用插入值
methodList.add(new InsertBatchSomeColumn(i -> i.getFieldFill() != FieldFill.UPDATE));
return methodList;
}
}
2.将自定义的sql注入器注入到Mybatis容器中
/**
* @author zhmsky
* @date 2022/8/15 15:15
*/
@Configuration
public class MybatisPlusConfig {
@Bean
public MySqlInjector sqlInjector() {
return new MySqlInjector();
}
}
3.业务开始使用
使用可以继承 BaseMapper 添加自定义方法
/**
* @author zhmsky
* @date 2022/8/15 15:17
*/
public interface CommonMapper<T> extends BaseMapper<T> {
/**
* 真正的批量插入
* @param entityList
* @return
*/
int insertBatchSomeColumn(List<T> entityList);
}
或者直接在业务中使用
public interface CpChickSupplyChainFlockProPreDMapper extends BaseMapper<CpChickSupplyChainFlockProPreD> {
/**
* 执行 executeBatch()语句实现真正的批量插入
* @param entityList
* @return
*/
int insertBatchSomeColumn(Collection<CpChickSupplyChainFlockProPreD> entityList);
}
执行后日志输出
2.批量删除性能
和批量插入的原因一样,使用mybatisPlus的IService.removeBatchByIds默认的方法,最后也是一条一条去执行的,性能比较差。
所以在执行批量删除时,不要使用IService的默认方法,应该使用BaseMapper.deleteBatchIds方法,它才是真正的执行了批量操作。
执行日志如下
08:49:12.538 [http-nio-9502-exec-2] DEBUG c.c.p.m.C.deleteBatchIds - [debug,137] - ==> Preparing: DELETE FROM cp_chick_supply_chain_flock_pro_pre_d WHERE id IN ( ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? )
三、mybatis-plus-join插件
1.引入pom
<dependency>
<groupId>com.github.yulichang</groupId>
<artifactId>mybatis-plus-join-boot-starter</artifactId>
<version>1.4.6</version>
</dependency>
2.接口继承MPJBaseMapper
public interface DeviceMapper extends BaseMapper<Device>, MPJBaseMapper<Device> {
}
3.业务使用
class test {
@Resource
private UserMapper userMapper;
void testJoin() {
List<UserDTO> list = userMapper.selectJoinList(UserDTO.class,
new MPJLambdaWrapper<UserDO>()
.selectAll(UserDO.class)
.select(UserAddressDO::getTel)
.selectAs(UserAddressDO::getAddress, UserDTO::getUserAddress)
.select(AreaDO::getProvince, AreaDO::getCity)
.leftJoin(UserAddressDO.class, UserAddressDO::getUserId, UserDO::getId)
.leftJoin(AreaDO.class, AreaDO::getId, UserAddressDO::getAreaId)
.eq(UserDO::getId, 1)
.like(UserAddressDO::getTel, "1")
.gt(UserDO::getId, 5));
}
}
参考资料