介绍
从 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