基于JSF与PrimeFaces如何为各资产自动生成专属雷达统计图表
资产专属雷达图实现流程
核心问题定位
原有代码直接将全量风险数据塞入单个雷达图实例,没有按资产ID做数据拆分,且存在重复查库、循环内重复赋值的性能问题,按以下步骤改造即可实现按资产生成独立图表、新增资产自动生成图表的需求。
步骤1:将全量风险数据按资产维度分组
首先把查询到的所有风险关联数据按asset_id分组,同一个资产下的所有风险数据归为一组,注意不要在循环里反复调用数据库查询方法,提前把查询结果存入局部变量复用,避免重复连库。
// 仅查询一次数据库,拿到全量数据后复用 List<risk> allRiskData = getGet_all_risk_for_radar_chart2(); // 按资产ID分组,key为资产ID,value为该资产关联的所有风险数据 Map<Integer, List<risk>> assetRiskGroup = allRiskData.stream() .collect(Collectors.groupingBy(risk::getId));
如果项目用的JDK版本不支持stream,手动遍历allRiskData按asset_id放到Map里也可以。
步骤2:封装单资产雷达图的通用生成方法
把原来写死的单图表生成逻辑,改造成支持传入资产ID、对应风险数据列表,返回专属RadarChartModel的通用方法,不再写死单个图表实例。
/** * 生成单个资产的专属雷达图 * @param assetId 资产ID * @param assetRisks 该资产关联的所有风险数据 * @return 组装完成的雷达图模型 */ private RadarChartModel buildSingleAssetRadar(Integer assetId, List<risk> assetRisks) { RadarChartModel model = new RadarChartModel(); ChartData chartData = new ChartData(); // 初始化计算风险值数据集 RadarChartDataSet riskSet = new RadarChartDataSet(); riskSet.setLabel("计算风险值"); riskSet.setFill(true); riskSet.setBackgroundColor("rgba(255, 99, 132, 0.2)"); riskSet.setBorderColor("rgb(255, 99, 132)"); riskSet.setPointBackgroundColor("rgb(255, 99, 132)"); riskSet.setPointBorderColor("#fff"); riskSet.setPointHoverBackgroundColor("#fff"); riskSet.setPointHoverBorderColor("rgb(255, 99, 132)"); List<Number> riskValues = new ArrayList<>(); // 初始化风险缓解率数据集 RadarChartDataSet mitigationSet = new RadarChartDataSet(); mitigationSet.setLabel("风险缓解率"); mitigationSet.setFill(true); mitigationSet.setBackgroundColor("rgba(54, 162, 235, 0.2)"); mitigationSet.setBorderColor("rgb(54, 162, 235)"); mitigationSet.setPointBackgroundColor("rgb(54, 162, 235)"); mitigationSet.setPointBorderColor("#fff"); mitigationSet.setPointHoverBackgroundColor("#fff"); mitigationSet.setPointHoverBorderColor("rgb(54, 162, 235)"); List<Number> mitigationValues = new ArrayList<>(); // 初始化雷达图轴标签 List<String> labels = new ArrayList<>(); // 仅遍历当前资产的风险数据填充,不再遍历全量数据 for (risk item : assetRisks) { riskValues.add(item.getCalculated_risk()); mitigationValues.add(item.getMitigation_percent()); labels.add("风险ID:" + item.getRisk_id()); // 后续可替换为风险名称字段,展示效果更好 } // 数据填充完成后统一赋值,不要在循环里反复set riskSet.setData(riskValues); mitigationSet.setData(mitigationValues); chartData.addChartDataSet(riskSet); chartData.addChartDataSet(mitigationSet); chartData.setLabels(labels); // 统一配置项 RadarChartOptions options = new RadarChartOptions(); Elements elements = new Elements(); ElementsLine lineConfig = new ElementsLine(); lineConfig.setTension(0); lineConfig.setBorderWidth(3); elements.setLine(lineConfig); options.setElements(elements); model.setOptions(options); model.setData(chartData); return model; }
步骤3:做图表缓存,支持批量生成所有资产的雷达图
在后端管理Bean中维护一个缓存Map,key为资产ID,value为对应资产的雷达图实例,避免每次请求都重新生成图表。
// 资产雷达图缓存,key=资产ID,value=对应雷达图模型 private Map<Integer, RadarChartModel> assetRadarCache = new HashMap<>(); /** * 刷新全量资产的雷达图缓存 */ public void refreshAllAssetRadar() throws Exception { assetRadarCache.clear(); List<risk> allRiskData = getGet_all_risk_for_radar_chart2(); Map<Integer, List<risk>> assetRiskGroup = allRiskData.stream() .collect(Collectors.groupingBy(risk::getId)); // 遍历每个资产,生成对应图表存入缓存 for (Map.Entry<Integer, List<risk>> entry : assetRiskGroup.entrySet()) { Integer assetId = entry.getKey(); List<risk> risks = entry.getValue(); assetRadarCache.put(assetId, buildSingleAssetRadar(assetId, risks)); } } /** * 对外提供查询方法,前端根据资产ID获取对应图表 */ public RadarChartModel getRadarByAssetId(Integer assetId) { return assetRadarCache.get(assetId); }
在Bean的@PostConstruct初始化方法中调用refreshAllAssetRadar(),页面首次加载时就会自动生成所有已有资产的雷达图。
步骤4:实现新增资产自动生成雷达图逻辑
在新增资产、保存资产与风险关联关系的业务方法执行成功、数据全部落库后,根据业务场景选择以下两种方案之一即可:
- 方案1(简单易维护,适合资产总量<1000的场景):直接调用
refreshAllAssetRadar()全量刷新缓存,不需要额外写逻辑,不会出现数据不一致问题 - 方案2(性能更好,适合资产量大的场景):单独查询刚新增资产对应的所有风险关联数据,调用
buildSingleAssetRadar()生成该资产的雷达图,直接put到assetRadarCache缓存中即可,不需要全量刷新
注意:必须等资产的风险关联关系、风险评分、缓解率数据全部写入数据库后再触发生成逻辑,否则会出现图表数据缺失的问题。
步骤5:前端页面渲染
在资产列表、资产详情页面,遍历资产数据时为每个资产单独引入PrimeFaces雷达图标签,绑定对应资产ID的图表实例即可:
<!-- 遍历资产列表 --> <ui:repeat value="#{assetBean.assetList}" var="asset"> <div class="asset-item"> <h4>资产#{asset.id} 风险雷达图</h4> <p:radarChart model="#{radarBean.getRadarByAssetId(asset.id)}" style="width: 420px;height:420px;"/> </div> </ui:repeat>
原有代码问题修正
- 移除循环内重复调用数据库查询方法的逻辑,所有数据一次查询后复用,避免不必要的数据库连接开销
- 移除循环内反复调用
setData()、setLabels()的逻辑,等数据集、标签列表全部填充完成后统一赋值即可 - 所有数据填充前先按资产ID过滤分组,禁止将不同资产的风险数据塞入同一个图表实例
内容的提问来源于stack exchange,提问作者Loran
相关产品推荐
相关产品推荐

