当前位置: 首页 > news >正文

PostgreSQL数据类型使用

postgresql使用学习

如果你看到这篇文章,相信你对postgresql已经有所了解,但是对postgresql的使用可能还不是很熟悉。如果是这样,请接着往下看。

postgresql类型

下面的类型引用字菜鸟教程,点击链接可以跳转
我们可以对比着mysql来简单了解学习postgresql。如果对mysql比较数据,那么学习postgresql也会比较容易。相比较与mysqlpostgresql有着更丰富的数据类型,更高的安全性。
postgresql类型如下:
数值类型
数值类型由 2 字节、4 字节或 8 字节的整数以及 4 字节或 8 字节的浮点数和可选精度的十进制数组成。

下表列出了可用的数值类型

名字存储长度描述范围
smallint2 字节小范围整数范围-32768 到 +32767
integer4 字节常用的整数-2147483648 到 +2147483647
bigint8 字节大范围整数-9223372036854775808 到 +9223372036854775807
decimal可变长用户指定的精度,精确小数点前 131072 位;小数点后 16383 位
numeric可变长用户指定的精度,精确小数点前 131072 位;小数点后 16383 位
real4 字节可变精度,不精确6 位十进制数字精度
double precision8 字节可变精度,不精确15 位十进制数字精度
smallserial2 字节自增的小范围整数1 到 32767
serial4 字节自增整数1 到 2147483647
bigserial8 字节自增的大范围整数1 到 9223372036854775807

货币类型
money 类型存储带有固定小数精度的货币金额。

numeric、int 和 bigint 类型的值可以转换为 money,不建议使用浮点数来处理处理货币类型,因为存在舍入错误的可能性。

名字存储长度描述范围
money8 字节货币金额-92233720368547758.08 到 +92233720368547758.07

字符类型
下表列出了 PostgreSQL 所支持的字符类型:

序号名字 & 描述
1character varying(n), varchar(n)变长,有长度限制
2character(n), char(n) f定长,不足补空白
3text 变长,无长度限制

日期/时间类型
下表列出了 PostgreSQL 支持的日期和时间类型。

名字存储空间描述最低值最高值分辨率
timestamp [ § ] [ without time zone ]8 字节日期和时间(无时区)4713 BC294276 AD1 毫秒 / 14 位
timestamp [ § ] with time zone8 字节日期和时间,有时区4713 BC294276 AD1 毫秒 / 14 位
date4 字节只用于日期4713 BC5874897 AD1 天
time [ § ] [ without time zone ]8 字节只用于一日内时间00:00:0024:00:001 毫秒 / 14 位
time [ § ] with time zone12 字节只用于一日内时间,带时区00:00:00+145924:00:00-14591 毫秒 / 14 位
interval [ fields ] [ § ]12 字节时间间隔-178000000 年178000000 年1 毫秒 / 14 位

布尔类型
PostgreSQL 支持标准的 boolean 数据类型。

boolean 有"true"(真)或"false"(假)两个状态, 第三种"unknown"(未知)状态,用 NULL 表示。

名称存储格式描述
boolean1 字节true/false

枚举类型
枚举类型是一个包含静态和值的有序集合的数据类型。

PostgreSQL 中的枚举类型类似于 C 语言中的 enum 类型。

与其他类型不同的是枚举类型需要使用 CREATE TYPE 命令创建。

CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');

创建一周中的几天,如下所示:

CREATE TYPE week AS ENUM ('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun');

就像其他类型一样,一旦创建,枚举类型可以用于表和函数定义。

CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');
CREATE TABLE person (
name text,
current_mood mood
);
INSERT INTO person VALUES ('Moe', 'happy');
SELECT * FROM person WHERE current_mood = 'happy';
name | current_mood
------+--------------
Moe  | happy
(1 row)

几何类型
几何数据类型表示二维的平面物体。

下表列出了 PostgreSQL 支持的几何类型。

最基本的类型:点。它是其它类型的基础。

名字存储空间说明表现形式
point16 字节平面中的点(x,y)
line32 字节(无穷)直线(未完全实现)((x1,y1),(x2,y2))
lseg32 字节(有限)线段((x1,y1),(x2,y2))
box32 字节矩形((x1,y1),(x2,y2))
path16+16n 字节闭合路径(与多边形类似)((x1,y1),…)
path16+16n 字节开放路径[(x1,y1),…]
polygon40+16n 字节多边形(与闭合路径相似)((x1,y1),…)
circle24 字节<(x,y),r> (圆心和半径)

网络地址类型
PostgreSQL 提供用于存储 IPv4 、IPv6 、MAC 地址的数据类型。

用这些数据类型存储网络地址比用纯文本类型好, 因为这些类型提供输入错误检查和特殊的操作和功能。

