在工厂建模系统中,版本管理是一个核心需求。本文将介绍如何基于MySQL 5.7的JSON字段存储和JDK 1.8实现工厂建模数据的版本对比功能。

技术栈概述

  • MySQL 5.7+: 支持原生JSON数据类型和JSON相关函数
  • JDK 1.8: 提供Lambda表达式和Stream API,简化集合操作
  • JSON处理: 使用Jackson或Gson库进行JSON序列化/反序列化

数据库设计

工厂建模表结构

CREATE TABLE factory_model (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    model_name VARCHAR(100) NOT NULL COMMENT '模型名称',
    version INT NOT NULL DEFAULT 1 COMMENT '版本号',
    model_data JSON NOT NULL COMMENT '模型数据(JSON格式)',
    description TEXT COMMENT '版本描述',
    created_by VARCHAR(50) NOT NULL,
    created_time DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    
    UNIQUE KEY uk_model_version (model_name, version)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='工厂建模表';

模型数据JSON结构示例

{
  "factory": {
    "name": "智能工厂A",
    "type": "AUTO_ASSEMBLY",
    "area": 5000
  },
  "productionLines": [
    {
      "id": "line-001",
      "name": "组装线1",
      "machines": [
        {
          "id": "machine-001",
          "type": "ROBOT_ARM",
          "specifications": {
            "maxLoad": 50,
            "precision": 0.01
          }
        }
      ]
    }
  ],
  "layout": {
    "width": 100,
    "height": 50,
    "units": []
  }
}

核心实现代码

1. 模型实体类

public class FactoryModel {
    private Long id;
    private String modelName;
    private Integer version;
    private String modelData; // JSON字符串
    private String description;
    private String createdBy;
    private Date createdTime;
    private Date updatedTime;
    
    // getters and setters
}

2. JSON数据对应的Java对象

public class ModelData {
    private FactoryInfo factory;
    private List<ProductionLine> productionLines;
    private Layout layout;
    
    // 静态内部类定义
    public static class FactoryInfo {
        private String name;
        private String type;
        private Double area;
        // getters and setters
    }
    
    public static class ProductionLine {
        private String id;
        private String name;
        private List<Machine> machines;
        // getters and setters
    }
    
    public static class Machine {
        private String id;
        private String type;
        private Specifications specifications;
        // getters and setters
    }
    
    public static class Specifications {
        private Double maxLoad;
        private Double precision;
        // getters and setters
    }
    
    public static class Layout {
        private Integer width;
        private Integer height;
        private List<LayoutUnit> units;
        // getters and setters
    }
    
    // getters and setters
}

3. 版本对比结果对象

public class VersionComparison {
    private String modelName;
    private Integer baseVersion;
    private Integer targetVersion;
    private List<Difference> differences;
    private Date compareTime;
    
    public static class Difference {
        private String path; // JSON路径,如 "factory.name"
        private ChangeType changeType; // ADD, DELETE, MODIFY
        private Object oldValue;
        private Object newValue;
        private String description;
        
        public enum ChangeType {
            ADD, DELETE, MODIFY
        }
        
        // getters and setters
    }
    
    // getters and setters
}

4. 版本对比服务实现

@Service
public class ModelVersionService {
    
    @Autowired
    private FactoryModelMapper modelMapper;
    
    private ObjectMapper objectMapper = new ObjectMapper();
    
    /**
     * 对比两个版本的模型数据
     */
    public VersionComparison compareVersions(String modelName, Integer version1, Integer version2) {
        FactoryModel model1 = modelMapper.selectByModelAndVersion(modelName, version1);
        FactoryModel model2 = modelMapper.selectByModelAndVersion(modelName, version2);
        
        if (model1 == null || model2 == null) {
            throw new RuntimeException("指定版本的模型不存在");
        }
        
        try {
            ModelData data1 = objectMapper.readValue(model1.getModelData(), ModelData.class);
            ModelData data2 = objectMapper.readValue(model2.getModelData(), ModelData.class);
            
            return compareModelData(data1, data2, version1, version2, modelName);
        } catch (Exception e) {
            throw new RuntimeException("模型数据解析失败", e);
        }
    }
    
    /**
     * 递归对比模型数据
     */
    private VersionComparison compareModelData(ModelData data1, ModelData data2, 
                                              Integer baseVersion, Integer targetVersion, 
                                              String modelName) {
        VersionComparison comparison = new VersionComparison();
        comparison.setModelName(modelName);
        comparison.setBaseVersion(baseVersion);
        comparison.setTargetVersion(targetVersion);
        comparison.setCompareTime(new Date());
        
        List<VersionComparison.Difference> differences = new ArrayList<>();
        
        // 对比工厂基本信息
        compareFactoryInfo(data1.getFactory(), data2.getFactory(), "factory", differences);
        
        // 对比生产线
        compareProductionLines(data1.getProductionLines(), data2.getProductionLines(), 
                              "productionLines", differences);
        
        // 对比布局
        compareLayout(data1.getLayout(), data2.getLayout(), "layout", differences);
        
        comparison.setDifferences(differences);
        return comparison;
    }
    
    private void compareFactoryInfo(ModelData.FactoryInfo info1, ModelData.FactoryInfo info2,
                                   String path, List<VersionComparison.Difference> differences) {
        if (info1 == null && info2 == null) return;
        
        if (info1 == null) {
            differences.add(createDifference(path, null, info2, 
                VersionComparison.Difference.ChangeType.ADD));
            return;
        }
        
        if (info2 == null) {
            differences.add(createDifference(path, info1, null, 
                VersionComparison.Difference.ChangeType.DELETE));
            return;
        }
        
        compareValue(path + ".name", info1.getName(), info2.getName(), differences);
        compareValue(path + ".type", info1.getType(), info2.getType(), differences);
        compareValue(path + ".area", info1.getArea(), info2.getArea(), differences);
    }
    
    private void compareProductionLines(List<ModelData.ProductionLine> lines1, 
                                       List<ModelData.ProductionLine> lines2,
                                       String path, List<VersionComparison.Difference> differences) {
        if (lines1 == null) lines1 = new ArrayList<>();
        if (lines2 == null) lines2 = new ArrayList<>();
        
        // 使用ID作为标识符进行对比
        Map<String, ModelData.ProductionLine> map1 = lines1.stream()
            .collect(Collectors.toMap(ModelData.ProductionLine::getId, Function.identity()));
        Map<String, ModelData.ProductionLine> map2 = lines2.stream()
            .collect(Collectors.toMap(ModelData.ProductionLine::getId, Function.identity()));
        
        // 找出新增的生产线
        map2.keySet().stream()
            .filter(id -> !map1.containsKey(id))
            .forEach(id -> differences.add(createDifference(
                path + "[" + id + "]", null, map2.get(id), 
                VersionComparison.Difference.ChangeType.ADD)));
        
        // 找出删除的生产线
        map1.keySet().stream()
            .filter(id -> !map2.containsKey(id))
            .forEach(id -> differences.add(createDifference(
                path + "[" + id + "]", map1.get(id), null, 
                VersionComparison.Difference.ChangeType.DELETE)));
        
        // 对比共同存在的生产线
        map1.keySet().stream()
            .filter(map2::containsKey)
            .forEach(id -> compareProductionLine(
                map1.get(id), map2.get(id), path + "[" + id + "]", differences));
    }
    
    private void compareProductionLine(ModelData.ProductionLine line1, 
                                      ModelData.ProductionLine line2,
                                      String path, List<VersionComparison.Difference> differences) {
        compareValue(path + ".name", line1.getName(), line2.getName(), differences);
        
        // 对比机器设备
        compareMachines(line1.getMachines(), line2.getMachines(), path + ".machines", differences);
    }
    
    private void compareMachines(List<ModelData.Machine> machines1, 
                                List<ModelData.Machine> machines2,
                                String path, List<VersionComparison.Difference> differences) {
        if (machines1 == null) machines1 = new ArrayList<>();
        if (machines2 == null) machines2 = new ArrayList<>();
        
        Map<String, ModelData.Machine> map1 = machines1.stream()
            .collect(Collectors.toMap(ModelData.Machine::getId, Function.identity()));
        Map<String, ModelData.Machine> map2 = machines2.stream()
            .collect(Collectors.toMap(ModelData.Machine::getId, Function.identity()));
        
        // 类似的对比逻辑...
    }
    
    private void compareValue(String path, Object oldValue, Object newValue, 
                             List<VersionComparison.Difference> differences) {
        if (!Objects.equals(oldValue, newValue)) {
            differences.add(createDifference(path, oldValue, newValue, 
                VersionComparison.Difference.ChangeType.MODIFY));
        }
    }
    
    private VersionComparison.Difference createDifference(String path, Object oldValue, 
                                                        Object newValue, 
                                                        VersionComparison.Difference.ChangeType changeType) {
        VersionComparison.Difference diff = new VersionComparison.Difference();
        diff.setPath(path);
        diff.setOldValue(oldValue);
        diff.setNewValue(newValue);
        diff.setChangeType(changeType);
        
        // 生成描述信息
        String desc = generateDescription(path, oldValue, newValue, changeType);
        diff.setDescription(desc);
        
        return diff;
    }
    
    private String generateDescription(String path, Object oldValue, Object newValue, 
                                      VersionComparison.Difference.ChangeType changeType) {
        switch (changeType) {
            case ADD:
                return String.format("新增 %s: %s", path, newValue);
            case DELETE:
                return String.format("删除 %s: %s", path, oldValue);
            case MODIFY:
                return String.format("修改 %s: %s -> %s", path, oldValue, newValue);
            default:
                return "未知变更";
        }
    }
}

5. MySQL JSON函数的高级应用

除了基本的存储,MySQL 5.7+ 的JSON函数还可以用于优化查询:

-- 查询包含特定类型机器的版本
SELECT version, model_name 
FROM factory_model 
WHERE JSON_CONTAINS(model_data, '{"type": "ROBOT_ARM"}', '$.productionLines[*].machines[*]');

-- 查询工厂面积大于特定值的版本
SELECT version, model_name 
FROM factory_model 
WHERE JSON_EXTRACT(model_data, '$.factory.area') > 3000;

-- 提取所有版本中的机器类型统计
SELECT JSON_EXTRACT(model_data, '$.productionLines[*].machines[*].type') as machine_types
FROM factory_model;

性能优化建议

  1. JSON路径索引: 对常用的JSON字段创建虚拟列并建立索引
ALTER TABLE factory_model 
ADD COLUMN factory_name VARCHAR(100) 
GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(model_data, '$.factory.name')));

CREATE INDEX idx_factory_name ON factory_model(factory_name);
  1. 部分更新: 使用MySQL的JSON_SET等函数进行部分字段更新,减少全量数据写入

  2. 数据归档: 定期归档旧版本数据,保持主表性能