How to map a map JSON column to Java Object with JPA

前端 未结 6 1018
一整个雨季
一整个雨季 2020-12-02 14:22

We have a big table with a lot of columns. After we moved to MySQL Cluster, the table cannot be created because of:

ERROR 1118 (42000): Row size too l

相关标签:
6条回答
  • 2020-12-02 14:53

    There is a workaround for those don't want write too much code.

    Frontend -> Encode your JSON Object to string base64 in POST method, decode it to json in GET method

    In POST Method
    data.components = btoa(JSON.stringify(data.components));
    
    In GET
    data.components = JSON.parse(atob(data.components))
    

    Backend -> In your JPA code, change the column to String or BLOB, no need Convert.

    @Column(name = "components", columnDefinition = "json")
    private String components;
    
    0 讨论(0)
  • 2020-12-02 15:02

    I had a similar problem, and solved it by using @Externalizer annotation and Jackson to serialize/deserialize data (@Externalizer is OpenJPA-specific annotation, so you have to check with your JPA implementation similar possibility).

    @Persistent
    @Column(name = "params")
    @Externalizer("toJSON")
    private Params params;
    

    Params class implementation:

    public class Params {
        private static final ObjectMapper mapper = new ObjectMapper();
    
        private Map<String, Object> map;
    
        public Params () {
            this.map = new HashMap<String, Object>();
        }
    
        public Params (Params another) {
            this.map = new HashMap<String, Object>();
            this.map.putAll(anotherHolder.map);
        }
    
        public Params(String string) {
            try {
                TypeReference<Map<String, Object>> typeRef = new TypeReference<Map<String, Object>>() {
                };
                if (string == null) {
                    this.map = new HashMap<String, Object>();
                } else {
                    this.map = mapper.readValue(string, typeRef);
                }
            } catch (IOException e) {
                throw new PersistenceException(e);
            }
        }
    
        public String toJSON() throws PersistenceException {
            try {
                return mapper.writeValueAsString(this.map);
            } catch (IOException e) {
                throw new PersistenceException(e);
            }
        }
    
        public boolean containsKey(String key) {
            return this.map.containsKey(key);
        }
    
        // Hash map methods
        public Object get(String key) {
            return this.map.get(key);
        }
    
        public Object put(String key, Object value) {
            return this.map.put(key, value);
        }
    
        public void remove(String key) {
            this.map.remove(key);
        }
    
        public Object size() {
            return map.size();
        }
    }
    

    HTH

    0 讨论(0)
  • 2020-12-02 15:14

    As I explained in this article, the JPA AttributeConverter is way too limited to map JSON object types, especially if you want to save them as JSON binary.

    You don’t have to create a custom Hibernate Type to get JSON support, All you need to do is use the Hibernate Types OSS project.

    For instance, if you're using Hibernate 5.2 or newer versions, then you need to add the following dependency in your Maven pom.xml configuration file:

    <dependency>
        <groupId>com.vladmihalcea</groupId>
        <artifactId>hibernate-types-52</artifactId>
        <version>${hibernate-types.version}</version> 
    </dependency> 
    

    Now, to explain how it all works.

    As I explained in this article, for MySQL, you need to send the JSON object in a text form, so you need to use the JsonStringType provided by the Hibernate Types project.

    Now, you need to declare the new type on either at the entity attribute level, or, even better, at the class level in a base class using @MappedSuperclass:

    @TypeDef(name = "json", typeClass = JsonStringType.class)
    

    And the entity mapping will look like this:

    @Type(type = "json")
    @Column(columnDefinition = "json")
    private Location location;
    

    If you're using Hibernate 5.2 or later, then the JSON type is registered automatically by MySQL57Dialect.

    Otherwise, you need to register it yourself:

    public class MySQLJsonDialect extends MySQL55Dialect {
    
        public MySQLJsonDialect() {
            super();
            this.registerColumnType(Types.JAVA_OBJECT, "json");
        }
    }
    

    And, set the hibernate.dialect Hibernate property to use the fully-qualified class name of the MySQLJsonDialect class you have just created.

    0 讨论(0)
  • 2020-12-02 15:16

    If you need to map json type property to json format when responding to the client (e.g. rest API response), add @JsonRawValue as the following:

    @Column(name = "params", columnDefinition = "json")
    @JsonRawValue
    private String params;
    

    This might not do the DTO mapping for server-side use, but the client will get the property properly formatted as json.

    0 讨论(0)
  • 2020-12-02 15:17

    It is simple

    @Column(name = "json_input", columnDefinition = "json")
    private String field;
    

    and in mysql database your column 'json_input' json type

    0 讨论(0)
  • 2020-12-02 15:19

    You can use a JPA converter to map your Entity to the database. Just add an annotation similar to this one to your params field:

    @Convert(converter = JpaConverterJson.class)
    

    and then create the class in a similar way (this converts a generic Object, you may want to specialize it):

    @Converter(autoApply = true)
    public class JpaConverterJson implements AttributeConverter<Object, String> {
    
      private final static ObjectMapper objectMapper = new ObjectMapper();
    
      @Override
      public String convertToDatabaseColumn(Object meta) {
        try {
          return objectMapper.writeValueAsString(meta);
        } catch (JsonProcessingException ex) {
          return null;
          // or throw an error
        }
      }
    
      @Override
      public Object convertToEntityAttribute(String dbData) {
        try {
          return objectMapper.readValue(dbData, Object.class);
        } catch (IOException ex) {
          // logger.error("Unexpected IOEx decoding json from database: " + dbData);
          return null;
        }
      }
    
    }
    

    That's it: you can use this class to serialize any object to json in the table.

    0 讨论(0)
提交回复
热议问题