当前使用版本(必填,否则不予处理)
3.4.0
该问题是如何引起的?(确定最新版也有问题再提!!!)
重现步骤(如果有就写完整)
当想要调用多次 select 时,是否可以将每次的字段都附加进去,而不是只有最后一次才生效,比如像下面这样
LambdaQueryWrapper<XX> xxQuery = Wrappers.<XX>query().select("DISTINCT aa") // 或者像 ifnull(aa, '123')
.lambda().select(XX::getBb, Orders::getCc);
...
最终想要生成的 sql 是
select DISTINCT aa, bb, cc from xx ... /* 或者 select ifnull(aa, '123'), bb, cc from xx ... */
因为 lambda 只能用属性名,所以只能先用 QueryWrapper 再转换成 lambda 然后再次调用 select,这样会调用多次 select,但实际却只有最后的 select 才有效
报错信息
无
Comment From: liuanxin
几天过去, 见到两年前的这个 issue, 我这个应该也是不会被处理的
我抽取了这么两个方法出来
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.support.ColumnCache;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda;
import com.google.common.base.CaseFormat;
import com.google.common.collect.Lists;
import org.apache.ibatis.reflection.property.PropertyNamer;
import java.util.List;
import java.util.Map;
/**
* <pre>
* mp 工具类
*
* 比如有表
* CREATE TABLE IF NOT EXISTS `t_user` (
* `id` bigint unsigned NOT NULL AUTO_INCREMENT,
* `user_name` varchar(32) NOT NULL DEFAULT '' COMMENT '用户名',
* `password` varchar(64) NOT NULL DEFAULT '' COMMENT '密码',
* `nick_name` varchar(16) NOT NULL DEFAULT '' COMMENT '昵称',
* `gender` int unsigned NOT NULL DEFAULT '0' COMMENT '性别(0.未知, 1.男, 2.女)',
* `status` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '1.已禁用',
* `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
* `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
* `is_deleted` bigint unsigned NOT NULL DEFAULT '0' COMMENT '0 表示未删除, 删除时将当前值置为主键 id 或时间戳',
* PRIMARY KEY (`id`),
* UNIQUE KEY `user_name` (`user_name`, `is_deleted`)
* ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户';
*
* 其对应的实体是
* /** 用户 --> t_user */
* @Data
* @TableName("t_user")
* public class User {
* private Long id;
*
* /** 用户名 --> user_name */
* private String userName;
*
* /** 密码 --> password */
* private String password;
*
* /** 昵称 --> nick_name */
* private String nickName;
*
* /** 性别(0.未知, 1.男, 2.女) --> gender */
* private Gender gender;
*
* /** 1.已禁用 --> status */
* private Boolean status;
*
* /** 创建时间 --> create_time */
* private Date createTime;
*
* /** 更新时间 --> update_time */
* private Date updateTime;
*
* /** 0 表示未删除, 删除时将当前值置为主键 id 或时间戳 --> is_deleted */
* @TableLogic(value = "0", delval = "unix_timestamp()")
* private Long isDeleted;
* }
* </pre>
*/
public class MybatisPlusUtil {
/**
* java 字段转换成数据库列名, 如: columnToString(User::getUserName) 返回 user_name
*
* @param column lambda 表达式对应的字段, 如 User::getId
* @param <T> 数据库表对应的实体
* @return 实体中的属性对应的数据库字段名, 如 id
*/
public static <T> String fieldToColumn(SFunction<T, ?> column) {
SerializedLambda lambda = LambdaUtils.resolve(column);
String fieldName = PropertyNamer.methodToProperty(lambda.getImplMethodName());
Map<String, ColumnCache> columnMap = LambdaUtils.getColumnMap(lambda.getInstantiatedType());
String returnColumn;
if (columnMap != null && columnMap.size() > 0) {
returnColumn = columnMap.get(LambdaUtils.formatKey(fieldName)).getColumn();
} else {
returnColumn = null;
}
if (returnColumn == null || returnColumn.trim().length() == 0) {
// 上面的不成功就按驼峰规则转换: lowerCamel => lower_camel)
// 如果是大写(lowerCamel => LOWER_CAMEL)则用 UPPER_UNDERSCORE
return CaseFormat.LOWER_CAMEL.converterTo(CaseFormat.LOWER_UNDERSCORE).convert(fieldName);
} else {
return returnColumn;
}
}
/**
* java 字段转换成数据库列名<br><br>
*
* 使用 columnsToString(User::getUserName, User::getPassword, User::getNickName)<br>
* 返回 [user_name, password, nick_name]
*
* @param columns lambda 表达式对应的字段数组, 如 [User::getId, User::getUserName]
* @param <T> 数据库表对应的实体
* @return 实体中的属性对应的数据库字段名, 如 [id, user_name]
*/
@SuppressWarnings("unchecked")
public static <T> String[] fieldsToColumnArray(SFunction<T, ?>... columns) {
List<String> returnList = Lists.newArrayList();
for (SFunction<T, ?> column : columns) {
returnList.add(fieldToColumn(column));
}
return returnList.toArray(new String[0]);
}
}
3.4.3 里面的 api 重构过, 是这样
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.support.ColumnCache;
import com.baomidou.mybatisplus.core.toolkit.support.LambdaMeta;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.google.common.base.CaseFormat;
import com.google.common.collect.Lists;
import org.apache.ibatis.reflection.property.PropertyNamer;
import java.util.List;
import java.util.Map;
/**
* <pre>
* mp 工具类
*
* 比如有表
* CREATE TABLE IF NOT EXISTS `t_user` (
* `id` bigint unsigned NOT NULL AUTO_INCREMENT,
* `user_name` varchar(32) NOT NULL DEFAULT '' COMMENT '用户名',
* `password` varchar(64) NOT NULL DEFAULT '' COMMENT '密码',
* `nick_name` varchar(16) NOT NULL DEFAULT '' COMMENT '昵称',
* `gender` int unsigned NOT NULL DEFAULT '0' COMMENT '性别(0.未知, 1.男, 2.女)',
* `status` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '1.已禁用',
* `create_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
* `update_time` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
* `is_deleted` bigint unsigned NOT NULL DEFAULT '0' COMMENT '0 表示未删除, 删除时将当前值置为主键 id 或时间戳',
* PRIMARY KEY (`id`),
* UNIQUE KEY `user_name` (`user_name`, `is_deleted`)
* ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户';
*
* 其对应的实体是
* /** 用户 --> t_user */
* @Data
* @TableName("t_user")
* public class User {
* private Long id;
*
* /** 用户名 --> user_name */
* private String userName;
*
* /** 密码 --> password */
* private String password;
*
* /** 昵称 --> nick_name */
* private String nickName;
*
* /** 性别(0.未知, 1.男, 2.女) --> gender */
* private Gender gender;
*
* /** 1.已禁用 --> status */
* private Boolean status;
*
* /** 创建时间 --> create_time */
* private Date createTime;
*
* /** 更新时间 --> update_time */
* private Date updateTime;
*
* /** 0 表示未删除, 删除时将当前值置为主键 id 或时间戳 --> is_deleted */
* @TableLogic(value = "0", delval = "unix_timestamp()")
* private Long isDeleted;
* }
* </pre>
*/
public class MybatisPlusUtil {
/**
* java 字段转换成数据库列名, 如: columnToString(User::getUserName) 返回 user_name
*
* @param column lambda 表达式对应的字段, 如 User::getId
* @param <T> 数据库表对应的实体
* @return 实体中的属性对应的数据库字段名, 如 id
*/
public static <T> String fieldToColumn(SFunction<T, ?> column) {
LambdaMeta lambda = LambdaUtils.extract(column);
String fieldName = PropertyNamer.methodToProperty(lambda.getImplMethodName());
Map<String, ColumnCache> columnMap = LambdaUtils.getColumnMap(lambda.getInstantiatedClass());
String returnColumn;
if (columnMap != null && columnMap.size() > 0) {
returnColumn = columnMap.get(LambdaUtils.formatKey(fieldName)).getColumn();
} else {
returnColumn = null;
}
if (returnColumn == null || returnColumn.trim().length() == 0) {
// 上面的不成功就按驼峰规则转换: lowerCamel => lower_camel)
// 如果是大写(lowerCamel => LOWER_CAMEL)则用 UPPER_UNDERSCORE
return CaseFormat.LOWER_CAMEL.converterTo(CaseFormat.LOWER_UNDERSCORE).convert(fieldName);
} else {
return returnColumn;
}
}
/**
* java 字段转换成数据库列名<br><br>
*
* 使用 columnsToString(User::getUserName, User::getPassword, User::getNickName)<br>
* 返回 [user_name, password, nick_name]
*
* @param columns lambda 表达式对应的字段数组, 如 [User::getId, User::getUserName]
* @param <T> 数据库表对应的实体
* @return 实体中的属性对应的数据库字段名, 如 [id, user_name]
*/
@SuppressWarnings("unchecked")
public static <T> String[] fieldsToColumnArray(SFunction<T, ?>... columns) {
List<String> returnList = Lists.newArrayList();
for (SFunction<T, ?> column : columns) {
returnList.add(fieldToColumn(column));
}
return returnList.toArray(new String[0]);
}
}
调用多次的问题也就不重要了, 把 lambda 当 string 用, 直接基于 QueryWrapper 就好了
Comment From: philyang7
翻了半天终于翻到和我一样的疑惑了。。。强迫症是真不想把数据库字段硬编码到select里。。。 不过你这个应该不用这么麻烦吧,直接硬编码进去就行了。
like this:
LambdaQueryWrapper
Comment From: liuanxin
翻了半天终于翻到和我一样的疑惑了。。。强迫症是真不想把数据库字段硬编码到select里。。。 不过你这个应该不用这么麻烦吧,直接硬编码进去就行了。
like this:
LambdaQueryWrapper xxQuery = Wrappers.query().select("DISTINCT aa","bb","cc") .lambda().eq(XX::getBb, "");
主要也就是不想要硬编字段名, 在用到字段的时候用 lambda, 其实还是基于 QueryWrapper + string 来整的
QueryWrapper<XXX> xxxQuery = Wrappers.query();
xxxQuery.select(
"DISTINCT " + MybatisPlusUtil.columnToString(XXX::getX),
MybatisPlusUtil.columnToString(XXX::getXxx)
...
);
xxxQuery.eq(MybatisPlusUtil.columnToString(XXX::getXX), xxxxx);
避免直接写字段名的同时, 也无所谓多次调用 select 了, 只能调一次就只调一次吧