diff --git a/pom.xml b/pom.xml index 904c61f..b60c6b3 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 com.github.yulichang mybatis-plus-join - 1.0.8 + 1.0.9 mybatis-plus-join An enhanced toolkit of Mybatis-Plus to simplify development. https://github.com/yulichang/mybatis-plus-join diff --git a/src/main/java/com/github/yulichang/common/JoinAbstractLambdaWrapper.java b/src/main/java/com/github/yulichang/common/JoinAbstractLambdaWrapper.java new file mode 100644 index 0000000..20f8630 --- /dev/null +++ b/src/main/java/com/github/yulichang/common/JoinAbstractLambdaWrapper.java @@ -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> + extends JoinAbstractWrapper { + + private Map columnMap = null; + private boolean initColumnMap = false; + + protected final Map, String> subTable = new HashMap<>(); + + @SuppressWarnings("unchecked") + protected String columnsToString(SFunction... columns) { + return columnsToString(true, columns); + } + + @Override + protected String columnToString(X column) { + return columnToString((SFunction) column, true); + } + + @SuppressWarnings("unchecked") + protected String columnsToString(boolean onlyColumn, SFunction... columns) { + return Arrays.stream(columns).map(i -> columnToString(i, onlyColumn)).collect(joining(StringPool.COMMA)); + } + + @Override + protected 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(); + } + +} diff --git a/src/main/java/com/github/yulichang/common/JoinAbstractWrapper.java b/src/main/java/com/github/yulichang/common/JoinAbstractWrapper.java new file mode 100644 index 0000000..1ec4b3b --- /dev/null +++ b/src/main/java/com/github/yulichang/common/JoinAbstractWrapper.java @@ -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> extends Wrapper + implements Compare, Nested, Join, Func { + + /** + * 占位符 + */ + protected final Children typedThis = (Children) this; + /** + * 必要度量 + */ + protected AtomicInteger paramNameSeq; + protected Map paramNameValuePairs; + protected SharedString lastSql; + /** + * SQL注释 + */ + protected SharedString sqlComment; + /** + * SQL起始语句 + */ + protected SharedString sqlFirst; + /** + * ß + * 数据库表映射实体类 + */ + private T entity; + protected MergeSegments expression; + /** + * 实体类型(主要用于确定泛型以及取TableInfo缓存) + */ + private Class entityClass; + + @Override + public T getEntity() { + return entity; + } + + public Children setEntity(T entity) { + this.entity = entity; + return typedThis; + } + + public Class getEntityClass() { + if (entityClass == null && entity != null) { + entityClass = (Class) entity.getClass(); + } + return entityClass; + } + + public Children setEntityClass(Class entityClass) { + if (entityClass != null) { + this.entityClass = entityClass; + } + return typedThis; + } + + @Override + public Children allEq(boolean condition, Map, 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 Children allEq(boolean condition, BiPredicate, V> filter, Map, 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 Children eq(boolean condition, SFunction column, Object val) { + return addCondition(condition, column, EQ, val); + } + + @Override + public Children ne(boolean condition, SFunction column, Object val) { + return addCondition(condition, column, NE, val); + } + + @Override + public Children gt(boolean condition, SFunction column, Object val) { + return addCondition(condition, column, GT, val); + } + + @Override + public Children ge(boolean condition, SFunction column, Object val) { + return addCondition(condition, column, GE, val); + } + + @Override + public Children lt(boolean condition, SFunction column, Object val) { + return addCondition(condition, column, LT, val); + } + + @Override + public Children le(boolean condition, SFunction column, Object val) { + return addCondition(condition, column, LE, val); + } + + @Override + public Children like(boolean condition, SFunction column, Object val) { + return likeValue(condition, LIKE, column, val, SqlLike.DEFAULT); + } + + @Override + public Children notLike(boolean condition, SFunction column, Object val) { + return likeValue(condition, NOT_LIKE, column, val, SqlLike.DEFAULT); + } + + @Override + public Children likeLeft(boolean condition, SFunction column, Object val) { + return likeValue(condition, LIKE, column, val, SqlLike.LEFT); + } + + @Override + public Children likeRight(boolean condition, SFunction column, Object val) { + return likeValue(condition, LIKE, column, val, SqlLike.RIGHT); + } + + @Override + public Children between(boolean condition, SFunction column, Object val1, Object val2) { + return doIt(condition, () -> columnToString(column), BETWEEN, () -> formatSql("{0}", val1), AND, + () -> formatSql("{0}", val2)); + } + + @Override + public Children notBetween(boolean condition, SFunction 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 consumer) { + return and(condition).addNestedCondition(condition, consumer); + } + + @Override + public Children or(boolean condition, Consumer consumer) { + return or(condition).addNestedCondition(condition, consumer); + } + + @Override + public Children nested(boolean condition, Consumer consumer) { + return addNestedCondition(condition, consumer); + } + + @Override + public Children not(boolean condition, Consumer 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 Children isNull(boolean condition, SFunction column) { + return doIt(condition, () -> columnToString(column), IS_NULL); + } + + @Override + public Children isNotNull(boolean condition, SFunction column) { + return doIt(condition, () -> columnToString(column), IS_NOT_NULL); + } + + @Override + public Children in(boolean condition, SFunction column, Collection coll) { + return doIt(condition, () -> columnToString(column), IN, inExpression(coll)); + } + + @Override + public Children notIn(boolean condition, SFunction column, Collection coll) { + return doIt(condition, () -> columnToString(column), NOT_IN, inExpression(coll)); + } + + @Override + public Children inSql(boolean condition, SFunction column, String inValue) { + return doIt(condition, () -> columnToString(column), IN, () -> String.format("(%s)", inValue)); + } + + @Override + public Children notInSql(boolean condition, SFunction column, String inValue) { + return doIt(condition, () -> columnToString(column), NOT_IN, () -> String.format("(%s)", inValue)); + } + + @Override + public Children groupBy(boolean condition, SFunction... columns) { + if (ArrayUtils.isEmpty(columns)) { + return typedThis; + } + return doIt(condition, GROUP_BY, + () -> columns.length == 1 ? columnToString(columns[0]) : columnsToString(columns)); + } + + @Override + public Children orderBy(boolean condition, boolean isAsc, SFunction... columns) { + if (ArrayUtils.isEmpty(columns)) { + return typedThis; + } + SqlKeyword mode = isAsc ? ASC : DESC; + for (SFunction 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 consumer) { + if (condition) { + consumer.accept(typedThis); + } + return typedThis; + } + + /** + * 内部自用 + *

