Spring Boot+MyBatis对接PostgreSQL jsonb字段报错的解决方法
Spring Boot + MyBatis 处理PostgreSQL jsonb字段的正确姿势
你遇到的错误核心原因是:数据库tags字段已改为jsonb类型,但MyBatis默认将字符串参数以varchar类型传入,PostgreSQL不允许这种隐式类型转换,因此抛出类型不匹配的报错。以下是几种可行的解决方式:
方案一:SQL语句中显式转换(快速临时解决)
直接在Mapper XML的SQL语句里,给tags参数添加PostgreSQL的类型转换语法,把传入的字符串转为jsonb:
<update id="updateByPrimaryKeySelective" parameterType="com.dolphin.soa.post.entity.RssSubSource"> UPDATE rss_sub_source SET <!-- 其他字段省略 --> tags = #{tags}::jsonb, <!-- 其他字段省略 --> WHERE id = #{id} </update>
这种写法兼容tags为null的场景,PostgreSQL会自动将null转为jsonb类型的null。
方案二:自定义TypeHandler(优雅持久化方案)
通过自定义MyBatis类型处理器,实现Java对象与PostgreSQL jsonb类型的自动转换,无需每次写SQL都手动转换。
实体类字段定义
实体类的tags字段可以定义为String、Map<String, Object>或自定义POJO:public class RssSubSource { // 其他字段省略 private String tags; // 或 private Map<String, Object> tags; // getter/setter }编写自定义TypeHandler
import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import org.postgresql.util.PGobject; import com.fasterxml.jackson.databind.ObjectMapper; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class JsonbTypeHandler extends BaseTypeHandler<Object> { private static final PGobject JSONB_OBJECT = new PGobject(); private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); @Override public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException { JSONB_OBJECT.setType("jsonb"); // 如果是Map或POJO,转成JSON字符串;如果是String直接用 JSONB_OBJECT.setValue(OBJECT_MAPPER.writeValueAsString(parameter)); ps.setObject(i, JSONB_OBJECT); } @Override public Object getNullableResult(ResultSet rs, String columnName) throws SQLException { String json = rs.getString(columnName); try { // 根据实体类字段类型选择转换方式,这里以Map为例 return OBJECT_MAPPER.readValue(json, Map.class); // 如果是String直接return json; } catch (Exception e) { throw new SQLException("解析jsonb字段失败", e); } } @Override public Object getNullableResult(ResultSet rs, int columnIndex) throws SQLException { return getNullableResult(rs, rs.getMetaData().getColumnName(columnIndex)); } @Override public Object getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { String json = cs.getString(columnIndex); try { return OBJECT_MAPPER.readValue(json, Map.class); } catch (Exception e) { throw new SQLException("解析jsonb字段失败", e); } } }注册TypeHandler
- 方式一:在Mapper XML中指定单个字段使用该处理器:
<update id="updateByPrimaryKeySelective" parameterType="com.dolphin.soa.post.entity.RssSubSource"> UPDATE rss_sub_source SET <!-- 其他字段省略 --> tags = #{tags, typeHandler=com.dolphin.soa.post.handler.JsonbTypeHandler}, <!-- 其他字段省略 --> WHERE id = #{id} </update> - 方式二:全局注册,让所有匹配类型的字段自动使用该处理器(Spring Boot可在配置文件中指定):
mybatis: type-handlers-package: com.dolphin.soa.post.handler
- 方式一:在Mapper XML中指定单个字段使用该处理器:
方案三:MyBatis-Plus内置支持(若项目使用MyBatis-Plus)
如果项目依赖了MyBatis-Plus,直接利用其内置的JacksonTypeHandler即可实现自动转换:
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; import java.util.Map; public class RssSubSource { // 其他字段省略 @TableField(typeHandler = JacksonTypeHandler.class) private Map<String, Object> tags; // 若用自定义POJO,直接替换Map为POJO类即可 // @TableField(typeHandler = JacksonTypeHandler.class) // private TagsDTO tags; // getter/setter }
MyBatis-Plus会自动完成Java对象与jsonb类型的双向转换,无需额外编写SQL或处理器。
内容的提问来源于stack exchange,提问作者Dolphin
相关产品推荐
相关产品推荐

