You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot如何实现返回columns泛型列表的GET查询接口

核心问题

你当前代码无法实现泛型返回的原因有三个:

  • 泛型仅在Controller层做了声明,Java泛型存在运行时类型擦除机制,Jackson无法获取实际反序列化目标类型,无法输出对应结构的对象
  • Service层逻辑写死了反序列化目标为AnalyticIcon类,无法适配不同table_name对应不同columns结构的需求
  • Controller层直接返回整个Config对象,没有单独解析columns字段,和接口需求不匹配
实现步骤

1. 维护表名与列结构的映射关系

服务端可以明确知道每个table_name对应的columns JSON结构,先统一维护映射关系,后续新增配置只需要加映射即可:

import java.util.Map;

// 配置表名和对应columns实体类的映射
public class ConfigTypeMapping {
    public static final Map<String, Class<?>> TABLE_TYPE_RELATION = Map.of(
        "table1", Table1ColumnConfig.class,
        "analyticIcon", AnalyticIcon.class
        // 后续新增配置表在此追加映射
    );
}

提前按照JSON结构编写对应的POJO类,比如Table1ColumnConfig对应第一条配置里的id、analyticId、propertytype、minVal、maxVal、StringVal、caseElse、result字段,AnalyticIcon对应第二条配置的字段,普通实体类加@Data注解即可。

2. 改造Service层

把写死类型的方法改成通用泛型方法,通过Jackson的JavaType解决泛型擦除问题:

Service接口

import java.util.List;

public interface ConfigService {
    // 原有已实现方法保留
    List<Config> getAllConfig();
    Config getConfigValueByTableName(String tableName);

    // 新增泛型方法
    <T> List<T> getColumnsByTableName(String tableName);
    <T> void updateColumnsByTableName(String tableName, List<T> columns);
}

ServiceImpl实现

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;

@Service
@RequiredArgsConstructor
public class ConfigServiceImpl implements ConfigService {

    private final ConfigDAO configDAO;
    private final ConfigMapper configMapper;
    // 直接注入Spring全局ObjectMapper,不要自己new,避免序列化配置不一致
    private final ObjectMapper objectMapper;

    @Override
    public List<Config> getAllConfig() {
        // 原有逻辑保留
        return null;
    }

    @Override
    public Config getConfigValueByTableName(String tableName) {
        // 原有逻辑保留
        return null;
    }

    @Override
    public <T> List<T> getColumnsByTableName(String tableName) {
        Optional<ConfigEntity> entityOpt = configDAO.findByTableName(tableName);
        if (entityOpt.isEmpty()) {
            // 查不到可以返回空列表,也可以抛自定义业务异常
            return List.of();
        }
        ConfigEntity entity = entityOpt.get();
        String columnsJson = entity.getColumns();

        // 获取当前表对应的目标实体类型
        Class<?> targetType = ConfigTypeMapping.TABLE_TYPE_RELATION.get(tableName);
        if (targetType == null) {
            throw new IllegalArgumentException("未配置表["+ tableName +"]对应的列结构类型");
        }

        try {
            // 构造泛型集合类型,解决类型擦除问题,避免返回List<LinkedHashMap>
            JavaType listType = objectMapper.getTypeFactory()
                    .constructCollectionType(List.class, targetType);
            return objectMapper.readValue(columnsJson, listType);
        } catch (Exception e) {
            throw new RuntimeException("解析表["+ tableName +"]配置失败", e);
        }
    }

    @Override
    public <T> void updateColumnsByTableName(String tableName, List<T> columns) {
        Optional<ConfigEntity> entityOpt = configDAO.findByTableName(tableName);
        if (entityOpt.isEmpty()) {
            throw new IllegalArgumentException("配置表["+ tableName +"]不存在");
        }
        ConfigEntity entity = entityOpt.get();
        try {
            String json = objectMapper.writeValueAsString(columns);
            entity.setColumns(json);
            configDAO.save(entity);
        } catch (Exception e) {
            throw new RuntimeException("更新表["+ tableName +"]配置失败", e);
        }
    }
}

3. 改造Controller层

直接调用Service层的泛型方法,返回解析后的columns列表即可:

import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;

@RestController
@RequestMapping("/config")
@RequiredArgsConstructor
public class ConfigController {

    private final ConfigService configService;

    // 原有已实现接口保留
    @GetMapping("/all")
    public ResponseEntity<List<Config>> getAll() {
        return ResponseEntity.ok(configService.getAllConfig());
    }

    @GetMapping("/detail")
    public ResponseEntity<Config> getByTableName(@RequestParam String tableName) {
        return ResponseEntity.ok(configService.getConfigValueByTableName(tableName));
    }

    // 查询columns接口
    @GetMapping("/columns")
    public ResponseEntity<?> getColumns(@RequestParam String tableName) {
        return ResponseEntity.ok(configService.getColumnsByTableName(tableName));
    }

    // 更新columns接口
    @PutMapping("/columns")
    public ResponseEntity<Void> updateColumns(
            @RequestParam String tableName,
            @RequestBody List<?> columns
    ) {
        configService.updateColumnsByTableName(tableName, columns);
        return ResponseEntity.ok().build();
    }
}
注意事项
  • 不要在业务代码中自行new ObjectMapper(),直接注入Spring Boot自动配置的全局单例ObjectMapper,保证日期格式、空值处理、命名策略等和全局配置一致
  • 编写对应POJO类时,如果JSON字段名和Java字段名不匹配,用@JsonProperty注解做映射
  • 后续新增配置表时,只需要编写对应columns结构的POJO类,再在ConfigTypeMapping中追加表名和类的映射即可,不需要修改核心业务逻辑
  • 之前写死类型的getAnalyticIconValues方法可以直接删除,通用泛型方法会统一处理所有表的解析逻辑

内容的提问来源于stack exchange,提问作者Doncarlito87

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 04:24:23