Typography

一只奶牛猫

Java实体类通用字段处理最佳实践 - SkcEntityUtils工具类详解

发布于 # java

优雅处理实体类的通用字段 - SkcEntityUtils工具类详解

在企业级应用开发中,我们经常需要处理实体类的一些通用字段,比如创建人、创建时间、更新人、更新时间以及租户相关信息等。这些字段的处理往往是重复且机械的工作。今天要介绍的SkcEntityUtils工具类就是为了解决这个问题而设计的,它使用反射和函数式编程的特性,优雅地处理了这些通用字段的赋值问题。

1. 工具类的主要功能

SkcEntityUtils主要提供了三类字段的自动填充功能:

  1. 创建相关字段(createId, createName, createTime)
  2. 更新相关字段(updateId, updateName, updateTime)
  3. 租户相关字段(tenantsid, tenantName, tenantId)

2. 核心设计

2.1 字段映射配置

工具类使用了三个不可变Map来存储字段名称和对应的值提供者(Supplier):

private static final Map<String, Supplier<?>> createFieldSuppliers;
private static final Map<String, Supplier<?>> updateFieldSuppliers;
private static final Map<String, Supplier<?>> tenantFieldSuppliers;

每个Map在静态初始化块中被初始化,并使用Collections.unmodifiableMap确保线程安全性。

2.2 函数式编程应用

工具类巧妙地使用了Java 8的Supplier函数式接口,为每个字段提供值的获取方式:

createMap.put("createId", KaiAuthTokenUtil::getUserId);
createMap.put("createTime", Date::new);

这种设计的优势在于:

2.3 反射机制的应用

工具类通过Java反射机制动态设置字段值:

Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
if (field.get(entity) == null) {
    field.set(entity, valueSupplier.get());
}

这样的设计允许我们:

3. 使用示例

// 创建实体对象
UserEntity user = new UserEntity();

// 设置创建相关字段
SkcEntityUtils.setCreateFields(user);

// 设置更新相关字段
SkcEntityUtils.setUpdateFields(user);

// 设置租户相关字段
SkcEntityUtils.setTenantFields(user);

4. 最佳实践建议

  1. 统一字段命名规范:确保实体类中的字段名称与工具类中定义的一致
  2. 异常处理:根据业务需求适当处理字段不存在的情况
  3. 权限控制:确保KaiAuthTokenUtil在获取用户信息时有正确的权限设置
  4. 性能考虑:由于使用反射,在高并发场景下需要注意性能影响

5. 扩展性

这个工具类的设计非常容易扩展:

  1. 添加新的字段映射:只需在相应的Map中添加新的键值对
  2. 添加新的字段类型:得益于泛型的使用,支持任意类型的字段
  3. 添加新的功能:可以参考现有模式添加新的字段组

6. 总结

SkcEntityUtils是一个设计优雅的工具类,它:

附上代码

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.util.*;
import java.util.function.Supplier;

@Slf4j
public class SkcEntityUtils {

    private static final Map<String, Supplier<?>> createFieldSuppliers;
    private static final Map<String, Supplier<?>> updateFieldSuppliers;
    private static final Map<String, Supplier<?>> tenantFieldSuppliers;

    static {
        Map<String, Supplier<?>> createMap = new HashMap<>();
        createMap.put("createId", KaiAuthTokenUtil::getUserId);
        createMap.put("createName", KaiAuthTokenUtil::getUserName);
        createMap.put("createTime", Date::new);
        createFieldSuppliers = Collections.unmodifiableMap(createMap);

        Map<String, Supplier<?>> updateMap = new HashMap<>();
        updateMap.put("updateId", KaiAuthTokenUtil::getUserId);
        updateMap.put("updateName", KaiAuthTokenUtil::getUserName);
        updateMap.put("updateTime", Date::new);
        updateFieldSuppliers = Collections.unmodifiableMap(updateMap);

        Map<String, Supplier<?>> tenantMap = new HashMap<>();
        tenantMap.put("tenantsid", KaiAuthTokenUtil::getTenantSid);
        tenantMap.put("tenantName", KaiAuthTokenUtil::getTenantName);
        tenantMap.put("tenantId", KaiAuthTokenUtil::getTenantId);
        tenantFieldSuppliers = Collections.unmodifiableMap(tenantMap);
    }

    public static void setCreateFields(Object entity) {
        setFields(entity, createFieldSuppliers);
    }

    public static void setUpdateFields(Object entity) {
        setFields(entity, updateFieldSuppliers);
    }

    public static void setTenantFields(Object entity) {
        setFields(entity, tenantFieldSuppliers);
    }

    private static void setFields(Object entity, Map<String, Supplier<?>> fieldSuppliers) {
        if (entity == null) {
            return;
        }

        Class<?> clazz = entity.getClass();
        fieldSuppliers.forEach((fieldName, valueSupplier) -> {
            try {
                Field field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
                if (field.get(entity) == null) {
                    field.set(entity, valueSupplier.get());
                }
            } catch (NoSuchFieldException | IllegalAccessException e) {
                // 如果字段不存在或无法访问,我们可以选择忽略它
                // 或者记录日志,取决于您的需求
                log.error("no such field {}", fieldName);
            }
        });
    }
}

在实际开发中,这样的工具类可以大大提高开发效率,特别是在处理包含大量通用字段的实体类时。