【问题标题】:Storing a JSON schema in mongodb with spring使用spring在mongodb中存储JSON模式
【发布时间】:2017-03-23 10:43:55
【问题描述】:

我是 Spring 数据和 mongodb 的新手。我有一个 JSON 对象,它代表一个 JSON 模式,我需要使用 spring 数据将它存储在 mongodb 中。但是 JSON Schema 的问题在于 JSON Schema 的结构是动态的;例如下面是两个结构完全不同的有效 JSON 模式。

{
    "type": "object",
    "properties": {
        "name": {
            "type": "string",
            "minLength": 10
        },
        "age": {
            "type": "integer"
        }
    },
    "required": [
        "name",
        "age"
    ]
}

{
    "type": "array",
    "items": {
        "type": "object",
        "properties": {
            "abc": {
                "type": "boolean"
            },
            "xyz": {
                "$ref": "#/definitions/"
            },
            "asd": {
                "type": "null"
            }
        },
        "required": [
            "abc",
            "xyz"
        ]
    }
}

如何定义一个 JAVA POJO 类,以便我可以将上面的 JSON 与定义的类映射并将其存储在 mongodb 中。或者是否可以在 spring 中进行 CURD 操作而不将其映射到 POJO 类?

【问题讨论】:

    标签: spring mongodb spring-data spring-data-mongodb jsonschema


    【解决方案1】:

    我建议使用MongoTemplate 并使用Gson/Jackson 进行序列化和反序列化。

    Mongo 模板有 CRUD 方法,它采用集合名称和 DBObject 实体,这与直接使用 mongo java 驱动程序非常相似。

    因此您将拥有 json 有效负载并使用其中一个映射器库将它们转换为 Map

    类似

    反序列化

    ObjectMapper mapper = new ObjectMapper(); 
    TypeReference<HashMap<String,Object>> typeRef 
            = new TypeReference<HashMap<String,Object>>() {};
    HashMap<String,Object> map = mapper.readValue(jsonpayload, typeRef); 
    

    数据库对象

    DBObject dbObject = new BasicDBObject(map);
    

    蒙古模板

    mongoTemplate.save(dbObject, "collectionname");
    

    您可以对所有其他 CRUD 操作执行类似的操作。

    【讨论】:

      【解决方案2】:

      请在此处找到必要的代码。

      @lombok.Data
      @JsonInclude(JsonInclude.Include.NON_NULL)
      @JsonIgnoreProperties(ignoreUnknown = true)
      public class Bounty {
      
        String type;
        Map<String, Object> items;
        Map<String, Object> properties;
        List<Object> required;
      }
      

      这是我的存储库类

      public interface BountyRepository extends MongoRepository<Bounty, String> {
      }
      

      这是一个控制器sn-p,你可以用它来试一试

      @GetMapping("/insert/{number}")
          public void insert(@PathVariable int number){
              bountyRepository.save(getBounty(number));
          }
      
      
          public Bounty getBounty(int number){
              ObjectMapper objectMapper = new ObjectMapper();
              String jsonString1 = "{\n" +
                  "    \"type\": \"object\",\n" +
                  "    \"properties\": {\n" +
                  "        \"name\": {\n" +
                  "            \"type\": \"string\",\n" +
                  "            \"minLength\": 10\n" +
                  "        },\n" +
                  "        \"age\": {\n" +
                  "            \"type\": \"integer\"\n" +
                  "        }\n" +
                  "    },\n" +
                  "    \"required\": [\n" +
                  "        \"name\",\n" +
                  "        \"age\"\n" +
                  "    ]\n" +
                  "}";
      
      
              String jsonString2 = "{\n" +
                  "    \"type\": \"array\",\n" +
                  "    \"items\": {\n" +
                  "        \"type\": \"object\",\n" +
                  "        \"properties\": {\n" +
                  "            \"abc\": {\n" +
                  "                \"type\": \"boolean\"\n" +
                  "            },\n" +
                  "            \"xyz\": {\n" +
                  "                \"$ref\": \"#/definitions/\"\n" +
                  "            },\n" +
                  "            \"asd\": {\n" +
                  "                \"type\": \"null\"\n" +
                  "            }\n" +
                  "        },\n" +
                  "        \"required\": [\n" +
                  "            \"abc\",\n" +
                  "            \"xyz\"\n" +
                  "        ]\n" +
                  "    }\n" +
                  "}";
      
              try {
                  Bounty bounty1 = objectMapper.readValue(jsonString1, Bounty.class);
                  Bounty bounty2 = objectMapper.readValue(jsonString2, Bounty.class);
      
      
                  if (number == 1) return bounty1;
                  if (number == 2) return bounty2;
              } catch (IOException e) {
                  e.printStackTrace();
              }
              return null;
          }
      

      这是保存后在 Mongo 中的样子。

      /* 1 */
      {
          "_id" : ObjectId("58da2390fde4f133178499fa"),
          "_class" : "pani.kiran.sumne.model.Bounty",
          "type" : "object",
          "properties" : {
              "name" : {
                  "type" : "string",
                  "minLength" : 10
              },
              "age" : {
                  "type" : "integer"
              }
          },
          "required" : [ 
              "name", 
              "age"
          ]
      }
      
      /* 2 */
      {
          "_id" : ObjectId("58da23adfde4f133178499fb"),
          "_class" : "pani.kiran.sumne.model.Bounty",
          "type" : "array",
          "items" : {
              "type" : "object",
              "properties" : {
                  "abc" : {
                      "type" : "boolean"
                  },
                  "xyz" : {
                      "$ref" : "#/definitions/"
                  },
                  "asd" : {
                      "type" : "null"
                  }
              },
              "required" : [ 
                  "abc", 
                  "xyz"
              ]
          }
      }
      

      【讨论】:

        【解决方案3】:

        在我的项目中,我的模型结构非常动态,我使用 java.util.Map 对象映射它们

        这就是我的 mondo 文档模型的实现方式:

        @Document(collection = "e_form_data")
        public class FormDataModel extends AbstractModel
        {
            private static final long serialVersionUID = -1733975205300782871L;
            @Field
            @Indexed(name = "e_form_id_idx")
            private String eFormId;
            @Field
            private Map<String, Object> eFormData;
        
            public FormDataModel()
            {
                super();
            }
        
            public FormDataModel(String id, String creatoDa, String modificatoDa, Date dataCreazione, Date dataModifica, String eFormId, Map<String, Object> eFormData)
            {
                super(id, creatoDa, modificatoDa, dataCreazione, dataModifica);
                this.eFormData = eFormData;
                this.eFormId = eFormId;
            }
        
            public FormDataModel(Map<String, Object> eFormData)
            {
                super();
                this.eFormData = eFormData;
            }
        
            public Map<String, Object> geteFormData()
            {
                return eFormData;
            }
        
            public void seteFormData(Map<String, Object> eFormData)
            {
                this.eFormData = eFormData;
            }
        
            public String geteFormId()
            {
                return eFormId;
            }
        
            public void seteFormId(String eFormId)
            {
                this.eFormId = eFormId;
            }
        
            public String getDataInserimento()
            {
                return Utils.formatDateTime(new DateTime(this.dataCreazione.getTime()), "dd/MM/yyyy");
            }
        
            @Override
            public String toString()
            {
                return "FormDataModel [eFormId=" + eFormId + ", eFormData=" + eFormData + "]";
            }
        
        }
        

        通过使用这一切都很好

        【讨论】:

        • 感谢您的回复,但我仍然不确定您将如何将该 JSON 结构映射到您建议的 Map?
        【解决方案4】:

        您可以使用@DBref映射嵌入文档

        @Document(collection = "first")
        public class First {
        
            @Id
            private String id;
        
            @DBRef
            private Properties properties;
        
            @Field
            private List<String> required;
        
            // constructor
            // getters and setter    
        }
        
        public class Properties {
        
            @Id
            private String id;
        
            @DBRef
            private Name name;
        
            @DBRef
            private Age age;
        
            // constructor
            // getters and setter   
        }
        
        public class Name { ... }
        public class Age { ... }
        

        http://www.baeldung.com/cascading-with-dbref-and-lifecycle-events-in-spring-data-mongodb

        http://docs.spring.io/spring-data/data-mongo/docs/1.4.2.RELEASE/reference/html/mapping-chapter.html#mapping-usage-references

        或者正如 Angelo Immediata 建议的那样

        @Document(collection = "first")
        public class First {
        
            @Id
            private String id;
        
            @Field
            private Map<String, Object> properties;
        
            @Field
            private List<String> required;
        
            // constructor
            // getters and setter    
        }
        

        您将需要一些自定义的读写转换器

        http://docs.spring.io/spring-data/data-mongo/docs/1.4.2.RELEASE/reference/html/mapping-chapter.html#mapping-explicit-converters

        【讨论】:

        • 您给出的示例,您定义了名为 Name、Age 等的类。在这里您假设它们是常量。如果您看到我的第二个示例,则那里的字段有所不同; abc、xyz 等。我的问题是如何找到一种适用于这两种情况的通用方法。
        • 需要把动态数据放到java Object or Map or com.mongodb.DBObject or com.mongodb.BasicDBObject com.mongodb.BasicDBList 中。检查这篇文章stackoverflow.com/a/35921264/1582089
        【解决方案5】:

        FWIW,MongoDB 3.6 在数据库级别引入了JSON Schema Validation support。您可以在MongoDB's blog 上阅读更多信息。希望对您有所帮助!

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-08-25
          • 1970-01-01
          • 2020-10-17
          • 2021-10-25
          • 2020-06-09
          • 1970-01-01
          • 1970-01-01
          • 2023-03-28
          相关资源
          最近更新 更多