如何使用PostgreSQL中的JSONB数据类型(一)

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
简介: 仅仅使用PG的row_to_json(),即可轻松返回需要的各种SQL数据。这样子使得中间层(JAVA/PHP)的代码变得简化,无需org.json和json-lib库再做一次封装,这是架构师要注意的一个重要趋势。

本系列博文讲述使用PG JSON/JSONB类型考虑的问题,这是第一篇(我尽量写的简单,让初学者跟着做很容易也能重复实验),看看不使用JSON/JSONB,仅仅使用row_to_json()函数能帮我们做什么。

JSON数据类型在当前互联网浪潮中得到了大量的采用,见下图GOOGLE指数对比JSON和XML,从全球范围来看:

cdc116eb7ecbfb88e1b7939e29d6be26eada620d

蓝色表示JSON的趋势,成明显的上升趋势。

从中国范围来看,JSON搜索热度明显高于XML:

b9f4459a662df4ab91d0295280ef51c99bcca1dd

PG也从9.2开始正式引入JSON类型,那自然就问:

  1. 既然DB支持了JSON和JSON相关函数,是不是中间服务层(如PHP,JAVA)通过JDBC可以直接获取到JSON数据,而无需再用org.json和json-lib库把以前的行数据进行转换?

我们来做个实验:

运行环境:PG 9.6,WIN8.1 64,Tomcat 8.0,JDK 8


-- 创建数据库
--postgres登陆pg,使用如下sql创建数据库和用户名密码:
CREATE ROLE jsontest PASSWORD 'jsontest.dba' CREATEDB NOSUPERUSER CREATEROLE LOGIN; 
CREATE DATABASE jsontestdb OWNER jsontest ENCODING 'UTF8';
-- 用jsontest登录,然后是DDL:
-- 创建一个全局的序列
DROP SEQUENCE IF EXISTS jsontest_uuid_seq CASCADE;
CREATE SEQUENCE jsontest_uuid_seq START 0;
-- Table: TUsers, 用户表
DROP TABLE IF EXISTS TUsers CASCADE;
CREATE TABLE TUsers (
id bigint DEFAULT nextval('jsontest_uuid_seq') PRIMARY KEY,-- 用户id
realName character varying(64) -- 真实姓名
)WITH ( OIDS=FALSE );
CREATE INDEX TUsers_cellphone_idx ON TUsers (realName);
-- Table: TProject,用户创建的项目表
DROP TABLE IF EXISTS TProject CASCADE;
CREATE TABLE TProject (
id bigint DEFAULT nextval('jsontest_uuid_seq') PRIMARY KEY,-- 活动id
title character varying(256) NOT NULL UNIQUE, -- 项目名称,设置为UNIQUE,避免混淆
creatorId integer DEFAULT NULL REFERENCES TUsers (id) match simple on delete SET NULL -- 活动创建的用户id
)WITH ( OIDS=FALSE );
CREATE INDEX TProject_creator_idx ON TProject( creatorId );
-- 测试数据:
insert into TUsers values(1,'test1');
insert into TUsers values(2,'test2');
insert into TUsers values(3,'test3');
insert into TProject values(1,'测试项目1',1);
insert into TProject values(2,'测试项目2',1);
insert into TProject values(3,'测试项目3',1);


DB设计图(在Eclipse用ERMaster反向生成)如下:

92eef4fae9092269236458894ef24e0fb960fa21

好,JAVA这一块,我们分多种情况来分析:

1. 数据库最多只会返回一行数据,然后转换成JSON对象:如通过用户id获得用户JSON对象


