Ian Phillips
Ian Phillips

Reputation: 567

Is it possible to write a data type Converter to handle postgres JSON columns?

Ideally using Jackson on the Java side of things. I have tried the obvious solution:

public class JsonObjectConverter implements Converter<Object, ObjectNode> {

    private final ObjectMapper mapper = new ObjectMapper();

    @Override public ObjectNode from(Object dbo) {
        try {
            return dbo != null ? mapper.readValue((String) dbo, ObjectNode.class) : null;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override public Object to(ObjectNode uo) {
        try {
            return uo != null ? mapper.writeValueAsString(uo) : null;
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    @Override public Class<Object> fromType() {
        return Object.class;
    }

    @Override public Class<ObjectNode> toType() {
        return ObjectNode.class;
    }
}

But if I try to use this I get errors like the following:

org.jooq.exception.DataAccessException: SQL [insert into "public"."my_table" ("id", "stuff") values (?, ?)]; ERROR: column "stuff" is of type json but expression is of type character varying
  Hint: You will need to rewrite or cast the expression.

However because of jOOQs enforced type safety (which is great, BTW) I can't just add a .cast(String.class) and be done. So, do I need to do something else in the converter, or should the code be called in a different way? I'm currently doing this:

Long id = ...
ObjectNode stuff = ...
create.insertInto(MY_TABLE)
    .set(MY_TABLE.ID, id)
    .set(MY_TABLE.STUFF, stuff)
    .execute();

and am using updatable records in other places in my code.

Upvotes: 2

Views: 3385

Answers (2)

Lukas Eder
Lukas Eder

Reputation: 221275

You probably won't get the JSON data type 100% correct with a Converter only. Ideally, you should use a jOOQ 3.5 org.jooq.Binding implementation, which is documented here:

The code generator can be configured to use your custom Binding (instead or in addition to Converters) directly on your database columns. The Binding will then take care of all necessary interaction on a JDBC level.

Upvotes: 0

Ian Phillips
Ian Phillips

Reputation: 567

Yes, it is, but you need to use Postgres specific API. In the code above you need to replace the from/to methods with the following:

@Override
public ObjectNode from(Object databaseObject) {
    if (databaseObject == null) { return null; }
    try {
        PGobject dbo = (PGobject) databaseObject;
        return mapper.readValue(dbo.getValue(), ObjectNode.class);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

@Override
public Object to(ObjectNode userObject) {
    if (userObject == null) { return null; }
    try {
        PGobject dbo = new PGobject();
        dbo.setType("json");
        dbo.setValue(mapper.writeValueAsString(userObject));
        return dbo;
    } catch (JsonProcessingException|SQLException e) {
        throw new RuntimeException(e);
    }
}

Upvotes: 4

Related Questions