需要帮助使用jdbi在postgresql中存储嵌套的json

时间:2015-09-08 18:08:59

标签: java json postgresql dropwizard jdbi

我正在尝试使用带有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);

我显然有些不对劲,但我似乎无法找出存储此嵌套数据的正确方法。

2 个答案:

答案 0 :(得分:2)

您可以使用PGObject在Java中构建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);
            }
        }
    }

}

答案 1 :(得分:1)

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

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

我通过创建一个mapper来获取要传入的字符串:

private ObjectMapper mapper = Jackson.newObjectMapper();

然后致电:

mapper.writeValueAsString(map);

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