如何使用JPA和Hibernate自动序列化和反序列化JSON字符串?

Rom*_*man 6 orm json hibernate jpa jackson

我有数据类/表"用户",其中包含"首选项"列

CREATE table "user"; 
ALTER TABLE "user" ADD COLUMN preferences TEXT;
Run Code Online (Sandbox Code Playgroud)

首选项类型是TEXT,我在那里存储JSON.

public class User extends AbstractEntity{
public String preferences;
}
Run Code Online (Sandbox Code Playgroud)

所以user.preferences价值是"{notifyByEmail:1, favouriteColor:"blue" }"

如何用一些注释包装它,以便我可以像访问它一样

user.preferences.notifyByEmail
Run Code Online (Sandbox Code Playgroud)

或者不需要包装成数据对象

user.preferences.get("notifByEmail");
user.preferences.set("notifByEmail",true);
Run Code Online (Sandbox Code Playgroud)

我想可能会有一些杰克逊注释,我可以添加到字段中

@JsonGenerate
public String preferences;
Run Code Online (Sandbox Code Playgroud)

我是JPA的新手,文档很陡.

我相信我的情况很常见.有谁可以提供任何例子?

Kum*_*mar 12

可以使用JPA Converter实现这一点.

实体;

@Id
@GeneratedValue
Long id;

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

转换器:

@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;
    }

}
Run Code Online (Sandbox Code Playgroud)

保存数据:

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);
Run Code Online (Sandbox Code Playgroud)

该值将存储在DB中

{"1":"three","2":"two"}
Run Code Online (Sandbox Code Playgroud)


sie*_*z0r 5

老实说,我认为最好的解决方案是为属性创建一个单独的表(首选项)。

+------------+
| preference |
+------------+---------+------+-----+
| Field      | Type    | Null | Key |
+------------+---------+------+-----+
| user_id    | bigint  | NO   | PRI |
| key        | varchar | NO   | PRI |
| value      | varchar | NO   |     |
+------------+---------+------+-----+
Run Code Online (Sandbox Code Playgroud)

您可以这样在您的实体中进行映射:

@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;
}
Run Code Online (Sandbox Code Playgroud)

这样,您的数据库就可以更规范化,而您也不必无所事事,例如将首选项存储为JSON之类的“创意解决方案”。

  • 这不能按照问题的要求进行。 (13认同)
  • @HiramChirino我认为为提问者提供替代方案没有任何问题。特别是当替代方案可能提供更好的解决方案时。发问者似乎对此解决方案感到满意(很有帮助),答案并非毫不费力或不正确,因此我看不出有任何否决理由。为了Stackoverflow,请[请参阅有关何时投票的帮助页面](http://stackoverflow.com/help/privileges/vote-down)。 (2认同)