在我们日常开发中,使用 MyBatis-Plus 写 SQL 执行的时候,难免会用到表字段,虽然 MyBatis-Plus 提供了 LambdaQueryWrapper 帮助我们使用 Lambda 方式调用对象属性名,但有的时候还是不免用到魔法值,当对象的属性名更改了之后,我们难免会漏掉,引发生产事故
1、定义支持序列化的 Function
import java.io.Serializable;
import java.util.function.Function;
/**
* 支持序列化的 Function
*
* @author miemie
* @since 2018-05-12
*/
@FunctionalInterface
public interface SFunction<T, R> extends Function<T, R>, Serializable {
}
这是 MyBatis-Plus 提供的函数,调用方式也非常简单,例如我们有一个实体对象 UserDO
public class UserDO {
private Long id;
private String name;
private Integer age;
private String phone;
}
调用方式为:
SFunction<UserDO, Long> func = UserDO::getId;
2、获取属性名
例如上面我们的调用方式 UserDO::getId 希望能够获得 “id” 字符串,下面我们使用 MyBatis-Plus 提供的工具类获得该属性名
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import org.apache.ibatis.reflection.property.PropertyNamer;
import java.beans.Introspector;
/**
* 获取属性名工具类
*
* @author asurplus
*/
public class FieldUtils {
/**
* 获取Java属性名
*
* @return userName
*/
public static <T, R> String getFieldName(SFunction<T, R> func) {
// 获取 lambda 表达式实现方法的名称
String fieldName = LambdaUtils.extract(func).getImplMethodName();
// 去掉前缀:get,is
fieldName = PropertyNamer.methodToProperty(fieldName);
// 首字母小写
return Introspector.decapitalize(fieldName);
}
}
调用方式:
public static void main(String[] args) {
String fieldName = FieldUtils.getFieldName(UserDO::getId);
System.out.println(fieldName);
}
输出:
3、获取数据库字段名
上面我们已经获取到了属性名,那么字段名也就是驼峰转下划线的操作
/**
* 获取数据库字段名
*
* @return user_name
*/
public static <T, R> String getColumnName(SFunction<T, R> func) {
String fieldName = getFieldName(func);
// 转下划线
return StrUtil.toUnderlineCase(fieldName);
}
这里的 StrUtil.toUnderlineCase() 是使用的 hutool 工具包中的工具类来实现的
调用方式:
public static void main(String[] args) {
String fieldName = FieldUtils.getColumnName(UserDO::getUserName);
System.out.println(fieldName);
}
输出:
4、底层调用
上面我们是借助 MyBatis-Plus 的 LambdaUtils 工具类获取的对象属性名,那么我们没有引入 MyBatis-Plus 该如何获取呢?
/**
* 获取Java属性名
*
* @return userName
*/
public static <T, R> String getFieldName(SFunction<T, R> func) {
try {
Method method = func.getClass().getDeclaredMethod("writeReplace");
method.setAccessible(true);
SerializedLambda serializedLambda = (SerializedLambda) method.invoke(func);
String fieldName = serializedLambda.getImplMethodName();
if (fieldName.startsWith("get")) {
fieldName = fieldName.substring(3);
}
if (fieldName.startsWith("is")) {
fieldName = fieldName.substring(2);
}
// 首字母小写
return fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
MyBatis-Plus 的 LambdaUtils 同样是这样做的底层调用,这样我们就能使用 Lambda 表达式的方式获取对象属性名,不用写入魔法值,当属性名更改了之后,idea 开发工具会提醒我们,也能及时发现修改
如您在阅读中发现不足,欢迎留言!!!