自定义lamba表达式属性名转字段名方法(mybaits-plus多表连接查询,无法使用lamba表达式指定获取字段名的解决办法)
背景
使用mybatis-plus进行多表连接查询时需要自定义sql,可以将mybatis-plus的条件构造器传入进去,生成选择语句与筛选语句(如下图)
让mybatis-plus条件构造器生成查询条件和选择语句可以使查询更加灵活,
mybatis-plus条件构造器 支持lamba表达式,但仅限于QueryWrapper管理的类
如果是多表查询,想要查另一个表的字段,只能使用传入字符串的方式。
如果两个表都同时有name这个字段,我们还需加上前缀,而LambaQueryWrapper是无法传入前缀的,我们只能传入字符串,这样极易产生魔法值导致项目不易维护。
经过思考,我们可以复用mybait-plus将属性转为表中对应字段的方法,在其基础上进行扩展,最终达到能任何实体类都能通过lamba表达式获取对应数据库字段,同时可以为其配置表前缀的目的。
实现
自定义字段转换器类ColumnResolver
package marchsoft.modules.linglu.common.utils;
import com.baomidou.mybatisplus.core.conditions.AbstractLambdaWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import java.util.Arrays;
import static java.util.stream.Collectors.joining;
/**
* @author lixiangxiang
* @description 字段转换器
*/
public class ColumnResolver<T> extends AbstractLambdaWrapper<T, ColumnResolver<T>> {
@Override
protected ColumnResolver<T> instance() {
return null;
}
@SafeVarargs
@Override
public final String columnsToString(SFunction<T, ?>... columns) {
return super.columnsToString(columns);
}
/**
* description: 实体类属性转数据库字段名+前缀 批量
*
* @author: lixiangxiang
* @param prefix 前缀
* @param columns 字段
* @return java.lang.String
*/
@SafeVarargs
public final String columnsToStringWithPrefix(String prefix, SFunction<T, ?>... columns) {
StringBuilder stringBuilder = new StringBuilder();
return Arrays.stream(columns).map(i -> columnsToStringWithPrefix(prefix,i)).collect(joining(StringPool.COMMA));
}
/**
* description: 实体类属性转数据库字段名+前缀
*
* @author: lixiangxiang
* @param prefix 前缀
* @param column 字段
* @return java.lang.String
*/
public final String columnsToStringWithPrefix(String prefix, SFunction<T, ?> column) {
return prefix + StringPool.DOT + super.columnToString(column);
}
@SafeVarargs
@Override
public final String columnsToString(boolean onlyColumn, SFunction<T, ?>... columns) {
return super.columnsToString(onlyColumn, columns);
}
@Override
public String columnToString(SFunction<T, ?> column) {
return super.columnToString(column);
}
@Override
public String columnToString(SFunction<T, ?> column, boolean onlyColumn) {
return super.columnToString(column, onlyColumn);
}
}
使用实例
案例描述,业务需要筛选出部门id 为 3 的用户user的姓名、年龄和部门名称,部门dpet在另一张关系表中存储。用户表使用别名u,部门表使用别名d
service层代码
QueryWrapper<User> queryWrapper = new QueryWrapper<Course>
ColumnResolver<Job> DeptResolver = new ColumnResolver<>();
ColumnResolver<Job> UserResolver = new ColumnResolver<>();
String userPrefix = "u";
String deptPrefix = "d";
queryWrapper.select(UserResolver.columnsToStringWithPrefix(userPrefix,User:;getName,User::getAge),
DeptResolver.columnsToStringWithPrefix(deptPrefix,Dept::getName));
Integer deptId = 3;
queryWrpper.eq(DeptReslover.columnsToStringWithPrefix("t",Dept::id),deptId);
userMapper.selectUserByDeptId(queryWrapper);
Mapper层代码
@Select({"select ${ew.sqlSelect}",
"from user u",
"left join dept d",
"on u.deptId = d.id",
"${ew.customSqlSegment}"})
selectUserByDeptId(@Param(Constants.WRAPPER)QueryWrapper queryWrapper)
前缀可以用枚举定义,为每个表指定特定的别名,保证写的sql语句中的表别名与枚举类中的一致即可。
(还有一种方案,不要让表指定别名,统一用表名来访问,枚举中定义表名,这样能最大程度上确保自己不会写错。)
注意:如果字段需要加前缀,请使用columnsToStringWithPrefix方法,prefix为sql语句中为表指定的别名,必须保证prefix与表别名一致