PG JSONB 对应 Java 字段 + MyBatis-Plus 完整实战

PG JSONB 对应 Java 字段 + MyBatis-Plus 完整实战

一、核心对应关系

PostgreSQL 的 jsonb 字段,在 Java 中推荐 3 种对应类型(优先级从高到低):

  1. 自定义实体类(✅ 最推荐,业务清晰、类型安全)
  2. Map<String, Object>(灵活,无固定 JSON 结构)
  3. String(最简单,手动序列化/反序列化)

二、MyBatis-Plus 核心关键

MP 要实现 Java 对象 ↔ PG jsonb 自动转换,必须使用【类型处理器(TypeHandler)】

  • MP 自带 JacksonTypeHandler(无需自定义,直接用)
  • 实体类必须加 2 个关键注解:
    1. @TableName(autoResultMap = true):开启自动映射
    2. @TableField(typeHandler = JacksonTypeHandler.class, jdbcType = JdbcType.OTHER):绑定 jsonb 处理器

三、完整实战代码

1. 环境依赖(Maven)

xml 复制代码
<!-- MyBatis-Plus -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.3.1</version>
</dependency>
<!-- PostgreSQL驱动 -->
<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <scope>runtime</scope>
</dependency>
<!-- Lombok(简化代码) -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

2. 数据库表(沿用之前的 PG 表)

sql 复制代码
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    user_info JSONB  -- 核心jsonb字段
);

3. Java 实体类(✅ 最优方案:自定义实体)

(1)嵌套 JSON 实体(对应 jsonb 内部结构)
java 复制代码
import lombok.Data;
import java.util.List;

/**
 * 对应 user_info jsonb 字段的结构
 */
@Data
public class UserInfo {
    private Integer age;
    private String gender;
    // 嵌套对象
    private Address address;
    // 数组
    private List<String> hobbies;
}

// 嵌套地址对象
@Data
class Address {
    private String city;
    private String code;
}
(2)主实体类(绑定 TypeHandler)
java 复制代码
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.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import lombok.Data;
import org.apache.ibatis.type.JdbcType;

@Data
// 关键1:开启自动结果映射
@TableName(value = "users", autoResultMap = true)
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;
    
    private String username;

    // 关键2:绑定jsonb类型处理器
    @TableField(
        typeHandler = JacksonTypeHandler.class,
        jdbcType = JdbcType.OTHER
    )
    private UserInfo userInfo; // JSONB → 自定义Java实体
}

4. Mapper 接口(MP 原生接口,无需写 SQL)

java 复制代码
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper extends BaseMapper<User> {
    // 无需手写增删改查,MP 自动实现
}

四、MyBatis-Plus 增删改查样例

1. 新增(直接传 Java 对象,自动转 jsonb 存库)

java 复制代码
@Autowired
private UserMapper userMapper;

// 新增用户(jsonb字段自动序列化)
public void addUser() {
    // 1. 组装jsonb对象
    UserInfo info = new UserInfo();
    info.setAge(26);
    info.setGender("男");
    
    Address address = new Address();
    address.setCity("北京");
    address.setCode("100000");
    info.setAddress(address);
    info.setHobbies(Arrays.asList("篮球", "旅行"));

    // 2. 组装主实体
    User user = new User();
    user.setUsername("zhangsan");
    user.setUserInfo(info);

    // 3. MP新增(自动把UserInfo转成PG jsonb)
    userMapper.insert(user);
}

2. 修改(直接更新 Java 对象,自动覆盖 jsonb)

java 复制代码
// 修改jsonb字段(例:更新城市、年龄)
public void updateUser() {
    // 1. 查询原数据
    User user = userMapper.selectById(1L);
    
    // 2. 修改jsonb内部字段
    UserInfo info = user.getUserInfo();
    info.setAge(27);
    info.getAddress().setCity("深圳");
    user.setUserInfo(info);

    // 3. MP修改(自动转jsonb更新)
    userMapper.updateById(user);
}

3. 删除(和普通字段一致,无特殊处理)

