Pregunta ¿Cómo serializar y deserializar automáticamente la cadena JSON utilizando JPA e Hibernate?


Tengo la clase / tabla de datos "Usuario" que tiene "preferencias" de columna

CREATE table "user"; 
ALTER TABLE "user" ADD COLUMN preferences TEXT;

El tipo de preferencias es TEXTO y estoy almacenando JSON allí.

public class User extends AbstractEntity{
public String preferences;
}

asi que user.preferences el valor es "{notifyByEmail:1, favouriteColor:"blue" }"

¿Cómo puedo envolverlo con alguna anotación para poder acceder a él como

user.preferences.notifyByEmail

o sin necesidad de envolver en objeto de datos

user.preferences.get("notifByEmail");
user.preferences.set("notifByEmail",true);

Me imagino que podría haber alguna anotación de Jackson que pueda agregar al campo me gusta

@JsonGenerate
public String preferences;

Soy bastante nuevo en JPA y la documentación es empinada.

Creo que mi caso es bastante común. ¿Alguien puede dar algún ejemplo?


5
2017-08-14 08:55


origen


Respuestas:


Honestamente, creo que su mejor solución es crear una tabla separada (preferencia) para sus propiedades.

+------------+
| preference |
+------------+---------+------+-----+
| Field      | Type    | Null | Key |
+------------+---------+------+-----+
| user_id    | bigint  | NO   | PRI |
| key        | varchar | NO   | PRI |
| value      | varchar | NO   |     |
+------------+---------+------+-----+

Puede mapear esto en su entidad de esta manera:

@Entity
public class User
{
    @Id
    private Long id;

    @ElementCollection
    @MapKeyColumn(name = "key")
    @Column(name = "value")
    @CollectionTable(name = "preference",
        joinColumns = @JoinColumn(name = "user_id"))
    private Map<String, String> preferences;
}

De esta manera, su base de datos está más normalizada y no tiene que buscar 'soluciones creativas' como almacenar las preferencias como JSON.


5
2017-08-15 13:14



Puede lograr esto utilizando el convertidor JPA.

Entidad;

@Id
@GeneratedValue
Long id;

@Column(name = "mapvalue")
@Convert(converter = MapToStringConverter.class)
Map<String, String> mapValue;

Convertidor:

@Converter
public class MapToStringConverter implements AttributeConverter<Map<String, String>, String> {

    ObjectMapper mapper = new ObjectMapper();

    @Override
    public String convertToDatabaseColumn(Map<String, String> data) {
        String value = "";
        try {
            value = mapper.writeValueAsString(data);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return value;
    }

    @Override
    public Map<String, String> convertToEntityAttribute(String data) {
        Map<String, String> mapValue = new HashMap<String, String>();
        TypeReference<HashMap<String, Object>> typeRef = new TypeReference<HashMap<String, Object>>() {
        };
        try {
            mapValue = mapper.readValue(data, typeRef);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return mapValue;
    }

}

Guardando datos:

Map<String, String> mapValue = new HashMap<String, String>();
mapValue.put("1", "one");
mapValue.put("2", "two");
DataEntity entity = new DataEntity();
entity.setMapValue(mapValue);
repo.save(entity);

El valor se almacenará en DB como

{"1":"three","2":"two"}

8
2018-01-05 08:39



Si realmente necesitas algo así, lo más recomendable es hacer lo siguiente:

public class User extends AbstractEntity{
  @JsonIgnore //This variable is going to be ignored whenever you send data to a client(ie. web browser)
  private String preferences;

  @Transient //This property is going to be ignored whenever you send data to the database
  @JsonProperty("preferences") //Whenever this property is serialized to the client, it is going to be named "perferences" instead "preferencesObj"
  private Preferences preferencesObj;

  public String getPreferences() {
    return new ObjectMapper().writeValueAsString(preferencesObj);
  }

  pbulic void setPreferneces(String preferences) {
    this.preferences = preferences;
    this.preferncesObj = new ObjectMapper().readValue(preferences, Preferences.class);
  }

  pubilc Preferences getPreferencesObj() {
    return preferencesObj;
  }

  public void setPreferencesObj(Preferences preferencesObj) {
    this.preferencesObj = preferencesObj;
  }
}

Notas adicionales:

  • Tal vez las "preferencias" de propiedad privada podrían eliminarse y solo usar getter y setter.
  • No he probado ese código.
  • El código anterior está destinado a utilizar el módulo Jackson ObjectMapper y Hibernate.

2
2017-10-19 18:34



Como expliqué en Este artículo, es muy fácil persistir el objeto JSON usando Hibernate.

No tiene que crear todos estos tipos manualmente, simplemente puede obtener   a través de Maven Central usando la siguiente dependencia:

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

Para obtener más información, echa un vistazo a la hibernate-types proyecto de código abierto.

Ahora, debe declarar el nuevo tipo en cualquier nivel de clase o en un package-info.java Descriptior a nivel de paquete:

@TypeDef(
    name = "jsonb-node", 
    typeClass = JsonNodeBinaryType.class
)

Y el mapeo de la entidad se verá así:

@Type(type = "json-node")
@Column(columnDefinition = "json")
private JsonNode preferences;

¡Eso es!


0
2017-11-01 11:29