编写实体类工具类,通过表名注解的值获取实体类以及主键字段

场景:执行动态sql,只知道表名,需要通过动态表名获取动态实体类以及动态主键字段

如下图所示,需要通过①获取到②和③

 

思路:项目启动的时候,清空相关redis缓存。扫描到所有实体类,通过反射获取到表名、实体类、主键等信息,将其存入redis方便后续获取。

 

工具类代码如下

import com.cenboomh.basicframework.utils.json.JacksonUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * 实体类工具类
 * 通过表名获取实体类和主键
 *
 * @author xiaomaju
 */
@Slf4j
@Component
public class MqmEntityUtils implements ApplicationRunner {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    private static final String REDIS_ENTITY_KEY_PREFIX = "mqm:entity:";
    private static final String REDIS_TABLE_CLASS_KEY = REDIS_ENTITY_KEY_PREFIX + "table:class:map";
    private static final String REDIS_TABLE_PK_KEY = REDIS_ENTITY_KEY_PREFIX + "table:pk:map";
    private static final String CLASS_PATTERN = "classpath*:com/xmj/**/*Entity.class";

    @Override
    public void run(ApplicationArguments args) {
        refreshCache();
    }

    /**
     * 初始化缓存
     */
    private void initializeCache() {
        try {
            // 先删除旧的缓存
            Set<String> keys = stringRedisTemplate.keys(REDIS_ENTITY_KEY_PREFIX + "*");
            if (keys != null && !keys.isEmpty()) {
                stringRedisTemplate.delete(keys);
                log.info("已删除旧的实体类缓存");
            }

            Map<String, String> tableClassMap = new HashMap<>();
            Map<String, String> tablePkMap = new HashMap<>();

            PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            CachingMetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver);
            
            Resource[] resources = resolver.getResources(CLASS_PATTERN);
            for (Resource resource : resources) {
                MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
                String className = metadataReader.getClassMetadata().getClassName();
                Class<?> clazz = Class.forName(className);
                
                Table tableAnnotation = clazz.getAnnotation(Table.class);
                if (tableAnnotation != null) {
                    String tableName = tableAnnotation.name().toUpperCase();
                    tableClassMap.put(tableName, className);
                    
                    // 查找主键字段
                    java.lang.reflect.Field[] fields = clazz.getDeclaredFields();
                    for (java.lang.reflect.Field field : fields) {
                        if (field.isAnnotationPresent(Id.class)) {
                            Column column = field.getAnnotation(Column.class);
                            if (column != null) {
                                tablePkMap.put(tableName, column.name());
                            }
                            break;
                        }
                    }
                }
            }

            // 将映射信息存入Redis(永不过期)
            stringRedisTemplate.opsForValue().set(
                REDIS_TABLE_CLASS_KEY, 
                JacksonUtil.writeValueAsString(tableClassMap)
            );

            stringRedisTemplate.opsForValue().set(
                REDIS_TABLE_PK_KEY,
                JacksonUtil.writeValueAsString(tablePkMap)
            );

            log.info("实体类缓存初始化完成,共缓存{}个实体类", tableClassMap.size());
        } catch (Exception e) {
            log.error("初始化实体类缓存失败", e);
        }
    }

    /**
     * 根据表名获取实体类
     *
     * @param tableName 表名
     * @return 实体类
     */
    public Class<?> getEntityByTableName(String tableName) {
        if (StringUtils.isBlank(tableName)) {
            return null;
        }
        try {
            String tableClassMapJson = stringRedisTemplate.opsForValue().get(REDIS_TABLE_CLASS_KEY);
            if (StringUtils.isNotBlank(tableClassMapJson)) {
                Map<String, String> tableClassMap = JacksonUtil.readValue(tableClassMapJson, Map.class);
                String className = tableClassMap.get(tableName.toUpperCase());
                if (StringUtils.isNotBlank(className)) {
                    return Class.forName(className);
                }
            }
        } catch (Exception e) {
            log.error("获取实体类失败, tableName: {}", tableName, e);
        }
        return null;
    }

    /**
     * 根据表名获取主键字段名
     *
     * @param tableName 表名
     * @return 主键字段名
     */
    public String getPrimaryKeyColumn(String tableName) {
        if (StringUtils.isBlank(tableName)) {
            return null;
        }
        try {
            String tablePkMapJson = stringRedisTemplate.opsForValue().get(REDIS_TABLE_PK_KEY);
            if (StringUtils.isNotBlank(tablePkMapJson)) {
                Map<String, String> tablePkMap = JacksonUtil.readValue(tablePkMapJson, Map.class);
                return tablePkMap.get(tableName.toUpperCase());
            }
        } catch (Exception e) {
            log.error("获取主键字段失败, tableName: {}", tableName, e);
        }
        return null;
    }

    /**
     * 刷新缓存
     */
    public void refreshCache() {
        initializeCache();
    }
}

 

启动本地服务,接口测试效果如下

 

posted @ 2025-03-07 09:36  请叫我小马驹  阅读(90)  评论(0)    收藏  举报