/*
	 * 根据用户id获取用户信息
	 */
	public String getUserInfoFromId(String userId){		
    	String res = null ;
    	if(null ==  userId || userId.isEmpty() ) return res;
    	Connection conn;
		Statement stm;
		ResultSet rs;	
		DBPool dbp = new DBPool();
		conn = dbp.getConnection();
		try {
			stm = conn.createStatement();
			/*
			 * WITH myInfo AS (select a.id,a.realName from TUsers a where a.id = 1 )
SELECT row_to_json(b.*) from myInfo b
			 */
			String sql = "SELECT row_to_json(a.*) from TUsers a where a.id = "+userId;
			rs = stm.executeQuery(sql);
			if(rs != null && rs.next()){						
				res = rs.getString(1); 				
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally{
			dbp.closeConnection();
		}    	
    	return res;    	
    }

上面的核心是SQL:SELECT row_to_json(a.*) from TUsers a where a.id = 1

那如果我获得的不是a.*,即不需要一整行数据(如密码不想返回)怎么办?如下会报错:

SELECT row_to_json(a.id,a.realName) from TUsers a where a.id = 1

解决办法有:

SELECT row_to_json(a.*) from (select b.id,b.realName from TUsers b where b.id = 1 ) a

或者使用WITH:

WITH myInfo AS (select a.id,a.realName from TUsers a where a.id = 1 )

SELECT row_to_json(b.*) from myInfo b

或者:

SELECT

(SELECT row_to_json(_) from (select a.id, a.realName) as _)

from

TUsers a where a.id = 1

关于这个问题可以参考:http://dba.stackexchange.com/questions/27732/set-names-to-attributes-when-creating-json-with-row-to-json

注意的是,不能用row()函数,因为无法产生别名,即正如https://www.postgresql.org/docs/9.6/static/functions-json.html里所演示的那样:

row_to_json(row(1,'foo')) {"f1":1,"f2":"foo"}

所以综合以上,建议用WITH方法,简洁明了。


public class UserWS extends HttpServlet :


@WebServlet(asyncSupported = true, urlPatterns = { "/user" })
public class UserWS extends HttpServlet {
	private static final long serialVersionUID = 1L;       
   
    public UserWS() {
        super();        
    }	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String rtn = "";
		request.setCharacterEncoding("UTF-8");
		response.setContentType("text/json; charset=UTF-8");  
		response.setCharacterEncoding("UTF-8");
		String action = request.getParameter("action");		
				
		if(action.equalsIgnoreCase("getuserinfo")) {//其他人通过用户ID获取用户信息
			String userid = (String) request.getParameter("userid");
			User u = new User();
			String userJson = u.getUserInfoFromId(userid);
			if(null != userJson){//存在该用户信息
				rtn = new StringBuilder()
						.append( "{\"issuccessful\":\"true\",\"data\":")
						.append((null==userJson)?"":userJson)
						.append("}")
						.toString();			
			}else{
				rtn = "{\"issuccessful\":\"false\",\"errorcode\":\"NOEXIST_USER\"}";
			}			
        }
		
		String callbackFunName = request.getParameter("callbackparam");
		if(null != callbackFunName ){
			response.getOutputStream().write(StringUtil.wrapCrossDomain(rtn,callbackFunName).getBytes("UTF-8")); 
		}else{
			response.getOutputStream().write(rtn.getBytes("UTF-8")); 
		}
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request,response);	
	}
}


测试一下,当我们发送:http://localhost:8080/jsonTest/user?action=getuserinfo&userid=1

返回:

{
"issuccessful":"true",
"data":{
"id":1,
"realname":"test1"
}
}

2. 数据库返回多行数据:如获取用户参与的项目

WITH myProjects AS (select a.id,a.title from TProject a where a.creatorId = 1)

SELECT row_to_json(b.*) from

(SELECT array_to_json(array(select row_to_json(myProjects.*) from myProjects),false) as myProjects) b

测试一下:http://localhost:8080/jsonTest/project?action=getmyprojects&userid=1

返回:


{  
"issuccessful":"true",
"data":{  
"myprojects":[  
{  
"id":3,
"title":"测试项目3"
},
{  
"id":2,
"title":"测试项目2"
},
{  
"id":1,
"title":"测试项目1"
}
]
}
}

3. 返回某些表的一行数据,加上某些表的多行数据:如获取用户基本信息以及用户参与的项目

WITH myInfo AS (select id,realName from TUsers where id = 1 ), -- 一行数据

myProjects AS (select a.id,a.title from TProject a,myInfo b where a.creatorId = b.id) -- 多行数据

