新增自定义sql的通用wrapper

This commit is contained in:
admin 2021-02-19 16:14:01 +08:00
parent 7a3b201e79
commit fa474ddbe6
6 changed files with 831 additions and 6 deletions

View File

@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.github.yulichang</groupId>
<artifactId>mybatis-plus-join</artifactId>
<version>1.0.8</version>
<version>1.0.9</version>
<name>mybatis-plus-join</name>
<description>An enhanced toolkit of Mybatis-Plus to simplify development.</description>
<url>https://github.com/yulichang/mybatis-plus-join</url>

View File

@ -0,0 +1,65 @@
package com.github.yulichang.common;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.support.ColumnCache;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.github.yulichang.toolkit.LambdaUtils;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import static java.util.stream.Collectors.joining;
/**
* copy {@link com.baomidou.mybatisplus.core.conditions.AbstractLambdaWrapper}
*/
@SuppressWarnings("serial")
public abstract class JoinAbstractLambdaWrapper<T, Children extends JoinAbstractLambdaWrapper<T, Children>>
extends JoinAbstractWrapper<T, Children> {
private Map<String, ColumnCache> columnMap = null;
private boolean initColumnMap = false;
protected final Map<Class<?>, String> subTable = new HashMap<>();
@SuppressWarnings("unchecked")
protected <X> String columnsToString(SFunction<X, ?>... columns) {
return columnsToString(true, columns);
}
@Override
protected <X> String columnToString(X column) {
return columnToString((SFunction<?, ?>) column, true);
}
@SuppressWarnings("unchecked")
protected <X> String columnsToString(boolean onlyColumn, SFunction<X, ?>... columns) {
return Arrays.stream(columns).map(i -> columnToString(i, onlyColumn)).collect(joining(StringPool.COMMA));
}
@Override
protected <X> String columnsToString(X... columns) {
return Arrays.stream(columns).map(i -> columnToString((SFunction<?, ?>) i, true)).collect(joining(StringPool.COMMA));
}
protected String columnToString(SFunction<?, ?> column, boolean onlyColumn) {
return getDefault(LambdaUtils.getEntityClass(column)) + StringPool.DOT +
LambdaUtils.getColumn(column);
}
protected String getDefault(Class<?> clazz) {
String alias = subTable.get(clazz);
if (StringUtils.isNotBlank(alias)) {
return alias;
}
TableInfo tableInfo = TableInfoHelper.getTableInfo(clazz);
Assert.notNull(tableInfo, "can not find table");
return tableInfo.getTableName();
}
}

View File