NOT 关键词

+ */ + protected Children not(boolean condition) { + return doIt(condition, NOT); + } + + /** + * 内部自用 + *

拼接 AND

+ */ + protected Children and(boolean condition) { + return doIt(condition, AND); + } + + /** + * 内部自用 + *

拼接 LIKE 以及 值

+ */ + protected Children likeValue(boolean condition, SqlKeyword keyword, SFunction 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 Children addCondition(boolean condition, SFunction column, SqlKeyword sqlKeyword, Object val) { + return doIt(condition, () -> columnToString(column), sqlKeyword, () -> formatSql("{0}", val)); + } + + /** + * 多重嵌套查询条件 + * + * @param condition 查询条件值 + */ + protected Children addNestedCondition(boolean condition, Consumer 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); + } + + /** + *

+ * 根据需要格式化SQL
+ *
+ * Format SQL for methods: EntityQ.where/and/or...("name={0}", value); + * ALL the {i} will be replaced with #{MPGENVALi}
+ *
+ * ew.where("sample_name={0}", "haha").and("sample_age >{0} + * and sample_age<{1}", 18, 30) TO + * sample_name=#{MPGENVAL1} and sample_age>#{MPGENVAL2} and + * sample_age<#{MPGENVAL3}
+ *

+ * + * @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 getParamNameValuePairs() { + return paramNameValuePairs; + } + + /** + * 获取 columnName + */ + protected String columnToString(X column) { + return (String) column; + } + + /** + * 多字段转换为逗号 "," 分割字符串 + * + * @param columns 多字段 + */ + protected 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); + } +} diff --git a/src/main/java/com/github/yulichang/common/JoinLambdaWrapper.java b/src/main/java/com/github/yulichang/common/JoinLambdaWrapper.java new file mode 100644 index 0000000..924eb04 --- /dev/null +++ b/src/main/java/com/github/yulichang/common/JoinLambdaWrapper.java @@ -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 + *

