3

我正在尝试使用带有 dropwizard 和 jdbi 的 JSONB 数据类型将 JSON 存储在 postgresql 9.4 数据库中。我能够存储数据,但是如果我的 json 比单个级别更深,则 json 会变成字符串而不是嵌套的 json。

比如下面的json

{
  "type":"unit",
  "nested": {
    "key":"embedded"
  }
}

实际上被存储为

{
  "type":"unit",
  "nested":"{key=embedded}"
}

我的 DAO 中的方法签名是

@SqlUpdate("insert into entity_json(id, content) values(:id, :content\\:\\:jsonb)")
protected abstract void createJson(@Bind("id") String id, @Bind("content") Map content);

我显然有问题,但我似乎无法找出存储这些嵌套数据的正确方法。

4

2 回答 2

4

您可以使用PGObjectJava 构建 JSONB 数据类型。这样,您可以避免任何特殊处理作为 SQL 的一部分:

PGobject dataObject = new PGobject();
dataObject.setType("jsonb");
dataObject.setValue(value.toString());

一个完整的示例,包括将对象转换为树,并使用 ArgumentFactory 将其转换为 PGobject,如下所示:

public class JsonbTest {

    @Test
    public void tryoutjson() throws Exception {
        final DBI dbi = new DBI("jdbc:postgresql://localhost:5432/sighting", "postgres", "admin");
        dbi.registerArgumentFactory(new ObjectNodeArgumentFactor());
        Sample sample = dbi.onDemand(Sample.class);

        ObjectMapper mapper = new ObjectMapper();

        int id = 2;

        User user = new User();
        user.emailaddress = "me@home.com";
        user.posts = 123;
        user.username = "test";

        sample.insert(id, mapper.valueToTree(user));
    }

    public static class User {
        public String username, emailaddress;
        public long posts;
    }

    public interface Sample {
        @SqlUpdate("INSERT INTO sample (id, data) VALUES (:id, :data)")
        int insert(@Bind("id") long id, @Bind("data") TreeNode data);
    }

    public static class ObjectNodeArgumentFactor implements ArgumentFactory<TreeNode> {

        private static class ObjectNodeArgument implements Argument {
            private final PGobject value;

            private ObjectNodeArgument(PGobject value) {
                this.value = value;
            }

            @Override
            public void apply(int position,
                              PreparedStatement statement,
                              StatementContext ctx) throws SQLException {
                statement.setObject(position, value);
            }
        }

        @Override
        public boolean accepts(Class<?> expectedType, Object value, StatementContext ctx) {
            return value instanceof TreeNode;
        }

        @Override
        public Argument build(Class<?> expectedType, TreeNode value, StatementContext ctx) {
            try {
                PGobject dataObject = new PGobject();
                dataObject.setType("jsonb");
                dataObject.setValue(value.toString());
                return new ObjectNodeArgument(dataObject);
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }

}
于 2015-09-11T21:14:01.237 回答
1

我可以通过传入一个通过调用writeValueAsString(Map)Jackson ObjectMapper 获得的字符串来解决这个问题。我的 createJson 方法变成了:

@SqlUpdate("insert into entity_json(id, content) values(:id, :content\\:\\:jsonb)")
public abstract void createJson(@Bind("id")String id, @Bind("content")String content);

我通过创建映射器获得了要传入的字符串:

private ObjectMapper mapper = Jackson.newObjectMapper();

然后调用:

mapper.writeValueAsString(map);

这给了我正在寻找的嵌套 json。

于 2015-09-08T19:33:44.997 回答