@ -0,0 +1,511 @@
package com.github.yulichang.common;
import com.baomidou.mybatisplus.core.conditions.ISqlSegment;
import com.baomidou.mybatisplus.core.conditions.SharedString;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.interfaces.Join;
import com.baomidou.mybatisplus.core.conditions.interfaces.Nested;
import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;
import com.baomidou.mybatisplus.core.enums.SqlKeyword;
import com.baomidou.mybatisplus.core.enums.SqlLike;
import com.baomidou.mybatisplus.core.toolkit.*;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlUtils;
import com.baomidou.mybatisplus.core.toolkit.sql.StringEscape;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.github.yulichang.wrapper.interfaces.Compare;
import com.github.yulichang.wrapper.interfaces.Func;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import static com.baomidou.mybatisplus.core.enums.SqlKeyword.*;
import static com.baomidou.mybatisplus.core.enums.WrapperKeyword.APPLY;
import static java.util.stream.Collectors.joining;
/**
* copy {@link com.baomidou.mybatisplus.core.conditions.AbstractWrapper}
*/
@SuppressWarnings({"serial", "unchecked"})
public abstract class JoinAbstractWrapper<T, Children extends JoinAbstractWrapper<T, Children>> extends Wrapper<T>
implements Compare<Children>, Nested<Children, Children>, Join<Children>, Func<Children> {
/**
* 占位符
*/
protected final Children typedThis = (Children) this;
/**
* 必要度量
*/
protected AtomicInteger paramNameSeq;
protected Map<String, Object> paramNameValuePairs;
protected SharedString lastSql;
/**
* SQL注释
*/
protected SharedString sqlComment;
/**
* SQL起始语句
*/
protected SharedString sqlFirst;
/**
* ß
* 数据库表映射实体类
*/
private T entity;
protected MergeSegments expression;
/**
* 实体类型(主要用于确定泛型以及取TableInfo缓存)
*/
private Class<T> entityClass;
@Override
public T getEntity() {
return entity;
}
public Children setEntity(T entity) {
this.entity = entity;
return typedThis;
}
public Class<T> getEntityClass() {
if (entityClass == null && entity != null) {
entityClass = (Class<T>) entity.getClass();
}
return entityClass;
}
public Children setEntityClass(Class<T> entityClass) {
if (entityClass != null) {
this.entityClass = entityClass;
}
return typedThis;
}
@Override
public <X, V> Children allEq(boolean condition, Map<SFunction<X, ?>, V> params, boolean null2IsNull) {
if (condition && CollectionUtils.isNotEmpty(params)) {
params.forEach((k, v) -> {
if (StringUtils.checkValNotNull(v)) {
eq(k, v);
} else {
if (null2IsNull) {
isNull(k);
}
}
});
}
return typedThis;
}
@Override
public <X, V> Children allEq(boolean condition, BiPredicate<SFunction<X, ?>, V> filter, Map<SFunction<X, ?>, V> params, boolean null2IsNull) {
if (condition && CollectionUtils.isNotEmpty(params)) {
params.forEach((k, v) -> {
if (filter.test(k, v)) {
if (StringUtils.checkValNotNull(v)) {
eq(k, v);
} else {
if (null2IsNull) {
isNull(k);
}
}
}
});
}
return typedThis;
}
@Override
public <X> Children eq(boolean condition, SFunction<X, ?> column, Object val) {
return addCondition(condition, column, EQ, val);
}
@Override
public <X> Children ne(boolean condition, SFunction<X, ?> column, Object val) {
return addCondition(condition, column, NE, val);
}
@Override
public <X> Children gt(boolean condition, SFunction<X, ?> column, Object val) {
return addCondition(condition, column, GT, val);
}
@Override
public <X> Children ge(boolean condition, SFunction<X, ?> column, Object val) {
return addCondition(condition, column, GE, val);
}
@Override
public <X> Children lt(boolean condition, SFunction<X, ?> column, Object val) {
return addCondition(condition, column, LT, val);
}
@Override
public <X> Children le(boolean condition, SFunction<X, ?> column, Object val) {
return addCondition(condition, column, LE, val);
}
@Override
public <X> Children like(boolean condition, SFunction<X, ?> column, Object val) {
return likeValue(condition, LIKE, column, val, SqlLike.DEFAULT);
}
@Override
public <X> Children notLike(boolean condition, SFunction<X, ?> column, Object val) {
return likeValue(condition, NOT_LIKE, column, val, SqlLike.DEFAULT);
}
@Override
public <X> Children likeLeft(boolean condition, SFunction<X, ?> column, Object val) {
return likeValue(condition, LIKE, column, val, SqlLike.LEFT);
}
@Override
public <X> Children likeRight(boolean condition, SFunction<X, ?> column, Object val) {
return likeValue(condition, LIKE, column, val, SqlLike.RIGHT);
}
@Override
public <X> Children between(boolean condition, SFunction<X, ?> column, Object val1, Object val2) {
return doIt(condition, () -> columnToString(column), BETWEEN, () -> formatSql("{0}", val1), AND,
() -> formatSql("{0}", val2));
}
@Override
public <X> Children notBetween(boolean condition, SFunction<X, ?> column, Object val1, Object val2) {
return doIt(condition, () -> columnToString(column), NOT_BETWEEN, () -> formatSql("{0}", val1), AND,
() -> formatSql("{0}", val2));
}
@Override
public Children and(boolean condition, Consumer<Children> consumer) {
return and(condition).addNestedCondition(condition, consumer);
}
@Override
public Children or(boolean condition, Consumer<Children> consumer) {
return or(condition).addNestedCondition(condition, consumer);
}
@Override
public Children nested(boolean condition, Consumer<Children> consumer) {
return addNestedCondition(condition, consumer);
}
@Override
public Children not(boolean condition, Consumer<Children> consumer) {
return not(condition).addNestedCondition(condition, consumer);
}
@Override
public Children or(boolean condition) {
return doIt(condition, OR);
}
@Override
public Children apply(boolean condition, String applySql, Object... value) {
return doIt(condition, APPLY, () -> formatSql(applySql, value));
}
@Override
public Children last(boolean condition, String lastSql) {
if (condition) {
this.lastSql.setStringValue(StringPool.SPACE + lastSql);
}
return typedThis;
}
@Override
public Children comment(boolean condition, String comment) {
if (condition) {
this.sqlComment.setStringValue(comment);
}
return typedThis;
}
@Override
public Children first(boolean condition, String firstSql) {
if (condition) {
this.sqlFirst.setStringValue(firstSql);
}
return typedThis;
}
@Override
public Children exists(boolean condition, String existsSql) {
return doIt(condition, EXISTS, () -> String.format("(%s)", existsSql));
}
@Override
public Children notExists(boolean condition, String existsSql) {
return not(condition).exists(condition, existsSql);
}
@Override
public <X> Children isNull(boolean condition, SFunction<X, ?> column) {
return doIt(condition, () -> columnToString(column), IS_NULL);
}
@Override
public <X> Children isNotNull(boolean condition, SFunction<X, ?> column) {
return doIt(condition, () -> columnToString(column), IS_NOT_NULL);
}
@Override
public <X> Children in(boolean condition, SFunction<X, ?> column, Collection<?> coll) {
return doIt(condition, () -> columnToString(column), IN, inExpression(coll));
}
@Override
public <X> Children notIn(boolean condition, SFunction<X, ?> column, Collection<?> coll) {
return doIt(condition, () -> columnToString(column), NOT_IN, inExpression(coll));
}
@Override
public <X> Children inSql(boolean condition, SFunction<X, ?> column, String inValue) {
return doIt(condition, () -> columnToString(column), IN, () -> String.format("(%s)", inValue));
}
@Override
public <X> Children notInSql(boolean condition, SFunction<X, ?> column, String inValue) {
return doIt(condition, () -> columnToString(column), NOT_IN, () -> String.format("(%s)", inValue));
}
@Override
public <X> Children groupBy(boolean condition, SFunction<X, ?>... columns) {
if (ArrayUtils.isEmpty(columns)) {
return typedThis;
}
return doIt(condition, GROUP_BY,
() -> columns.length == 1 ? columnToString(columns[0]) : columnsToString(columns));
}
@Override
public <X> Children orderBy(boolean condition, boolean isAsc, SFunction<X, ?>... columns) {
if (ArrayUtils.isEmpty(columns)) {
return typedThis;
}
SqlKeyword mode = isAsc ? ASC : DESC;
for (SFunction<X, ?> column : columns) {
doIt(condition, ORDER_BY, () -> columnToString(column), mode);
}
return typedThis;
}
@Override
public Children having(boolean condition, String sqlHaving, Object... params) {
return doIt(condition, HAVING, () -> formatSqlIfNeed(condition, sqlHaving, params));
}
@Override
public Children func(boolean condition, Consumer<Children> consumer) {
if (condition) {
consumer.accept(typedThis);
}
return typedThis;
}
/**
* 内部自用
* <p>NOT 关键词</p>
*/
protected Children not(boolean condition) {
return doIt(condition, NOT);
}
/**
* 内部自用
* <p>拼接 AND</p>
*/
protected Children and(boolean condition) {
return doIt(condition, AND);
}
/**
* 内部自用
* <p>拼接 LIKE 以及 </p>
*/
protected <X> Children likeValue(boolean condition, SqlKeyword keyword, SFunction<X, ?> column, Object val, SqlLike sqlLike) {
return doIt(condition, () -> columnToString(column), keyword, () -> formatSql("{0}", SqlUtils.concatLike(val, sqlLike)));
}
/**
* 普通查询条件
*
* @param condition 是否执行
* @param column 属性
* @param sqlKeyword SQL 关键词
* @param val 条件值
*/
protected <X> Children addCondition(boolean condition, SFunction<X, ?> column, SqlKeyword sqlKeyword, Object val) {
return doIt(condition, () -> columnToString(column), sqlKeyword, () -> formatSql("{0}", val));
}
/**
* 多重嵌套查询条件
*
* @param condition 查询条件值
*/
protected Children addNestedCondition(boolean condition, Consumer<Children> consumer) {
if (condition) {
final Children instance = instance();
consumer.accept(instance);
return doIt(true, APPLY, instance);
}
return typedThis;
}
/**
* 子类返回一个自己的新对象
*/
protected abstract Children instance();
/**
* 格式化SQL
*
* @param sqlStr SQL语句部分
* @param params 参数集
* @return sql
*/
protected final String formatSql(String sqlStr, Object... params) {
return formatSqlIfNeed(true, sqlStr, params);
}
/**
* <p>
* 根据需要格式化SQL<br>
* <br>
* Format SQL for methods: EntityQ<T>.where/and/or...("name={0}", value);
* ALL the {<b>i</b>} will be replaced with #{MPGENVAL<b>i</b>}<br>
* <br>
* ew.where("sample_name=<b>{0}</b>", "haha").and("sample_age &gt;<b>{0}</b>
* and sample_age&lt;<b>{1}</b>", 18, 30) <b>TO</b>
* sample_name=<b>#{MPGENVAL1}</b> and sample_age&gt;#<b>{MPGENVAL2}</b> and
* sample_age&lt;<b>#{MPGENVAL3}</b><br>
* </p>
*
* @param need 是否需要格式化
* @param sqlStr SQL语句部分
* @param params 参数集
* @return sql
*/
protected final String formatSqlIfNeed(boolean need, String sqlStr, Object... params) {
if (!need || StringUtils.isBlank(sqlStr)) {
return null;
}
if (ArrayUtils.isNotEmpty(params)) {
for (int i = 0; i < params.length; ++i) {
String genParamName = Constants.WRAPPER_PARAM + paramNameSeq.incrementAndGet();
sqlStr = sqlStr.replace(String.format("{%s}", i),
String.format(Constants.WRAPPER_PARAM_FORMAT, Constants.WRAPPER, genParamName));
paramNameValuePairs.put(genParamName, params[i]);
}
}
return sqlStr;
}
/**
* 获取in表达式 包含括号
*
* @param value 集合
*/
private ISqlSegment inExpression(Collection<?> value) {
return () -> value.stream().map(i -> formatSql("{0}", i))
.collect(joining(StringPool.COMMA, StringPool.LEFT_BRACKET, StringPool.RIGHT_BRACKET));
}
/**
* 必要的初始化
*/
protected void initNeed() {
paramNameSeq = new AtomicInteger(0);
paramNameValuePairs = new HashMap<>(16);
expression = new MergeSegments();
lastSql = SharedString.emptyString();
sqlComment = SharedString.emptyString();
sqlFirst = SharedString.emptyString();
}
@Override
public void clear() {
entity = null;
paramNameSeq.set(0);
paramNameValuePairs.clear();
expression.clear();
lastSql.toEmpty();
sqlComment.toEmpty();
sqlFirst.toEmpty();
}
/**
* 对sql片段进行组装
*
* @param condition 是否执行
* @param sqlSegments sql片段数组
* @return children
*/
protected Children doIt(boolean condition, ISqlSegment... sqlSegments) {
if (condition) {
expression.add(sqlSegments);
}
return typedThis;
}
@Override
public String getSqlSegment() {
return expression.getSqlSegment() + lastSql.getStringValue();
}
@Override
public String getSqlComment() {
if (StringUtils.isNotBlank(sqlComment.getStringValue())) {
return "/*" + StringEscape.escapeRawString(sqlComment.getStringValue()) + "*/";
}
return null;
}
@Override
public String getSqlFirst() {
if (StringUtils.isNotBlank(sqlFirst.getStringValue())) {
return StringEscape.escapeRawString(sqlFirst.getStringValue());
}
return null;
}
@Override
public MergeSegments getExpression() {
return expression;
}
public Map<String, Object> getParamNameValuePairs() {
return paramNameValuePairs;
}
/**
* 获取 columnName
*/
protected <X> String columnToString(X column) {
return (String) column;
}
/**
* 多字段转换为逗号 "," 分割字符串
*
* @param columns 多字段
*/
protected <X> String columnsToString(X... columns) {
return Arrays.stream(columns).map(this::columnToString).collect(joining(StringPool.COMMA));
}
@Override
@SuppressWarnings("all")
public Children clone() {
return SerializationUtils.clone(typedThis);
}
}

