跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava

SpringBoot+MyBatis Plus+PostgreSQL 整合常用数据类型(json、array)操作

介绍如何在 SpringBoot 结合 MyBatis Plus 与 PostgreSQL 中处理常用数据类型。重点解决 JSON 和数组类型在 Java 实体与数据库间的映射问题,通过自定义 TypeHandler 实现 FastJSON 对象与 PostgreSQL JSON/JSONB 类型的转换,以及 List/Array 与 PostgreSQL 数组类型的映射。包含项目搭建、配置文件、TypeHandler 实现及接口测试示例。

Kubernet发布于 2026/3/29更新于 2026/5/2723 浏览

前言

  1. SpringBoot+MyBatis Plus+PostgreSQL 整合常用数据类型的使用基本上和 MySQL 使用差不多。
  2. 但是 PGSQL 中 Json 类型和数组类型并不能直接像 MySQL 那样,例如 MySQL 中 JSON 可以使用 String 类型存储和查询。
  3. 但是在 PGSQL 中,如果使用 String 接收数组类型,当插入数据的时候就会报错:
column "xxx" is of type json but expression is of type character varying 
  1. 本文核心展示在整合使用时的常用数据类型使用以及 json 和数组的接口与插入等,并且可以根据解决方案拓展自定义类型。

注意:默认大家已经掌握 SpringBoot、MyBatis Plus、PostgreSQL 的基础使用。

项目搭建

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.4.5</version>
        <relativePath/>
    </parent>
    <groupId>com.codecoord</groupId>
    <artifactId>springboot-postgresql</artifactId>
    <version>1.0</version>
    <properties>
        <java.version>21</java.version>
        <maven.compiler.source>${java.version}</maven.compiler.source>
        <maven.compiler.target>${java.version}</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>2.0.57</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
            <version>3.5.14</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.27</version>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>42.7.8</version>
        </dependency>
    </dependencies>
    <repositories>
        <repository>
            <id>aliyun</id>
            <name>aliyun</name>
            <url>https://maven.aliyun.com/repository/public</url>
            <releases><enabled>true</enabled></releases>
            <snapshots><enabled>true</enabled></snapshots>
        </repository>
    </repositories>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
数据库脚本
DROP TABLE IF EXISTS data_type;
CREATE TABLE data_type (
    id bigserial PRIMARY KEY,
    -- 数值类型
    tiny_int_col smallint,
    small_int_col smallint,
    integer_col integer,
    big_int_col bigint,
    decimal_col decimal(10,2),
    numeric_col numeric(15,4),
    real_col real,
    double_precision_col doubleprecision,
    -- 字符串类型
    char_col char(10),
    varchar_col varchar(255),
    text_col text,
    -- 日期时间类型
    date_col date,
    time_col time,
    timestamp_col timestamp,
    timestamp_tz_col timestamptz,
    -- 布尔类型
    boolean_col boolean,
    -- JSON 类型
    json_col json,
    jsonb_col jsonb,
    -- 数组类型
    array_col int[],
    decimal_array_col decimal(10,2)[]
);
实体类

指定类型处理器,如果不指定就需要在配置文件中配置处理器扫描路径。

package com.codecoord.postgresql.domain;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.codecoord.postgresql.handler.ArrayTypeHandler;
import com.codecoord.postgresql.handler.JsonTypeHandler;
import lombok.Data;

import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.util.List;

@Data
@TableName("public.data_type")
public class DataType implements Serializable {
    private static final long serialVersionUID = 1L;

    @TableId(type = IdType.AUTO)
    private Long id;

    // 数值类型
    private Short tinyIntCol;
    private Short smallIntCol;
    private Integer integerCol;
    private Long bigIntCol;
    private BigDecimal decimalCol;
    private BigDecimal numericCol;
    private Float realCol;
    private Double doublePrecisionCol;

    // 字符串类型
    private String charCol;
    private String varcharCol;
    private String textCol;

    // 日期时间类型
    private LocalDate dateCol;
    private LocalTime timeCol;
    private LocalDateTime timestampCol;
    private OffsetDateTime timestampTzCol;

    // 布尔类型
    private Boolean booleanCol;

    // JSON 类型,需要指定对应的 json 处理器
    // @TableField(typeHandler = JsonTypeHandler.class)
    private JSONObject jsonCol;
    // @TableField(typeHandler = JsonTypeHandler.class)
    private JSONObject jsonbCol;

    // 数组类型
    // @TableField(typeHandler = ArrayTypeHandler.class)
    // private Integer[] arrayCol;
    private List<Integer> arrayCol;
    private BigDecimal[] decimalArrayCol;
}
配置文件

这里最重要的就是指定 TypeHandler 所在的包路径。 type-handlers-package: com.codecoord.postgresql.handler

server:
  port: 80
