2012-08-14 9 views
5

que tienen clase de datos/tabla "Usuario" que tiene la columna "preferencias"¿Cómo serializar y deserializar automáticamente cadenas JSON usando JPA e Hibernate?

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

preferencias de texto es texto y estoy almacenando JSON allí.

public class User extends AbstractEntity{ 
public String preferences; 
} 

por lo user.preferences valor es "{notifyByEmail:1, favouriteColor:"blue" }"

¿Cómo puedo envolverlo con un poco de anotación para que pueda acceder a ella como

user.preferences.notifyByEmail 

o sin necesidad de envolver al objeto de datos

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

Imagino que podría haber alguna anotación de Jackson que pueda dd a campo como

@JsonGenerate 
public String preferences; 

Soy bastante nuevo a la APP y la documentación es empinada.

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

+0

¿Cuál es la razón fundamental para querer almacenar estos datos como Cadenas JSON y no campos separados, distintos (Aquí, de tipos booleanos y enum)? – Samuel

+0

Justificación, no quiero admitir todas las preferencias de valores clave como campos separados. Clave de preferencia: los valores son cada vez mayores y hay bastantes de ellos. cambiar el modelo o agregar campo a la base de datos para cada uno de ellos es una exageración. – Roman

+0

¿Ha considerado usar una tabla de atributos para contar los valores? El caso de uso es de hecho común, pero la solución propuesta no encaja del todo en un modelo relacional. – Samuel

Respuesta

6

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 asignar esto en su entidad como esta:

@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 es más normalizado y que no tiene que perder el tiempo con 'soluciones creativas' como el almacenamiento de preferencias como JSON .

+0

Gracias por proporcionar una descripción completa. ¡Voy a intentarlo! – Roman

+7

Esto no lo hace de la forma en que la pregunta lo solicitó. –

+2

@HiramChirino No veo nada de malo en proporcionar una alternativa al interrogador. Especialmente cuando una alternativa puede proporcionar una mejor solución. El interrogador pareció satisfecho con esta solución (útil), la respuesta no es fácil o incorrecta, así que no veo ningún motivo para rechazarla. Por favor [vea las páginas de ayuda sobre cuándo votar] (http://stackoverflow.com/help/privileges/vote-down), por el bien de Stackoverflow. – siebz0r

2

Si realmente necesita algo así como que el más recomendable es hacer las siguientes:

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 la propiedad privada "preferencias" podría suprimirse y sólo utilizar captadores y setter.
  • No he probado ese código.
  • El código anterior está destinado a utilizar el módulo Jackson ObjectMapper y Hibernate.
+0

¿puede proporcionar el ejemplo de JSON Array también? He estado luchando por deserializar JSON Array con anotación. – Azerue

+0

¿Esto no requeriría una clase llamada 'Preferences' que necesita todas las diferentes preferencias que un' User' puede tener? – Christian

+0

sí. Si prefiere algo dinámico, mejor use un mapa en su lugar. –

6

Se puede lograr esto con 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) { 
      // TODO Auto-generated catch block 
      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) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
     return mapValue; 
    } 

} 

Almacenamiento de 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 la base de datos como

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

Como expliqué en this article, es muy fácil persistir objeto JSON utilizando Hibe rnate.

Usted no tiene que crear todos estos tipos de forma manual, se puede conseguir simplemente ellos a través de Maven central utilizando 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 open-source project.

Ahora, tiene que declarar el nuevo tipo a cada nivel de clase o en un package-info.java a nivel de paquete descriptior:

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

Y el mapeo de entidad se verá así:

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

Eso es todo!

Cuestiones relacionadas