使用 Spring / JPA 写入 Postgres 数据库的 JSON 列

Posted

技术标签:

【中文标题】使用 Spring / JPA 写入 Postgres 数据库的 JSON 列【英文标题】:Writing to JSON column of Postgres database using Spring / JPA 【发布时间】:2015-11-21 04:39:59 【问题描述】:

我有一个名为“test”的表,其中包含 Postgres 9.3 中类型为 json 的列“sample_column”。我正在尝试使用 Spring / JPA 将以下内容写入列: "name":"Updated name"

我在其他帖子上读到我需要添加一个自定义转换器来将字符串映射到 json 类型。这是我现在的代码:

TestDAO.java:

@Entity
@Table(name="test")
public class TestDAO implements Serializable 
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="id", unique=true, nullable=false)
    private Long id;   

    @Column(name="sample_column")
    @Convert(converter = MyCustomConverter.class)
    private MyCustomClass sampleColumn;

    // Getter / Setters

用于映射json内容的CustomClass:

public class MyCustomClass 
    @JsonProperty("name")
    public String name;

    public String getName() 
        return name;
    

    public void setName(String name) 
        this.name = name;
    


最后是 ConverterClass:

@javax.persistence.Converter
public class MyCustomConverter implements AttributeConverter<MyCustomClass, String> 

    private final static ObjectMapper objectMapper = new ObjectMapper();

    @Override
    @NotNull
    public String convertToDatabaseColumn(@NotNull MyCustomClass myCustomObject) 
        try 
            return objectMapper.writeValueAsString(myCustomObject);
         catch (Exception ex) 
            return null;
        
    

    @Override
    @NotNull
    public MyCustomClass convertToEntityAttribute(@NotNull String databaseDataAsJSONString) 
        try 
            return objectMapper.readValue(databaseDataAsJSONString, MyCustomClass.class);
         catch (Exception ex) 
            return null;
        
    

现在,我正在尝试将 json 列设置如下:

testDAO.getSampleColumn().setName("Updated name");
testRepository.saveAndFlush(testDAO);

但是当我尝试保存它时,我收到以下错误:

Caused by: org.postgresql.util.PSQLException: ERROR: column "sample_column" is of type json but expression is of type character varying
  Hint: You will need to rewrite or cast the expression.

但是,我可以使用 testDAO.getSampleColumn().getName(); 读取 JSON 列 这里有什么问题?我不想将任何演员表添加到表中以自动将 Varying 转换为 Json。

谢谢。

【问题讨论】:

您的 JPA 提供程序和规范是什么?您确定您的提供商符合 JPA 2.1 标准吗?转换器需要 JPA 2.1。 【参考方案1】:

您需要在 JDBC 级别使用 setObject,或传递 PgJDBC 参数 stringtype=unspecified 以允许从字符串类型到 json 等的隐式转换。

PostgreSQL 对类型转换过于严格是个问题。

【讨论】:

我应该在哪里使用setObject()?抱歉,我是 JPA 的新手。你能提供一些示例代码吗?或者至少给我指出一些资源? 我使用的是 Spring-boot,我在 application.properties 文件中设置了所有数据库属性。有没有可以用来设置stringtype=unspecified 的特定属性?现在,我只是将它作为参数添加到spring.datasource.url(即)spring.datasource.url=jdbc:postgresql://localhost:5432/dbname?stringtype=unspecified。这样做很有效。但我不想将它添加到数据源 url。如果它是一个单独的属性,我会更喜欢。 好吧,看起来 Spring-boot 提供了一个spring.datasource.connection-properties,我们可以用它来添加任何额外的参数。谢谢。 非常好的答案,尤其是与 cmets 一起。我希望看到答案与来自 cmets 的信息一起得到扩展。 spring.datasource.connection-propertiesno longer works。这个对我有用:spring.datasource.tomcat.connection-properties【参考方案2】:

对于使用 Spring-boot 的人来说,有两种方法可以按照@Craig Ringer 所说的进行操作

spring.datasource.url=jdbc:postgresql://localhost:5432/postgres?stringtype=unspecified

或使用属性

spring.datasource.hikari.data-source-properties.stringtype=unspecified

【讨论】:

【参考方案3】:

虽然两者都有效,但我建议使用 PGobject 功能而不是在连接级别设置该功能。

final ObjectMapper objectMapper = new ObjectMapper();
PGobject languageObject = new PGobject();
languageObject.setType("json");
languageObject.setValue(objectMapper.writeValueAsString(blogPosts.getLanguages()));

完成后,将参数传递给 Spring jdbctemplate 以发挥作用

希望如此。

【讨论】:

根据作者的问题,有没有办法在convertToDatabaseColumn() 中使用这种方法?我是 JPA 的新手,并且一直在努力解决类似的问题。如果我只是盲目地将其放入 convertToDatabaseColumn,那么 Postgres 就会开始抱怨 bytea【参考方案4】:

您需要将您的 JSON 转换为字符串,然后从中创建一个 PGobject。这个解决方案对我有用:

PGobject pGobject = new PGobject();
pGobject.setType("json");
pGobject.setValue(vehicleJsonData);

    try 
        KeyHolder keyHolder = new GeneratedKeyHolder();
        jdbcTemplate.update(connection -> 
            PreparedStatement ps = connection.prepareStatement(SQL_CREATE, Statement.NO_GENERATED_KEYS);
            ps.setLong(1, id);
            ps.setObject(2, pGobject);
            return ps;
        , keyHolder);

     catch (Exception e) 
        e.printStackTrace();
    

【讨论】:

以上是关于使用 Spring / JPA 写入 Postgres 数据库的 JSON 列的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot + Spring Batch + Spring JPA

JAVA spring (JPA) 中的PostgreSQL + Elasticsearch 同步

将 Spring Boot 与 JPA 一起使用时如何持久化

Spring Boot + 调度程序 + Spring Data JPA + Oracle 中的异常处理

带有悲观锁的过程中的Spring JPA事务提交

spring-boot jpa mysql emoji utfmb4 异常处理