名字存储空间描述
cidr7 或 19 字节IPv4 或 IPv6 网络
inet7 或 19 字节IPv4 或 IPv6 主机和网络
macaddr6 字节MAC 地址

在对 inet 或 cidr 数据类型进行排序的时候, IPv4 地址总是排在 IPv6 地址前面,包括那些封装或者是映射在 IPv6 地址里的 IPv4 地址, 比如 ::10.2.3.4 或 ::ffff:10.4.3.2。

位串类型
位串就是一串 1 和 0 的字符串。它们可以用于存储和直观化位掩码。 我们有两种 SQL 位类型:bit(n) 和bit varying(n), 这里的n是一个正整数。

bit 类型的数据必须准确匹配长度 n, 试图存储短些或者长一些的数据都是错误的。bit varying 类型数据是最长 n 的变长类型;更长的串会被拒绝。 写一个没有长度的bit 等效于 bit(1), 没有长度的 bit varying 意思是没有长度限制。

文本搜索类型
全文检索即通过自然语言文档的集合来找到那些匹配一个查询的检索。

PostgreSQL 提供了两种数据类型用于支持全文检索:

序号名字 & 描述
1tsvector
tsvector 的值是一个无重复值的 lexemes 排序列表, 即一些同一个词的不同变种的标准化。
2tsquery tsquery 存储用于检索的词汇,并且使用布尔操作符 &(AND),

UUID 类型
uuid 数据类型用来存储 RFC 4122,ISO/IEF 9834-8:2005 以及相关标准定义的通用唯一标识符(UUID)。 (一些系统认为这个数据类型为全球唯一标识符,或GUID。) 这个标识符是一个由算法产生的 128 位标识符,使它不可能在已知使用相同算法的模块中和其他方式产生的标识符相同。 因此,对分布式系统而言,这种标识符比序列能更好的提供唯一性保证,因为序列只能在单一数据库中保证唯一。

UUID 被写成一个小写十六进制数字的序列,由分字符分成几组, 特别是一组8位数字+3组4位数字+一组12位数字,总共 32 个数字代表 128 位, 一个这种标准的 UUID 例子如下:

a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11

XML 类型
xml 数据类型可以用于存储XML数据。 将 XML 数据存到 text 类型中的优势在于它能够为结构良好性来检查输入值, 并且还支持函数对其进行类型安全性检查。 要使用这个数据类型,编译时必须使用 configure --with-libxml。

xml 可以存储由XML标准定义的格式良好的"文档", 以及由 XML 标准中的 XMLDecl? content 定义的"内容"片段, 大致上,这意味着内容片段可以有多个顶级元素或字符节点。 xmlvalue IS DOCUMENT 表达式可以用来判断一个特定的 xml 值是一个完整的文件还是内容片段。

创建XML值
使用函数 xmlparse: 来从字符数据产生 xml 类型的值:

XMLPARSE (DOCUMENT '<?xml version="1.0"?><book><title>Manual</title><chapter>...</chapter></book>')
XMLPARSE (CONTENT 'abc<foo>bar</foo><bar>foo</bar>')

JSON 类型
json 数据类型可以用来存储 JSON(JavaScript Object Notation)数据, 这样的数据也可以存储为 text,但是 json 数据类型更有利于检查每个存储的数值是可用的 JSON 值。

此外还有相关的函数来处理 json 数据:

实例实例结果
array_to_json(‘{{1,5},{99,100}}’::int[])[[1,5],[99,100]]
row_to_json(row(1,‘foo’)){“f1”:1,“f2”:“foo”}

数组类型
PostgreSQL 允许将字段定义成变长的多维数组。

数组类型可以是任何基本类型或用户定义类型,枚举类型或复合类型。

声明数组
创建表的时候,我们可以声明数组,方式如下:

CREATE TABLE sal_emp (
name            text,
pay_by_quarter  integer[],
schedule        text[][]
);

实际使用postgresql

使用postgresql肯定是根据postgresql的类型进行使用,postgresql的类型定义如上,下面我们开始在代码中定义使用这些类型。
demo使用技术: postgresql,mybatis
本篇demo的演示借助于mybatis框架实现。

1.首先梳理一下,如果要使用一个数据库,那么必须要先连接到数据库,即配置数据源。
2.连接数据库成功后就需要操作数据,写入数据到数据库,从数据库取出数据。

因为postgresql有丰富的数据类型,不同的数据类型有不同的处理器。基于此,mybatis提供了类型处理器来对不同的类型进行处理。

使用mybatis时必须有两个配置文件

mybatis的核心配置文件:配置数据源相关信息
数据库操作的mapper文件:sql语句与查询接口的映射