View File

@ -0,0 +1,130 @@
package com.github.yulichang.common;
import com.baomidou.mybatisplus.core.conditions.SharedString;
import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 自定义连表sql
* <p>
* 不使用表别名:
* <pre>
* //注解
* @Select("select user.*,user_address.tel from user left join user_address on user.id = user_address.user_id ${ew.customSqlSegment}")
*
* //或者xml
* <select id="userLeftJoin" resultType="UserDTO">
* select user.*, user_address.tel
* from user left join user_address on user.id = user_address.user_id
* ${ew.customSqlSegment}
* </select>
*
* //mapper
* UserDTO userLeftJoin(@Param(Constants.WRAPPER) Wrapper<UserDO> queryWrapper);
*
* wrapper使用方法:
* UserDTO userDTO = userMapper.userLeftJoin(new JoinLambdaWrapper<UserDO>()
* .eq(UserDO::getId, "1")
* .eq(UserAddressDO::getUserId, "1"));
*
* 对应生成sql:
* select user.*, user_address.tel
* from user left join user_address on user.id = user_address.user_id
* WHERE (
* user.id = ?
* AND user_address.user_id = ?)
* </pre>
* <p>
* 使用别名:
*
* <pre>
* //注解
* @Select("select u.*,ua.tel from user u left join user_address ua on u.id = ua.user_id ${ew.customSqlSegment}")
*
* //或者xml
* <select id="userLeftJoin" resultType="UserDTO">
* select u.*, ua.tel
* from user u left join user_address ua on u.id = ua.user_id
* ${ew.customSqlSegment}
* </select>
*
* //mapper
* UserDTO userLeftJoin(@Param(Constants.WRAPPER) Wrapper<UserDO> queryWrapper);
*
* wrapper使用方法:
* UserDTO userDTO = userMapper.userLeftJoin(new JoinLambdaWrapper<UserDO>()
* .alias(UserDO.class, "u") //如果sql使用别名,需要再此定义别名
* .alias(UserAddressDO.class, "ua") //如果sql使用别名,需要再此定义别名
* .eq(UserDO::getId, "1")
* .eq(UserAddressDO::getUserId, "1"));
*
* 对应生成sql:
* select u.*, ua.tel
* from user u left join user_address ua on u.id = ua.user_id
* WHERE (
* u.id = ?
* AND ua.user_id = ?)
* </pre>
* <p>
* 如需单独使用只需拷贝以下类
* {@link com.github.yulichang.common.JoinLambdaWrapper}
* {@link com.github.yulichang.common.JoinAbstractWrapper}
* {@link com.github.yulichang.common.JoinAbstractLambdaWrapper}
* {@link com.github.yulichang.wrapper.interfaces.Compare}
* {@link com.github.yulichang.wrapper.interfaces.Func}
* <p>
*
* @author yulichang
* @since 1.0.9
*/
public class JoinLambdaWrapper<T> extends JoinAbstractLambdaWrapper<T, JoinLambdaWrapper<T>> {
/**
* 实体与别名对应关系
*/
public JoinLambdaWrapper<T> alias(Class<?> clazz, String alisa) {
subTable.put(clazz, alisa);
return typedThis;
}
public JoinLambdaWrapper() {
super.initNeed();
}
/**
* 不建议直接 new 该实例使用 Wrappers.lambdaQuery(...)
*/
JoinLambdaWrapper(T entity, Class<T> entityClass, SharedString sqlSelect, AtomicInteger paramNameSeq,
Map<String, Object> paramNameValuePairs, MergeSegments mergeSegments,
SharedString lastSql, SharedString sqlComment, SharedString sqlFirst) {
super.setEntity(entity);
super.setEntityClass(entityClass);
this.paramNameSeq = paramNameSeq;
this.paramNameValuePairs = paramNameValuePairs;
this.expression = mergeSegments;
this.lastSql = lastSql;
this.sqlComment = sqlComment;
this.sqlFirst = sqlFirst;
}
/**
* 用于生成嵌套 sql
* <p> sqlSelect 不向下传递</p>
*/
@Override
protected JoinLambdaWrapper<T> instance() {
return new JoinLambdaWrapper<>(getEntity(), getEntityClass(), null, paramNameSeq, paramNameValuePairs,
new MergeSegments(), SharedString.emptyString(), SharedString.emptyString(), SharedString.emptyString());
}
@Override
public void clear() {
super.clear();
}
}