+ * 不使用表别名: + *

+ *     //注解
+ *     @Select("select user.*,user_address.tel from user left join user_address on user.id = user_address.user_id ${ew.customSqlSegment}")
+ *
+ *     //或者xml
+ *     
+ *
+ *     //mapper
+ *     UserDTO userLeftJoin(@Param(Constants.WRAPPER) Wrapper queryWrapper);
+ *
+ *     wrapper使用方法:
+ *     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 = ?)
+ * 
+ *

+ * 使用别名: + * + *

+ *     //注解
+ *     @Select("select u.*,ua.tel from user u left join user_address ua on u.id = ua.user_id ${ew.customSqlSegment}")
+ *
+ *     //或者xml
+ *     
+ *
+ *     //mapper
+ *     UserDTO userLeftJoin(@Param(Constants.WRAPPER) Wrapper queryWrapper);
+ *
+ *     wrapper使用方法:
+ *     UserDTO userDTO = userMapper.userLeftJoin(new JoinLambdaWrapper()
+ *                 .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 = ?)
+ * 
+ *

+ * 如需单独使用只需拷贝以下类 + * {@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} + *

+ * + * @author yulichang + * @since 1.0.9 + */ +public class JoinLambdaWrapper extends JoinAbstractLambdaWrapper> { + + /** + * 实体与别名对应关系 + */ + public JoinLambdaWrapper alias(Class clazz, String alisa) { + subTable.put(clazz, alisa); + return typedThis; + } + + + public JoinLambdaWrapper() { + super.initNeed(); + } + + /** + * 不建议直接 new 该实例,使用 Wrappers.lambdaQuery(...) + */ + JoinLambdaWrapper(T entity, Class entityClass, SharedString sqlSelect, AtomicInteger paramNameSeq, + Map 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 + *

故 sqlSelect 不向下传递

+ */ + @Override + protected JoinLambdaWrapper instance() { + return new JoinLambdaWrapper<>(getEntity(), getEntityClass(), null, paramNameSeq, paramNameValuePairs, + new MergeSegments(), SharedString.emptyString(), SharedString.emptyString(), SharedString.emptyString()); + } + + @Override + public void clear() { + super.clear(); + } +} + diff --git a/src/main/java/com/github/yulichang/common/README.md b/src/main/java/com/github/yulichang/common/README.md new file mode 100644 index 0000000..c86e8f1 --- /dev/null +++ b/src/main/java/com/github/yulichang/common/README.md @@ -0,0 +1,122 @@ +## 连表通用wrapper + +### 使用方法 + +#### 不使用表别名 + +注解: + +```java + +@Mapper +public interface UserMapper extends BaseMapper { + + @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 + +``` + + +``` + +使用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 { + + @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 + +``` + +``` + +使用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 = ?) +``` \ No newline at end of file diff --git a/src/main/java/com/github/yulichang/toolkit/LambdaUtils.java b/src/main/java/com/github/yulichang/toolkit/LambdaUtils.java index e806fff..caffc5a 100644 --- a/src/main/java/com/github/yulichang/toolkit/LambdaUtils.java +++ b/src/main/java/com/github/yulichang/toolkit/LambdaUtils.java @@ -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 String getColumn(SFunction 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) {