mybatis配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--    给User对象起别名User,如果不定义这个,后续使用到User的地方必须引用User对象的全路径名--><typeAliases><typeAlias type="com.example.pgstudy.User" alias="User"/></typeAliases>
<!--    全局类型处理器--><typeHandlers><typeHandler handler="com.example.pgstudy.UUIDTypeHandler"/><typeHandler handler="com.example.pgstudy.IntegerArrayTypeHandler"/><typeHandler handler="com.example.pgstudy.StringArrayTypeHandler"/></typeHandlers>
<!--    配置数据源--><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="org.postgresql.Driver"/><property name="url" value="jdbc:postgresql://localhost:5432/root"/><property name="username" value="root"/><property name="password" value="123456"/></dataSource></environment></environments>
<!--    配置mapper资源文件--><mappers><mapper resource="UserMapper.xml"/><mapper resource="DemoTypeMapper.xml"/></mappers>
</configuration>

DemoTypeMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.pgstudy.DemoTypeMapper"><resultMap id="DemoTypeResultMap" type="com.example.pgstudy.DemoType"><id property="id" column="id" /><result property="intCol" column="int_col" /><result property="bigintCol" column="bigint_col" /><result property="smallintCol" column="smallint_col" /><result property="serialCol" column="serial_col" /><result property="decimalCol" column="decimal_col" /><result property="numericCol" column="numeric_col" /><result property="realCol" column="real_col" /><result property="doubleCol" column="double_col" /><result property="charCol" column="char_col" /><result property="varcharCol" column="varchar_col" /><result property="textCol" column="text_col" /><result property="boolCol" column="bool_col" /><result property="dateCol" column="date_col" /><result property="timeCol" column="time_col" /><result property="timestampCol" column="timestamp_col" /><result property="intervalCol" column="interval_col" typeHandler="com.example.pgstudy.IntervalTypeHandler" /><result property="byteaCol" column="bytea_col" /><result property="jsonCol" column="json_col" typeHandler="com.example.pgstudy.JsonTypeHandler" /><result property="jsonbCol" column="jsonb_col" typeHandler="com.example.pgstudy.JsonbTypeHandler" /><result property="uuidCol" column="uuid_col" /><result property="intArrayCol" column="int_array_col" /><result property="textArrayCol" column="text_array_col" /><result property="inetCol" column="inet_col" typeHandler="com.example.pgstudy.PostgreSQLTypeHandler" /><result property="cidrCol" column="cidr_col" typeHandler="com.example.pgstudy.PostgreSQLTypeHandler" /><result property="macaddrCol" column="macaddr_col" typeHandler="com.example.pgstudy.PostgreSQLTypeHandler" /><result property="pointCol" column="point_col" typeHandler="com.example.pgstudy.PostgreSQLTypeHandler" /><result property="lineCol" column="line_col" typeHandler="com.example.pgstudy.PostgreSQLTypeHandler" /><result property="lsegCol" column="lseg_col" typeHandler="com.example.pgstudy.PostgreSQLTypeHandler" /><result property="boxCol" column="box_col" typeHandler="com.example.pgstudy.PostgreSQLTypeHandler" /><result property="pathCol" column="path_col" typeHandler="com.example.pgstudy.PostgreSQLTypeHandler" /><result property="polygonCol" column="polygon_col" typeHandler="com.example.pgstudy.PostgreSQLTypeHandler" /><result property="circleCol" column="circle_col" typeHandler="com.example.pgstudy.PostgreSQLTypeHandler" /><result property="xmlCol" column="xml_col" typeHandler="com.example.pgstudy.PostgreSQLTypeHandler" /><result property="moneyCol" column="money_col" /></resultMap><insert id="insertDemoType" parameterType="com.example.pgstudy.DemoType">INSERT INTO demo_types (int_col, bigint_col, smallint_col, serial_col, decimal_col, numeric_col, real_col, double_col,char_col, varchar_col, text_col, bool_col, date_col, time_col, timestamp_col, interval_col,bytea_col, json_col, jsonb_col, uuid_col, int_array_col, text_array_col, inet_col, cidr_col, macaddr_col,point_col, line_col, lseg_col, box_col, path_col, polygon_col, circle_col, xml_col, money_col) VALUES (#{intCol}, #{bigintCol}, #{smallintCol}, #{serialCol}, #{decimalCol}, #{numericCol}, #{realCol}, #{doubleCol},#{charCol}, #{varcharCol}, #{textCol}, #{boolCol}, #{dateCol}, #{timeCol}, #{timestampCol}, #{intervalCol, typeHandler=com.example.pgstudy.IntervalTypeHandler},#{byteaCol}, #{jsonCol, typeHandler=com.example.pgstudy.JsonTypeHandler}, #{jsonbCol, typeHandler=com.example.pgstudy.JsonbTypeHandler}, #{uuidCol}, #{intArrayCol, jdbcType=ARRAY}, #{textArrayCol, jdbcType=ARRAY}, #{inetCol, typeHandler=com.example.pgstudy.PostgreSQLTypeHandler}, #{cidrCol, typeHandler=com.example.pgstudy.PostgreSQLTypeHandler}, #{macaddrCol, typeHandler=com.example.pgstudy.PostgreSQLTypeHandler},#{pointCol, typeHandler=com.example.pgstudy.PostgreSQLTypeHandler}, #{lineCol, typeHandler=com.example.pgstudy.PostgreSQLTypeHandler}, #{lsegCol, typeHandler=com.example.pgstudy.PostgreSQLTypeHandler}, #{boxCol, typeHandler=com.example.pgstudy.PostgreSQLTypeHandler}, #{pathCol, typeHandler=com.example.pgstudy.PostgreSQLTypeHandler}, #{polygonCol, typeHandler=com.example.pgstudy.PostgreSQLTypeHandler}, #{circleCol, typeHandler=com.example.pgstudy.PostgreSQLTypeHandler}, #{xmlCol, typeHandler=com.example.pgstudy.PostgreSQLTypeHandler}, #{moneyCol})</insert><select id="selectAllDemoTypes" resultMap="DemoTypeResultMap">SELECT * FROM demo_types</select><update id="updateDemoType" parameterType="com.example.pgstudy.DemoType">UPDATE demo_types SETint_col=#{intCol}, bigint_col=#{bigintCol}, smallint_col=#{smallintCol}, serial_col=#{serialCol},decimal_col=#{decimalCol}, numeric_col=#{numericCol}, real_col=#{realCol}, double_col=#{doubleCol},char_col=#{charCol}, varchar_col=#{varcharCol}, text_col=#{textCol}, bool_col=#{boolCol},date_col=#{dateCol}, time_col=#{timeCol}, timestamp_col=#{timestampCol}, interval_col=#{intervalCol, typeHandler=com.example.pgstudy.IntervalTypeHandler},bytea_col=#{byteaCol}, json_col=#{jsonCol, typeHandler=com.example.pgstudy.JsonTypeHandler}, jsonb_col=#{jsonbCol, typeHandler=com.example.pgstudy.JsonbTypeHandler}, uuid_col=#{uuidCol},int_array_col=#{intArrayCol, jdbcType=ARRAY}, text_array_col=#{textArrayCol, jdbcType=ARRAY},inet_col=#{inetCol, typeHandler=com.example.pgstudy.PostgreSQLTypeHandler}, cidr_col=#{cidrCol, typeHandler=com.example.pgstudy.PostgreSQLTypeHandler}, macaddr_col=#{macaddrCol, typeHandler=com.example.pgstudy.PostgreSQLTypeHandler}, point_col=#{pointCol, typeHandler=com.example.pgstudy.PostgreSQLTypeHandler},line_col=#{lineCol, typeHandler=com.example.pgstudy.PostgreSQLTypeHandler}, lseg_col=#{lsegCol, typeHandler=com.example.pgstudy.PostgreSQLTypeHandler}, box_col=#{boxCol, typeHandler=com.example.pgstudy.PostgreSQLTypeHandler}, path_col=#{pathCol, typeHandler=com.example.pgstudy.PostgreSQLTypeHandler},polygon_col=#{polygonCol, typeHandler=com.example.pgstudy.PostgreSQLTypeHandler}, circle_col=#{circleCol, typeHandler=com.example.pgstudy.PostgreSQLTypeHandler}, xml_col=#{xmlCol, typeHandler=com.example.pgstudy.PostgreSQLTypeHandler}, money_col=#{moneyCol}WHERE id=#{id}</update><delete id="deleteDemoTypeById" parameterType="int">DELETE FROM demo_types WHERE id=#{id}</delete>
</mapper> 