spring:
  datasource:
    url: jdbc:postgresql://ip:5432/databasename
    driver-class-name: org.postgresql.Driver
    username: username
    password: password
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      initial-size: 1
      min-idle: 1
      max-active: 10
      max-wait: 60000
      time-between-eviction-runs-millis: 60000
      min-evictable-idle-time-millis: 300000
      validation-query: SELECT 1
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    map-underscore-to-camel-case: true
  global-config:
    db-config:
      logic-delete-value: 1
      logic-not-delete-value: 0
      id-type: auto
      banner: false
  # 指定类型处理包
  type-handlers-package: com.codecoord.postgresql.handler
类型处理器

解决 Java 和数据库数据类型之间的匹配,实际上就是通过 TypeHandler 来告诉 MyBatis/MP 这个数据应该怎么处理。基本上所有的类型匹配都可以采用这种方式解决。

JSON 类型处理器

此处使用 fastjson 的 JSONObject 类型来作为 JSON 载体和 MyBatis/MP 类型处理器之间的载体。也可以自定义一个类型,比如下面的处理器只支持 JSONObject 和 Map 数据类型,如果有自己的新类型,按照下面的代码示例加入即可。

package com.codecoord.postgresql.handler;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson2.JSON;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;

import java.sql.*;
import java.util.Map;

@MappedJdbcTypes(JdbcType.OTHER)
@MappedTypes({JSONObject.class, com.alibaba.fastjson2.JSONObject.class, Map.class})
@SuppressWarnings("unused")
public class JsonTypeHandler extends BaseTypeHandler<Object> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {
        ps.setObject(i, JSON.toJSONString(parameter), Types.OTHER);
    }

    @Override
    public Object getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return JSON.parse(rs.getString(columnName));
    }

    @Override
    public Object getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return JSON.parse(rs.getString(columnIndex));
    }

    @Override
    public Object getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return JSON.parse(cs.getString(columnIndex));
    }
}
Array 类型处理器
package com.codecoord.postgresql.handler;

import com.baomidou.mybatisplus.core.toolkit.ArrayUtils;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.sql.*;
import java.util.*;

@MappedJdbcTypes(JdbcType.ARRAY)
@SuppressWarnings("unused")
@MappedTypes({List.class, int[].class, long[].class, short[].class, Integer[].class, Short[].class, Long[].class, BigDecimal[].class})
public class ArrayTypeHandler extends BaseTypeHandler<Object> {

    private boolean isListType;
    private static final Map<Class<?>, String> TYPES = Map.of(
            Short.class, "smallint",
            Integer.class, "integer",
            Long.class, "bigint",
            short.class, "smallint",
            int.class, "integer",
            long.class, "bigint",
            String.class, "text",
            BigDecimal.class, "numeric"
    );

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {
        Object[] array;
        if (parameter instanceof List) {
            array = ((List<?>) parameter).toArray();
        } else if (parameter.getClass().isArray()) {
            array = (Object[]) parameter;
        } else {
            throw new IllegalArgumentException("PG array type only supports Array or List");
        }
        Array pgArray = ps.getConnection().createArrayOf(resolvePgType(ps, parameter), array);
        ps.setArray(i, pgArray);
    }

    private String resolvePgType(PreparedStatement ps, Object parameter) {
        isListType = false;
        Class<?> componentType;
        if (parameter instanceof List<?> list) {
            componentType = list.isEmpty() ? String.class : list.getFirst().getClass();
            isListType = true;
        } else {
            componentType = parameter.getClass().getComponentType();
        }
        String pgType = TYPES.get(componentType);
        if (StringUtils.hasLength(pgType)) {
            return pgType;
        }
        throw new IllegalArgumentException("Unsupported PG array element type " + componentType);
    }

    @Override
    public Object getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return toJavaArray(rs.getArray(columnName));
    }

    @Override
    public Object getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return toJavaArray(rs.getArray(columnIndex));
    }

    @Override
    public Object getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return toJavaArray(cs.getArray(columnIndex));
    }

    private Object toJavaArray(Array array) throws SQLException {
        if (Objects.isNull(array)) {
            return null;
        }
        Object[] objects = (Object[]) array.getArray();
        if (!isListType || ArrayUtils.isEmpty(objects)) {
            return array.getArray();
        }
        return new ArrayList<>(Arrays.asList(objects));
    }
}
接口代码
package com.codecoord.postgresql.controller;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson2.JSON;
import com.codecoord.postgresql.domain.DataType;
import com.codecoord.postgresql.service.DataTypeService;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.Objects;

@RestController
@RequestMapping("/sql")
public class PostgreSqlController {

    @Resource
    private DataTypeService dataTypeService;

    @RequestMapping("/autoincrement")
    public DataType autoincrement(@RequestParam(value = "id", required = false) Long id) {
        DataType dataType = new DataType();
        // 做自定义赋值,默认是自增
        if (Objects.nonNull(id)) {
            dataType.setId(id);
        }
        // 注意:如果指定了插入 Id,但是自增序列是从 1 开始的,则有可能导致数据库主键冲突
        // 当自增时,则实体类必须要有一个字段有值,否则将会导致构建的 SQL value 为空,导致问题
        dataType.setVarcharCol("测试自增字段");
        dataTypeService.save(dataType);
        return dataTypeService.getById(dataType.getId());
    }

