动态获取实体类字段名

951 阅读1分钟

一、问题场景:

在使用MyBatisPlus操作MySql数据库时,对于查找的字段名可以以类似下面的写法:

queryWrapper.eq(Dict::getId, 1);

这样写的好处就在于一来不会写错,而用字符串形式的属性名容易写错,二来如果修改属性名或者删除属性后,也能在编译阶段就发现问题而不是把问题留到运行阶段。

但是在用MongoDBTemplate操作Mongo数据时,却不支持这种写法,经过研究,找到两种类似的写法。一种是引入lombok依赖包,通过FieldNameConstants注解的方式,另一种则是通过java反射的方式;

二、lombok方法:

1、首先引入依赖:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.34</version>
</dependency>

2、实体类上添加注解:

@FieldNameConstants
public class User {
    
    private Long userId;

    private String userName;

    private String userAddr;

    private Integer userAge;
    
    //............
    
}

3、mongo查找时使用:

Criteria criteria = Criteria.where(User.Fields.userAge).gt(30);
return mongoTemplate.find(new Query(criteria), User.class);

三、反射的方式:

1、通过反射实现获取属性名的方法:

// 拿到getter方法对应的属性名称
public static <T> String getFieldName(PropertyFunc<T, ?> field) {
    Class<?> clazz = field.getClass();
    try {
        Method method = clazz.getDeclaredMethod("writeReplace");
        method.setAccessible(true);
        SerializedLambda lambda = (SerializedLambda) method.invoke(field);
        return uncapitalize(lambda.getImplMethodName());
    } catch (Exception e) {
        if (!Object.class.isAssignableFrom(clazz.getSuperclass())) {
            return getFieldName(field);
        }
        throw new RuntimeException("属性不存在");
    }
}

// 去掉方法的“get/is”前缀,首字母小写
private static String uncapitalize(String str) {
    if (StringUtils.isBlank(str) || str.length() < 4) {
        return str;
    }
    String fieldName = str.startsWith("get") ?
            str.substring(3) : str.startsWith("is") ? str.substring(2) : str;
    return CharSequenceUtil.lowerFirst(fieldName);
}

2、使用:

Criteria criteria = Criteria.where(getFieldName(User::getUserAge)).gt(30);
return mongoTemplate.find(new Query(criteria), User.class);