View File

@ -0,0 +1,122 @@
## 连表通用wrapper
### 使用方法
#### 不使用表别名
注解:
```java
@Mapper
public interface UserMapper extends BaseMapper<UserDO> {
@Select("select user.*,user_address.tel from user left join user_address on user.id = user_address.user_id ${ew.customSqlSegment}")
UserDTO userLeftJoin(@Param(Constants.WRAPPER) Wrapper<?> queryWrapper);
}
```
或者xml
```
<select id="userLeftJoin" resultType="UserDTO">
select
user.*,
user_address.tel
from
user
left join user_address on user.id = user_address.user_id
${ew.customSqlSegment}
</select>
```
使用wrapper:
```java
class MpJoinTest {
@Resource
private UserMapper userMapper;
@Test
void test() {
UserDTO userDTO = userMapper.userLeftJoin(new JoinLambdaWrapper<>()
.eq(UserDO::getId, "1")
.eq(UserAddressDO::getUserId, "1"));
}
}
```
对应sql:
```
select
user.*,
user_address.tel
from
user
left join user_address on user.id = user_address.user_id
WHERE (
user.id = ?
AND user_address.user_id = ?)
```
#### 使用表别名
注解:
```java
@Mapper
public interface UserMapper extends BaseMapper<UserDO> {
@Select("select u.*,ua.tel from user u left join user_address ua on u.id = ua.user_id ${ew.customSqlSegment}")
UserDTO userLeftJoin(@Param(Constants.WRAPPER) Wrapper<?> queryWrapper);
}
```
或者xml
```
<select id="userLeftJoin" resultType="UserDTO">
select
u.*,
ua.tel
from
user u
left join user_address ua on u.id = ua.user_id
${ew.customSqlSegment}
</select>
```
使用wrapper:
```java
class MpJoinTest {
@Resource
private UserMapper userMapper;
@Test
void test() {
UserDTO userDTO = userMapper.userLeftJoin(new JoinLambdaWrapper<>()
.alias(UserDO.class, "u") //如果使用别名需要再此声明别名与实体的对应关系
.alias(UserAddressDO.class, "ua") //如果使用别名需要再此声明别名与实体的对应关系
.eq(UserDO::getId, "1")
.eq(UserAddressDO::getUserId, "1"));
}
}
```
对应sql:
```
select
u.*,
ua.tel
from
user u
left join user_address ua on u.id = ua.user_id
WHERE (
u.id = ?
AND ua.user_id = ?)
```

View File

@ -6,7 +6,6 @@ import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda;
import org.apache.ibatis.reflection.property.PropertyNamer;
import java.lang.reflect.Field;
import java.util.Objects;
/**
@ -29,13 +28,11 @@ public final class LambdaUtils {
* 优先获取tableField中的值
*/
public static <T> String getColumn(SFunction<T, ?> fn) {
SerializedLambda lambda = com.baomidou.mybatisplus.core.toolkit.LambdaUtils.resolve(fn);
String fieldName = PropertyNamer.methodToProperty(lambda.getImplMethodName());
try {
Field field = lambda.getImplClass().getDeclaredField(fieldName);
TableField annotation = field.getAnnotation(TableField.class);
if (Objects.nonNull(annotation)) {
TableField annotation = lambda.getImplClass().getDeclaredField(fieldName).getAnnotation(TableField.class);
if (Objects.nonNull(annotation) && StringUtils.isNotBlank(annotation.value())) {
return annotation.value();
}
} catch (NoSuchFieldException ignored) {