java 复制代码
// 删除(根据id删除,jsonb字段无特殊逻辑)
public void deleteUser() {
    userMapper.deleteById(1L);
}

4. 查询(普通查询 + jsonb 嵌套字段条件查询)

(1)普通查询(自动把 jsonb 转回 Java 对象)
java 复制代码
// 根据id查询,直接拿到UserInfo对象
public void getUser() {
    User user = userMapper.selectById(1L);
    // 直接使用jsonb数据
    System.out.println("年龄:" + user.getUserInfo().getAge());
    System.out.println("城市:" + user.getUserInfo().getAddress().getCity());
}
(2)条件查询(查询 jsonb 内部字段,例:男性、年龄>25)
java 复制代码
// 条件查询:筛选jsonb中gender=男,age>25的用户
public void queryByJsonb() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    // PG原生语法:(user_info->>'age')::int > 25
    wrapper.apply("(user_info->>'age')::int > {0}", 25);
    wrapper.eq("user_info->>'gender'", "男");
    
    List<User> userList = userMapper.selectList(wrapper);
}

五、备用方案:Map<String, Object> 对应 jsonb

如果 JSON 结构不固定,用 Map 替代自定义实体:

java 复制代码
@Data
@TableName(value = "users", autoResultMap = true)
public class User {
    private Long id;
    private String username;

    @TableField(
        typeHandler = JacksonTypeHandler.class,
        jdbcType = JdbcType.OTHER
    )
    private Map<String, Object> userInfo; // JSONB → Map
}

使用方式:

java 复制代码
Map<String, Object> info = new HashMap<>();
info.put("age", 26);
info.put("gender", "男");
user.setUserInfo(info);

六、备用方案:String 对应 jsonb

最简单,无自动转换,手动处理:

java 复制代码
@TableField(jdbcType = JdbcType.OTHER)
private String userInfo; // JSONB → String

使用方式:

java 复制代码
// 手动传JSON字符串
user.setUserInfo("{\"age\":26,\"gender\":\"男\"}");

七、关键注意事项

  1. 必须加 autoResultMap = true,否则查询无法把 jsonb 转回 Java 对象
  2. jdbcType = JdbcType.OTHER:PG jsonb 对应 JDBC 特殊类型
  3. 优先用自定义实体:业务维护性远高于 Map/String
  4. 增删改查完全复用 MP 原生方法,无需手写 JSON 相关 SQL

总结

  1. PG jsonb → Java 最优对应:自定义实体类
  2. MP 核心:JacksonTypeHandler + autoResultMap = true
  3. 增删改查直接用 MP 原生方法,自动完成对象与 jsonb 的转换
  4. 条件查询用 PG 原生 ->>/-> 语法配合 wrapper.apply()
相关推荐
leaves falling2 小时前
C++ string 类:从入门到模拟实现
开发语言·c++
智算菩萨2 小时前
【Tkinter】15 样式与主题深度解析:ttk 主题系统、Style 对象与跨平台样式管理实战
开发语言·python·ui·ai编程·tkinter
不早睡不改名@2 小时前
Netty源码分析---Reactor线程模型深度解析(二)
java·网络·笔记·学习·netty
子非鱼@Itfuture2 小时前
`<T> T execute(...)` 泛型方法 VS `TaskExecutor<T>` 泛型接口对比分析
java·开发语言
2601_949816162 小时前
spring.profiles.active和spring.profiles.include的使用及区别说明
java·后端·spring
weixin_419349793 小时前
Python 项目中生成 requirements.txt 文件
开发语言·python
林恒smileZAZ3 小时前
前端大屏适配方案:rem、vw/vh、scale 到底选哪个?
开发语言·前端·css·css3
疯狂成瘾者3 小时前
接口规范设计:返回体 + 错误码 + 异常处理
java·状态模式
阿Y加油吧3 小时前
LeetCode 二叉搜索树双神题通关!有序数组转平衡 BST + 验证 BST,小白递归一把梭
java·算法·leetcode