SELECT row_to_json(x.*) from

(

select c.*,d.* from

myInfo c,

(SELECT array_to_json(array(select row_to_json(myProjects.*) from myProjects),false) as myProjects) d -- 把多行数据生成一行一列json数组

) x

该SQL返回:

{
"myprojects":[
{
"id":3,
"title":"测试项目3"
},
{
"id":2,
"title":"测试项目2"
},
{
"id":1,
"title":"测试项目1"
}
],
"id":1,
"realname":"test1"
}


总结一下:

仅仅使用PG的row_to_json(),即可轻松返回需要的各种SQL数据。这样子使得中间层(JAVA/PHP)的代码变得简化,无需org.json和json-lib库再做一次封装,这是架构师要注意的一个重要趋势。




相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
目录
相关文章
|
7月前
|
JSON Java 关系型数据库
Spring Boot 学习研究笔记(十三) Spring Data JPA与PostgreSQL的jsonb类型集成
Spring Boot 学习研究笔记(十三) Spring Data JPA与PostgreSQL的jsonb类型集成
|
12月前
|
XML SQL JSON
PostgreSQL 12 文档: 第 8 章 数据类型
PostgreSQL有着丰富的本地数据类型可用。用户可以使用CREATE TYPE命令为 PostgreSQL增加新的数据类型。
126 0
|
12月前
|
存储 JSON NoSQL
「Postgresql架构」使用PostgreSQL中的JSONB数据类型加快操作
「Postgresql架构」使用PostgreSQL中的JSONB数据类型加快操作
|
SQL 存储 移动开发
PostgreSQL psql的使用,SQL语法,数据类型,递归SQL用法(四)|学习笔记
快速学习3 PostgreSQL psql的使用,SQL语法,数据类型,递归SQL用法(四)
385 0
 PostgreSQL psql的使用,SQL语法,数据类型,递归SQL用法(四)|学习笔记
|
SQL 关系型数据库 数据库
3 PostgreSQL psql的使用,SQL语法,数据类型,递归SQL用法(三)|学习笔记
快速学习3 PostgreSQL psql的使用,SQL语法,数据类型,递归SQL用法(三)
306 0
|
存储 关系型数据库 数据库
PostgreSQL 数据类型
PostgreSQL 数据类型
87 0
|
关系型数据库 PostgreSQL
PostgreSQL - 数据类型 VARCHAR 编码长度问题
PostgreSQL - 数据类型 VARCHAR 编码长度问题
444 0
|
SQL JSON 关系型数据库
关于PostgreSQL数据增删改查的日常总结(主要jsonb类型)
在工作中,对PostgreSQL数据库操作,最难的也就是对jsonb类型的数据进行增删改查了,其他字段跟MySQL数据库没什么区别,现在我就分享一下平时工作中总结的相关操作,这是我承包公司一年sql脚本开发中遇到并总结的,公司使用这种数据库的可以收藏,提高你的开发速度。注意,示例中ext为jsonb类型。
281 0
|
存储 XML SQL
PostgreSQL 数据类型
本章节,我们将讨论 PostgreSQL 的数据类型,数据类型是我们再创建表的时候为每个字段设置的。 设置数据类型的好处: PostgreSQL提 供了丰富的数据类型。用户可以使用 CREATE TYPE 命令在数据库中创建新的数据类型。PostgreSQL 的数据类型有很多种,下面我们具体来说明。 数值类型 数值类型由 2 字节、4 字节或 8 字节的整数以及 4 字节或 8 字节的浮点数和可选精度的十进制数组成。 下表列出了可用的数值类型。
164 0
|
数据格式 JSON 索引
AnalyticDB for PostgreSQL 6.0新特性 JSONB数据类型
ADB PG 6.0 JSONB数据类型特性 JSON Types JSON数据类型顾名思义是用来存储JSON数据的,这种数据也可以用text类型来存储,但是JSON数据类型会对数据做JSON规则校验,同时提供一些列的特定的JSON化的函数,让用户可以对这些数据作出一些特殊的操作。
2795 0