UserMapper.xml(不是必须的)

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.pgstudy.UserMapper"><resultMap id="UserResultMap" type="com.example.pgstudy.User"><id property="id" column="id" /><result property="name" column="name" /><result property="age" column="age" /></resultMap><insert id="insertUser" parameterType="com.example.pgstudy.User">INSERT INTO users (name, age) VALUES (#{name}, #{age})</insert><select id="selectAllUsers" resultMap="UserResultMap">SELECT * FROM users</select><update id="updateUser" parameterType="com.example.pgstudy.User">UPDATE users SET age = #{age} WHERE name = #{name}</update><delete id="deleteUserByName" parameterType="string">DELETE FROM users WHERE name = #{name}</delete>
</mapper> 

DemoTypeMapper

package com.example.pgstudy;import java.util.List;public interface DemoTypeMapper {void insertDemoType(DemoType demoType);List<DemoType> selectAllDemoTypes();void updateDemoType(DemoType demoType);void deleteDemoTypeById(Integer id);
} 

UserMapper

package com.example.pgstudy;import java.util.List;public interface UserMapper {void insertUser(User user);List<User> selectAllUsers();void updateUser(User user);void deleteUserByName(String name);
} 

PostgreSQLTypeHandler

package com.example.pgstudy;import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedTypes;
import org.postgresql.util.PGobject;import java.sql.*;@MappedTypes(String.class)
public class PostgreSQLTypeHandler extends BaseTypeHandler<String> {private String pgTypeName;public PostgreSQLTypeHandler() {// 默认构造函数}public PostgreSQLTypeHandler(String pgTypeName) {this.pgTypeName = pgTypeName;}@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {PGobject pgObject = new PGobject();// 根据参数自动识别类型或使用指定类型if (pgTypeName != null) {pgObject.setType(pgTypeName);} else {// 改进的自动识别逻辑if (parameter.matches("\\d+\\.\\d+\\.\\d+\\.\\d+/\\d+")) {pgObject.setType("cidr");  } else if (parameter.matches("\\d+\\.\\d+\\.\\d+\\.\\d+")) {pgObject.setType("inet");} else if (parameter.matches("[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}")) {pgObject.setType("macaddr");} else if (parameter.startsWith("(") && parameter.endsWith(")") && parameter.contains(",") && !parameter.contains("),(")) {pgObject.setType("point");} else if (parameter.startsWith("{") && parameter.endsWith("}") && parameter.contains(",")) {pgObject.setType("line");} else if (parameter.startsWith("[(") && parameter.endsWith(")]")) {pgObject.setType("lseg");} else if (parameter.startsWith("((") && parameter.endsWith("))")) {// 区分 box, path, polygonString content = parameter.substring(2, parameter.length() - 2);String[] parts = content.split("\\),\\(");if (parts.length == 2) {pgObject.setType("box");  // box has exactly 2 points} else if (parts.length > 2) {// 假设超过2个点的是path,闭合的多边形用不同格式pgObject.setType("path");} else {pgObject.setType("box");  // fallback}} else if (parameter.matches("\\([0-9\\.\\-]+,[0-9\\.\\-]+\\),\\([0-9\\.\\-]+,[0-9\\.\\-]+\\)")) {// PostgreSQL box类型的输出格式: (x1,y1),(x2,y2)pgObject.setType("box");} else if (parameter.startsWith("<(") && parameter.endsWith(">")) {pgObject.setType("circle");} else if (isXmlFormat(parameter)) {pgObject.setType("xml");} else {// 为了安全起见,让PostgreSQL自己处理类型转换pgObject.setType("text");}}pgObject.setValue(parameter);ps.setObject(i, pgObject);}/*** 判断字符串是否为XML格式*/private boolean isXmlFormat(String str) {if (str == null || str.trim().isEmpty()) {return false;}String trimmed = str.trim();// 基本XML格式检查:以<开始,以>结束,包含标签if (trimmed.startsWith("<") && trimmed.endsWith(">")) {// 进一步检查是否包含XML标签结构if (trimmed.contains("</") || trimmed.startsWith("<?xml") || (trimmed.indexOf('>') > 0 && trimmed.indexOf('>') < trimmed.length() - 1)) {return true;}}return false;}@Overridepublic String getNullableResult(ResultSet rs, String columnName) throws SQLException {Object obj = rs.getObject(columnName);return convertToString(obj);}@Overridepublic String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {Object obj = rs.getObject(columnIndex);return convertToString(obj);}@Overridepublic String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {Object obj = cs.getObject(columnIndex);return convertToString(obj);}/*** 将数据库对象转换为字符串,特别处理XML类型*/private String convertToString(Object obj) throws SQLException {if (obj == null) {return null;}// 检查是否是PostgreSQL XML类型if (obj instanceof SQLXML) {SQLXML xmlObj = (SQLXML) obj;return xmlObj.getString();}return obj.toString();}
} 

IntegerArrayTypeHandler

package com.example.pgstudy;import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedTypes;
import org.postgresql.util.PGobject;import java.sql.*;@MappedTypes(Integer[].class)
public class IntegerArrayTypeHandler extends BaseTypeHandler<Integer[]> {@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, Integer[] parameter, JdbcType jdbcType) throws SQLException {StringBuilder str = new StringBuilder();str.append("{");for (int j = 0; j < parameter.length; j++) {str.append(parameter[j]);if (j < parameter.length - 1) {str.append(",");}}str.append("}");PGobject pgObject = new PGobject();pgObject.setType("int4[]");pgObject.setValue(str.toString());ps.setObject(i, pgObject);}@Overridepublic Integer[] getNullableResult(ResultSet rs, String columnName) throws SQLException {Array array = rs.getArray(columnName);if (array == null) {return null;}return (Integer[]) array.getArray();}@Overridepublic Integer[] getNullableResult(ResultSet rs, int columnIndex) throws SQLException {Array array = rs.getArray(columnIndex);if (array == null) {return null;}return (Integer[]) array.getArray();}@Overridepublic Integer[] getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {Array array = cs.getArray(columnIndex);if (array == null) {return null;}return (Integer[]) array.getArray();}
} 

