PostgreSQL 教程: 在 Java 中处理 JSON 和 JSONB 数据

九月 6, 2024

摘要:在本教程中,您将学习如何用 Java 处理 PostgreSQL 中的 JSON 和 JSONB 数据。

目录

介绍

从 v9.2 开始,PostgreSQL 为 JSON 对象提供了原生数据类型的支持。后续版本引入了 JSONB(二进制格式化的 JSON 对象)和许多 JSON 数据操作函数,使其成为 NoSQL 操作方面非常强大的工具。

字符串操作和解析在数据库中是高开销的操作,因此,尽管您之前可能在 PostgreSQL 中以字符串形式存储 JSON 对象,但原生数据类型的引入消除了开销,并使 JSON 操作的吞吐量大大加快。

JSON 和 JSONB

在 PostgreSQL v9.4 中引入了原生数据类型 JSONB,它以二进制格式存储 JSON 对象。JSON 和 JSONB 之间的主要区别列出在下表中:

JSON JSONB
以文本格式存储数据 以分解的二进制格式存储数据
输入速度快,因为不需要转换 输入速度稍慢,因为存在与二进制转换相关的开销
处理函数必须在每次执行时重新解析数据 无需重新解析,使数据处理速度明显加快
输入中的所有空格和换行符都按原样保留 去除多余的空白和换行符
不支持索引 支持索引
保留重复的键,处理函数只考虑最后一个值 重复的键在输入时被清除,只存储最后一个值
保留键的顺序 不保留键的顺序

JSON 数据定义

JSON 列的创建方式与任何其他数据类型一样。我们在下面创建一个表 ‘sales’(我们将在后续示例中使用),其中包含 2 列 ‘id’ 和 ‘sale’,后者是一个 JSON 列:

CREATE TABLE sales (id INT, sale JSON);

JSON 数据插入

JSON 数据类型会检查有效的 JSON 格式,因此 INSERT 语句应注意这一点。下面这个简单的 Java 程序,将 4 条记录插入到我们刚刚创建的表中。

String[] json = {
  "{\"customer_name\": \"John\", \"items\": { \"description\": \"milk\", \"quantity\": 4 } }",
  "{\"customer_name\": \"Susan\", \"items\": { \"description\": \"bread\", \"quantity\": 2 } }",
  "{\"customer_name\": \"Mark\", \"items\": { \"description\": \"bananas\", \"quantity\": 12 } }",
  "{\"customer_name\": \"Jane\", \"items\": { \"description\": \"cereal\", \"quantity\": 1 } }"};

try {
  String sql = "INSERT INTO sales VALUES (?, ?::JSON)";
  PreparedStatement ps = conn.prepareStatement(sql);

  for (int i=0; i<4; i++) {
    ps.setInt(1, i+1);
    ps.setObject(2, json[i]);
    ps.executeUpdate();
  }
  conn.commit();
} catch (Exception e) {
   System.out.println(e.getMessage());
   e.printStackTrace();
}

请注意字符串是如何在预备语句中转换为 JSON 的。

这是上面的数据插入后,在 psql 中的显示结果:

select * from sales;
 id | sale
----+-----------------------------------------------------------------------------------
 1  | {"customer_name": "John", "items": { "description": "milk", "quantity": 4 } }
 2  | {"customer_name": "Susan", "items": { "description": "bread", "quantity": 2 } }
 3  | {"customer_name": "Mark", "items": { "description": "bananas", "quantity": 12 } }
 4  | {"customer_name": "Jane", "items": { "description": "cereal", "quantity": 1 } }
(4 rows)

JSON 数据检索

在检索 JSON 数据时,您可以使用 PostgreSQL 原生运算符访问单个元素,也可以使用 Java 库提供的 JSONObject 处理 Java 中的对象。下面给出了两种情况的示例。

使用 PostgreSQL 运算符

PostgreSQL 提供了 ‘->’ 运算符来检索 JSON 对象中各种键的值。下面的示例程序,检索 sale 的 ‘customer_name’ 列表,然后检索 ‘items’ 的 ‘description’ 列表。sale 是一个嵌入的 JSON。

try {
  /* Retrieving customer_name */
  String sql = "select sale->'customer_name' from sales";
  PreparedStatement ps = conn.prepareStatement(sql);
  ResultSet rs = ps.executeQuery();

  while(rs.next()) {
    System.out.println(rs.getString(1));
  }

  System.out.println("******************************************");

  /* Retrieving description, which is an embedded JSON */
  sql = "select sale->'items'->'description' from sales";
  ps = conn.prepareStatement(sql);
  rs = ps.executeQuery();

  while(rs.next()) {
    System.out.println(rs.getString(1));
  }
} catch (Exception e) {
  System.out.println(e.getMessage());
  e.printStackTrace();
}

上面程序的输出是:

"John"
"Susan"
"Mark"
"Jane"
******************************************
"milk"
"bread"
"bananas"
"cereal"

使用 JSONObject

为了在 Java 程序中使用 JSONObject,您需要在 CLASSPATH 中拥有其软件库的 jar 文件。jar 文件可以从许多位置免费获得,包括这个位置。基本的 JSON 操作将在下面介绍,但您可以在此处获取更多 API 详细信息。

下面的 Java 代码,可获得与上面演示的相同的结果:

try {
  /* Retrieving customer_name */
  String sql = "select sale from sales";
  Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
  ResultSet rs = stmt.executeQuery(sql);

  JSONObject json;
  JSONObject json2;

  while(rs.next()) {
    json = new JSONObject(rs.getString(1));
    System.out.println(json.get("customer_name"));
  }

  System.out.println("******************************************");

  /* Retrieving description, which is an embedded JSON */
  rs.first();

  do {
    json = new JSONObject(rs.getString(1));
    json2 = (JSONObject)json.get("items");
    System.out.println(json2.get("description"));
  } while(rs.next());
} catch (Exception e) {
  System.out.println(e.getMessage());
  e.printStackTrace();
}

该程序的输出为:

John
Susan
Mark
Jane
******************************************
milk
bread
bananas
cereal