    @RequestMapping("/numeric")
    public DataType numeric() {
        DataType dataType = new DataType();
        dataType.setSmallIntCol((short) 123);
        dataType.setIntegerCol(456);
        dataType.setBigIntCol(789L);
        dataType.setDecimalCol(new BigDecimal("123.45"));
        dataType.setNumericCol(new BigDecimal("678.90"));
        dataType.setRealCol(12.34f);
        dataType.setDoublePrecisionCol(56.78);
        dataTypeService.save(dataType);
        return dataTypeService.getById(dataType.getId());
    }

    @RequestMapping("/string")
    public DataType string() {
        DataType dataType = new DataType();
        dataType.setVarcharCol("测试字符串");
        dataType.setCharCol("A");
        dataType.setTextCol("长文本内容");
        dataTypeService.save(dataType);
        return dataTypeService.getById(dataType.getId());
    }

    @RequestMapping("/date")
    public DataType date() {
        DataType dataType = new DataType();
        dataType.setDateCol(LocalDate.parse("2025-12-29"));
        dataType.setTimeCol(LocalTime.parse("15:30:45"));
        dataType.setTimestampCol(LocalDateTime.parse("2025-12-29T15:30:45"));
        dataType.setTimestampTzCol(OffsetDateTime.now());
        dataTypeService.save(dataType);
        return dataTypeService.getById(dataType.getId());
    }

    @RequestMapping("/boolean")
    public DataType booleanType() {
        DataType dataType = new DataType();
        dataType.setBooleanCol(true);
        dataTypeService.save(dataType);
        return dataTypeService.getById(dataType.getId());
    }

    @RequestMapping("/json")
    public DataType json() {
        DataType dataType = new DataType();
        // column "json_col" is of type json but expression is of type character varying
        // 直接默认赋值是不支持 JSON 格式的
        // 1. 需要自定义类型处理 FastjsonTypeHandler
        dataType.setJsonCol(JSON.parseObject("{"name":"test","value":"[]","array":[1,2,3],"nested":{"key":"value"}}"));
        dataType.setJsonbCol(JSON.parseObject("{"title":"sample jsonb","content":"PostgreSQL JSONB data","tags":["json","postgresql","database"],"metadata":{"created":"2025-12-29","version":1.0,"active":true}}"));
        dataTypeService.save(dataType);
        return dataTypeService.getById(dataType.getId());
    }

    @RequestMapping("/array")
    public DataType array() {
        DataType dataType = new DataType();
        // dataType.setArrayCol(new Integer[]{1, 2, 3});
        dataType.setArrayCol(List.of(1, 2, 3, 5));
        dataType.setDecimalArrayCol(new BigDecimal[]{new BigDecimal("123.45"), new BigDecimal("678.90")});
        dataTypeService.save(dataType);
        return dataTypeService.getById(dataType.getId());
    }
}

接口测试

测试 json 类型

  1. 请求接口插入数据
  2. json 数据处理成功

测试数组类型

  1. 插入数据成功
  2. 数组类型接收成功

目录

  1. 前言
  2. 项目搭建
  3. pom.xml
  4. 数据库脚本
  5. 实体类
  6. 配置文件
  7. 指定类型处理包
  8. 类型处理器
  9. JSON 类型处理器
  10. Array 类型处理器
  11. 接口代码
  12. 接口测试
  13. 测试 json 类型
  14. 测试数组类型
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Dify工作流集成TTS:低代码实现语音输出
  • YOLO12 在无人机倾斜摄影中的密集窗户识别实践
  • C++ 红黑树的设计原理与插入实现详解
  • 从 Actix-web 迁移至 Salvo:Rust Web 开发体验对比
  • 前端防抖与节流实战:主流库选择与避坑指南
  • AIGC 插画生成技术解析与 Python 实战
  • C++ 哈希表原理与冲突解决策略
  • HDFS 分布式文件系统存储原理详解:冗余、存取与容错
  • 前端调用 AI 接口全流程实战:从配置到流式响应
  • C++ 模板进阶:非类型参数与特化机制
  • AI 开发实战:基于 MLflow 的实验跟踪指南
  • Vheer:免费免登录的 AI 绘画与视频生成工具
  • STM32 数字万用表 DIY 项目设计与硬件选型
  • WebStorm 下载与安装实战
  • ToDesk、顺网云与海马云运行 DeepSeek 大模型性能对比
  • C++ 搜索引擎项目实战:日志系统与 Server 入口详解
  • 基于管道通信的 Linux 进程池实现与任务分发解析
  • 数据结构:链表核心算法实战解析
  • 基于 OpenClaw 与飞书集成实现 AI 新闻推送机器人
  • Git 远程协作实战:仓库关联、推送与标签管理详解

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online