目录结构
在这里插入图片描述

DemoType类

package com.example.pgstudy;import lombok.Data;import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.time.Duration;
import java.util.UUID;@Data
public class DemoType {private Integer id;private Integer intCol;private Long bigintCol;private Short smallintCol;private Integer serialCol;private BigDecimal decimalCol;private BigDecimal numericCol;private Float realCol;private Double doubleCol;private String charCol;private String varcharCol;private String textCol;private Boolean boolCol;private Date dateCol;private Time timeCol;private Timestamp timestampCol;private String intervalCol; // interval建议用String或PGIntervalprivate byte[] byteaCol;private String jsonCol;private String jsonbCol;private UUID uuidCol;private Integer[] intArrayCol;private String[] textArrayCol;private String inetCol;private String cidrCol;private String macaddrCol;private String pointCol;private String lineCol;private String lsegCol;private String boxCol;private String pathCol;private String polygonCol;private String circleCol;private String xmlCol;private BigDecimal moneyCol;
} 

执行入口

package com.example.pgstudy;import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;public class Main {public static void main(String[] args) throws IOException, SQLException {initTable();// MyBatis配置String resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);try (SqlSession session = sqlSessionFactory.openSession(true)) { // 自动提交UserMapper mapper = session.getMapper(UserMapper.class);// 插入数据User user1 = new User();user1.setName("张三");user1.setAge(20);mapper.insertUser(user1);System.out.println("插入成功");// 查询数据List<User> users = mapper.selectAllUsers();System.out.println("查询结果:");for (User u : users) {System.out.println("ID: " + u.getId() + ", Name: " + u.getName() + ", Age: " + u.getAge());}// 更新数据user1.setAge(25);mapper.updateUser(user1);System.out.println("更新成功");// 删除数据mapper.deleteUserByName("张三");System.out.println("删除成功");// 测试所有类型的插入、查询、更新、删除DemoTypeMapper demoTypeMapper = session.getMapper(DemoTypeMapper.class);DemoType demo = new DemoType();demo.setIntCol(1);demo.setBigintCol(1234567890123L);demo.setSmallintCol((short)2);demo.setSerialCol(10);demo.setDecimalCol(new java.math.BigDecimal("123.45"));demo.setNumericCol(new java.math.BigDecimal("678.90"));demo.setRealCol(3.14f);demo.setDoubleCol(2.718);demo.setCharCol("abc");demo.setVarcharCol("hello world");demo.setTextCol("这是一段文本");demo.setBoolCol(true);demo.setDateCol(java.sql.Date.valueOf("2024-06-01"));demo.setTimeCol(java.sql.Time.valueOf("12:34:56"));demo.setTimestampCol(java.sql.Timestamp.valueOf("2024-06-01 12:34:56"));demo.setIntervalCol("1 day 2 hours");demo.setByteaCol("hello bytea".getBytes());demo.setJsonCol("{\"a\":1,\"b\":2}");demo.setJsonbCol("{\"x\":true}");demo.setUuidCol(java.util.UUID.randomUUID());demo.setIntArrayCol(new Integer[]{1,2,3});demo.setTextArrayCol(new String[]{"a","b","c"});demo.setInetCol("192.168.1.1");demo.setCidrCol("192.168.100.128/25");demo.setMacaddrCol("08:00:2b:01:02:03");demo.setPointCol("(1,2)");demo.setLineCol("{1,2,3}");demo.setLsegCol("[(0,0),(1,1)]");demo.setBoxCol("((0,0),(1,1))");demo.setPathCol("((1,1),(2,2),(3,3))");demo.setPolygonCol("((0,0),(1,1),(1,0))");demo.setCircleCol("<(0,0),2>");demo.setXmlCol("<root><a>1</a></root>");demo.setMoneyCol(new java.math.BigDecimal("100.50"));demoTypeMapper.insertDemoType(demo);System.out.println("插入demo_types成功");// 查询var demoList = demoTypeMapper.selectAllDemoTypes();System.out.println("查询demo_types结果:");for (DemoType d : demoList) {System.out.println("ID: " + d.getId() + ", intCol: " + d.getIntCol() + ", jsonCol: " + d.getJsonCol());System.out.println("box_col: '" + d.getBoxCol() + "', path_col: '" + d.getPathCol() + "'");System.out.println("xml_col: '" + d.getXmlCol() + "'");}// 更新if (!demoList.isEmpty()) {DemoType first = demoList.get(0);first.setTextCol("更新后的文本");System.out.println("准备更新,box_col值为: '" + first.getBoxCol() + "'");demoTypeMapper.updateDemoType(first);System.out.println("更新demo_types成功");}// 删除if (!demoList.isEmpty()) {demoTypeMapper.deleteDemoTypeById(demoList.get(0).getId());System.out.println("删除demo_types成功");}}}public static void initTable() throws IOException, SQLException {// 1. 先确保表存在String url = "jdbc:postgresql://localhost:5432/root";String user = "root";String password = "123456";try (Connection conn = DriverManager.getConnection(url, user, password)) {String createTable = "CREATE TABLE IF NOT EXISTS users (id SERIAL PRIMARY KEY, name VARCHAR(100), age INT)";try (Statement stmt = conn.createStatement()) {stmt.execute(createTable);}}// 创建demo_types表(如已存在可忽略)try (Connection conn = DriverManager.getConnection(url, user, password)) {String createDemoTypesTable = """CREATE TABLE IF NOT EXISTS demo_types (id SERIAL PRIMARY KEY,int_col INTEGER,bigint_col BIGINT,smallint_col SMALLINT,serial_col SERIAL,decimal_col DECIMAL(10,2),numeric_col NUMERIC(10,2),real_col REAL,double_col DOUBLE PRECISION,char_col CHAR(10),varchar_col VARCHAR(50),text_col TEXT,bool_col BOOLEAN,date_col DATE,time_col TIME,timestamp_col TIMESTAMP,interval_col INTERVAL,bytea_col BYTEA,json_col JSON,jsonb_col JSONB,uuid_col UUID,int_array_col INTEGER[],text_array_col TEXT[],inet_col INET,cidr_col CIDR,macaddr_col MACADDR,point_col POINT,line_col LINE,lseg_col LSEG,box_col BOX,path_col PATH,polygon_col POLYGON,circle_col CIRCLE,xml_col XML,money_col MONEY);""";try (Statement stmt = conn.createStatement()) {stmt.execute(createDemoTypesTable);}}}
} 

执行流程分析

1.使用initTable()初始化创建所需要的表。
2.利用mybatis执行数据增删改查操作。

我们在上面展示代码中只挑选出了两个比较有代表性的TypeHandler,分别是IntegerArrayTypeHandler,PostgreSQLTypeHandler,其余的类型处理器逻辑都基本相同。其中IntegerArrayTypeHandler属于操作数组类型,PostgreSQLTypeHandler属于操作丰富的postgresql类型。

如果是实现自己的类型处理器,就需要集成BaseTypeHandler<T>类。其中,T表示要设置的参数类型。对于IntegerArrayTypeHandler处理器来说,T表示Integer[];对于PostgreSQLTypeHandler处理器来说,T表示String。实现该处理器之后,重写对应的setXXX/getXXX方法即可。
setXXX表示往数据库写入数据,getXXX表示从数据库中读取数据。

在这里插入图片描述
如上图所示,当我们实现好对应的处理器,并把处理器放在mybatis核心配置文件/mapper文件中后,就可以调用mapper的接口进行操作数据库了。
demo.setIntArrayCol表示设置数组数据。
demo.setInetCol、demo.setCidrCol等方法表示设置postgresql的其他类型数据。可以发现对于postgresql内置的好多复杂类型,基本都是通过String类型传值设置。

我们此时可能有两个疑问:

  • 问题一:为什么对于postgersql内置的类型是通过String类型值传入的?
  • 问题二:对于代码中使用string值与postgresql的内置类型对应,这种操作有什么优势?
  • 问题三:为什么在mybatis核心配置文件,或者是mapper文件中配置类处理器都可以?

下面我们一一解答上面三个问题。

问题

问题一:为什么对于postgersql内置的类型是通过String类型值传入的?
首先,我们应该知道java代码和postgresql是对应两种不同的语言。即一个是编程语言,一个是sql语言。对于两个不同的语言,其表示的类型肯定有所差异,也有共同点,共同点就是都有字符串,差异就不用讲了,因为两种不同的语言,差异太多了。
不管是什么语言,都有字符串,所以是不是可以理解为字符串是一种适配器?通过字符串可以把不同的语言都给联系起来,进行信息/数据交换。

问题二:对于代码中使用string值与postgresql的内置类型对应,这种操作有什么优势?
我们可以发现一个有意思的点,对于PostgreSQLTypeHandler的类型参数都为string类型,这个处理器的核心只是设置pgTypeName,即参数类型。那我们此时可能有个疑问,代码中都定义string类型表示数据,那在代码层面上postgresql丰富的数据类型貌似并没有什么优势,那postgresql的优势在哪?
优势主要包括以下几点:

  • 数据完整性: 自动验证和强制类型约束
  • 性能优化: 专门的存储格式和索引支持
  • 功能丰富: 特定类型的操作符和函数
  • 开发效率: 减少应用层验证代码
  • 维护性: 统一的数据处理标准

1.数据完整性验证

-- 使用VARCHAR
INSERT INTO table1 (ip_address) VALUES ('256.256.256.256'); -- 错误的IP地址也能插入
-- 使用INET
INSERT INTO table1 (ip_address) VALUES ('256.256.256.256'); -- PostgreSQL直接报错,拒绝插入

2.索引和查询优化

-- INET类型支持网段查询
SELECT * FROM networks WHERE ip_address << '192.168.1.0/24';-- JSONB支持特定索引
CREATE INDEX idx_json ON table1 USING GIN (json_data);-- 几何类型支持空间查询
SELECT * FROM locations WHERE point_col <-> POINT(0,0) < 10;

3.特殊操作和函数支持
网络类型

-- PostgreSQL INET类型
SELECT ip_address & '255.255.255.0'::inet;  -- 网络掩码运算
SELECT ip_address << '192.168.1.0/24'::inet;  -- 子网判断-- VARCHAR类型
-- 需要复杂的字符串处理和自定义函数

几何类型

-- PostgreSQL几何类型
SELECT box_col @> point '(1,1)';  -- 点是否在框内
SELECT circle_col && box_col;     -- 圆和框是否相交-- VARCHAR类型
-- 需要解析字符串并编写复杂的数学计算

JSON/JSONB类型

-- PostgreSQL JSONB
SELECT json_data->>'name' FROM table1;  -- JSON字段提取
SELECT * FROM table1 WHERE json_data @> '{"status": "active"}';  -- JSON包含查询-- VARCHAR类型
-- 需要手动解析JSON字符串,无法使用索引优化

4.数据一致性和业务规则
类型约束

-- PostgreSQL特定类型
CREATE TABLE networks (ip INET,subnet CIDR,mac MACADDR
);-- 自动强制类型检查:
INSERT INTO networks VALUES ('invalid-ip', 'invalid-subnet', 'invalid-mac'); -- 直接报错-- VARCHAR类型
CREATE TABLE networks (ip VARCHAR(15),subnet VARCHAR(18),mac VARCHAR(17)
);
-- 需要在应用层编写验证逻辑

问题三:为什么在mybatis核心配置文件,或者是mapper文件中配置类处理器都可以?
这个问题很好理解,原因就是如果在mybatis和核心配置文件里配置类处理器,那么对于该处理器在定义时会定义注解:
@MappedTypes(Integer[].class),即只要遇到类型Integer[],就会应用该处理器,即在mybatis核心配置文件中定义的类型处理器全局生效,只要遇到符合该处理器定义的类型时就会应用该处理器。
如果是在mapper文件中定义的处理器,即只会针对当前字段应用该处理器,可以更精细化的控制。如果在mybatis核心配置文件中配置PostgreSQLTypeHandler处理器,因为该处理器处理的类型为String,那么当遇到String类型时就会应用该处理器,可以对于String类型的json串,需要应用JsonTypeHandler,所以如果定义在mybatis的核心配置文件中就会有冲突,因此对于可能会冲突的类型处理器,定义在mapper文件中更合理。

[源码git仓库地址]

相关文章:

  • ros创建工作空间配置运行状态机
  • go语言学习 第8章:切片
  • spring中的@RabbitListener注解详解
  • 腾讯 ovCompose 跨平台框架发布,几年后还会有人用吗?
  • SSM spring Bean实例化
  • matlab 2024a ​工具箱Aerospsce Toolbox报错​
  • 【力扣链表篇】19.删除链表的倒数第N个节点
  • 2025年06月07日Github流行趋势
  • Vue3 项目的基本架构解读
  • 2012-2023年 上市公司-知识重组创造、知识重组再利用数据-社科经管实证数据
  • 《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析LLP (二)
  • 备份还原打印机驱动
  • 数据库管理与高可用-MySQL高可用
  • Java基于SpringBoot的校园闲置物品交易系统,附源码+文档说明
  • 以智能管理为基础,楼宇自控打造建筑碳中和新路径
  • WebFuture 系统升级提示外键约束的问题处理
  • WebWorker-----高频面试题(浏览器篇)
  • 30、memory-order-relaxed
  • 从零开始开发纯血鸿蒙应用之网络检测
  • A Execllent Software Project Review and Solutions
  • 美女做那种视频网站有哪些/中国舆情在线
  • 衡阳网站建设公司/南京百度推广优化
  • 建设充值网站多钱/西安百度框架户
  • 番禺做网站设计/优秀的品牌策划案例
  • 新余网站制作/十大免费软文推广平台
  • 广西网站建设营销公司/百度极速版推广