springboot+mybatis出现数据库字段值为null时,map里面不显示key

本文详细介绍了如何在MyBatis中进行配置,包括在resource文件下创建mybatis-configuration.xml文件,设置callSettersOnNulls参数,以及在数据库配置类中设置typeAliasesPackage和mapperLocations,最后通过configLocation加载配置文件。

在resource文件下面创建文件mybatis-configuration

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD SQL MAP Config 3.1//EN" 
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <settings>
      <setting name="callSettersOnNulls" value="true"/>
  </settings>
</configuration>

然后在数据库的配置类中

sqlSessionFactoryBean.setTypeAliasesPackage("com.kq.vo");
		sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath:/mapper/*.xml"));
		// 读取本包,子包所有.xml文件
		sqlSessionFactoryBean.setConfigLocation(resolver.getResource("classpath:/mybatis-configuration.xml"));
		return sqlSessionFactoryBean.getObject();

如果实在application中配置时,则直接在application中加

mybatis.configuration.call-setters-on-nulls=true即可

<think> 我们讨论在Spring Boot + MyBatis + PostgreSQL环境中处理`text[]`数组类型和`jsonb`类型字段。 首先,PostgreSQL的数组类型(如`text[]`)和JSON类型(`jsonb`)需要特殊的处理,因为MyBatis本身并直接支持这些类型。 解决方案通常包括: 1. 使用自定义类型处理器(TypeHandler) 2. 在SQL映射中使用PostgreSQL特定的函数和语法 下面分别针对两种类型给出解决方案: ### 一、处理 `text[]` 数组类型 #### 1. 创建自定义类型处理器 `TextArrayTypeHandler` ```java import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import org.postgresql.util.PGobject; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Arrays; import java.util.List; public class TextArrayTypeHandler extends BaseTypeHandler<List<String>> { private static final String ARRAY_TYPE = "text[]"; @Override public void setNonNullParameter(PreparedStatement ps, int i, List<String> parameter, JdbcType jdbcType) throws SQLException { PGobject arrayObject = new PGobject(); arrayObject.setType(ARRAY_TYPE); // 将List转换为Postgres数组字符串表示:{"a","b"} String arrayString = "{" + String.join(",", parameter) + "}"; arrayObject.setValue(arrayString); ps.setObject(i, arrayObject); } @Override public List<String> getNullableResult(ResultSet rs, String columnName) throws SQLException { return parseArray(rs.getString(columnName)); } @Override public List<String> getNullableResult(ResultSet rs, int columnIndex) throws SQLException { return parseArray(rs.getString(columnIndex)); } @Override public List<String> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { return parseArray(cs.getString(columnIndex)); } private List<String> parseArray(String arrayString) { if (arrayString == null || arrayString.isEmpty() || arrayString.equals("{}")) { return null; } // 去除数组字符串两端的{},然后分割 String[] items = arrayString.substring(1, arrayString.length() - 1).split(","); return Arrays.asList(items); } } ``` #### 2. 在实体类字段上使用该类型处理器 ```java public class MyEntity { private Integer id; @TableField(typeHandler = TextArrayTypeHandler.class) // MyBatis-Plus // 或者使用MyBatis原生:在Mapper XML中指定typeHandler private List<String> textArray; // getters and setters } ``` #### 3. 在Mapper XML中使用 ```xml <resultMap id="myEntityMap" type="MyEntity"> <result column="text_array" property="textArray" typeHandler="com.example.handler.TextArrayTypeHandler"/> </resultMap> ``` ### 二、处理 `jsonb` 类型 #### 1. 创建自定义类型处理器 `JsonbTypeHandler` 我们可以使用通用的JSON处理器,这里使用Jackson。 首先添加依赖: ```xml <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.13.0</version> </dependency> ``` 然后创建处理器: ```java import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import org.postgresql.util.PGobject; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class JsonbTypeHandler<T> extends BaseTypeHandler<T> { private final Class<T> type; private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static final String JSONB_TYPE = "jsonb"; public JsonbTypeHandler(Class<T> type) { this.type = type; } @Override public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException { PGobject jsonObject = new PGobject(); jsonObject.setType(JSONB_TYPE); try { jsonObject.setValue(OBJECT_MAPPER.writeValueAsString(parameter)); } catch (Exception e) { throw new SQLException("Error converting object to JSON string", e); } ps.setObject(i, jsonObject); } @Override public T getNullableResult(ResultSet rs, String columnName) throws SQLException { String json = rs.getString(columnName); return parseJson(json); } @Override public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException { String json = rs.getString(columnIndex); return parseJson(json); } @Override public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { String json = cs.getString(columnIndex); return parseJson(json); } private T parseJson(String json) { if (json == null || json.isEmpty()) { return null; } try { return OBJECT_MAPPER.readValue(json, type); } catch (Exception e) { throw new RuntimeException("Error parsing JSON string to object", e); } } } ``` #### 2. 在实体类字段上使用 假设我们有一个`User`实体,其中有一个`profile`字段是JSONB类型,存储为`Map`或自定义类。 ```java public class User { private Integer id; // 对于MyBatis原生,可以在XML中配置;MyBatis-Plus则用注解 @TableField(typeHandler = JsonbTypeHandler.class) private Map<String, Object> profile; // 也可以是自定义类,如 UserProfile // 或者自定义类型 @TableField(typeHandler = JsonbTypeHandler.class) private UserProfile userProfile; // getters and setters } ``` 注意:`JsonbTypeHandler`需要指定类型,因此在使用注解需要特殊处理。MyBatis-Plus的`@TableField`支持传递泛型参数,所以我们可以通过继承来指定: ```java // 为特定类型创建子类 public class UserProfileTypeHandler extends JsonbTypeHandler<UserProfile> { public UserProfileTypeHandler() { super(UserProfile.class); } } // 然后在实体类中使用 @TableField(typeHandler = UserProfileTypeHandler.class) private UserProfile userProfile; ``` #### 3. Mapper XML配置(如果是MyBatis原生) ```xml <resultMap id="userMap" type="User"> <result column="profile" property="profile" typeHandler="com.example.handler.JsonbTypeHandler"/> </resultMap> ``` ### 三、在SQL语句中使用 对于插入或更新,直接传入对象,处理器会自动转换。 例如,插入语句: ```xml <insert id="insertUser" parameterType="User"> INSERT INTO users (id, profile) VALUES (#{id}, #{profile, typeHandler=com.example.handler.JsonbTypeHandler}) </insert> ``` ### 四、注意事项 1. **依赖**:确保项目中包含PostgreSQL JDBC驱动(如`postgresql`)和Jackson。 2. **性能**:处理器中每次转换都会进行序列化/反序列化,对于大对象需注意性能。 3. **处理**:在处理器中注意处理null字符串。 ### 相关问题设计 根据主题,设计相关问题如下: §§相关问题§§: 1. 在MyBatis中自定义类型处理器(TypeHandler)需要继承哪个基类? 2. PostgreSQL的JSONB类型与JSON类型有什么区别? 3. 如何在MyBatis的XML映射文件中指定自定义类型处理器? 4. 除了自定义类型处理器,处理PostgreSQL数组类型还有哪些替代方案? </think> ### Spring Boot + MyBatis + PostgreSQL 处理 `text[]` 和 `jsonb` 类型字段 在 PostgreSQL 中处理 `text[]`(文本数组)和 `jsonb`(JSON 二进制)类型需要特殊处理,因为 JDBC 和 MyBatis 默认支持这些复杂类型。以下是完整的解决方案: --- ### 一、`text[]` 数组类型处理 #### 1. 自定义类型处理器 `TextArrayTypeHandler` ```java import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import org.postgresql.jdbc.PgArray; import java.sql.*; public class TextArrayTypeHandler extends BaseTypeHandler<String[]> { @Override public void setNonNullParameter(PreparedStatement ps, int i, String[] parameter, JdbcType jdbcType) throws SQLException { // 创建 PostgreSQL 数组对象 Array array = ps.getConnection().createArrayOf("text", parameter); ps.setArray(i, array); } @Override public String[] getNullableResult(ResultSet rs, String columnName) throws SQLException { return toStringArray(rs.getArray(columnName)); } @Override public String[] getNullableResult(ResultSet rs, int columnIndex) throws SQLException { return toStringArray(rs.getArray(columnIndex)); } @Override public String[] getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { return toStringArray(cs.getArray(columnIndex)); } private String[] toStringArray(Array pgArray) throws SQLException { if (pgArray == null) return null; // 特殊处理 PostgreSQL 数组实现 if (pgArray instanceof PgArray) { return (String[]) ((PgArray) pgArray).getArray(); } return (String[]) pgArray.getArray(); } } ``` #### 2. 实体类使用 ```java public class Document { private Long id; // MyBatis 原生使用方式 @Result(column = "tags", property = "tags", typeHandler = TextArrayTypeHandler.class) private String[] tags; // 映射 text[] 类型 // MyBatis-Plus 使用方式 @TableField(typeHandler = TextArrayTypeHandler.class) private String[] categories; } ``` #### 3. XML 映射配置 ```xml <resultMap id="documentMap" type="Document"> <result column="tags" property="tags" typeHandler="com.example.handler.TextArrayTypeHandler"/> </resultMap> ``` --- ### 二、`jsonb` 类型处理 #### 1. 自定义类型处理器 `JsonbTypeHandler` ```java import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import org.postgresql.util.PGobject; import java.sql.*; public class JsonbTypeHandler<T> extends BaseTypeHandler<T> { private final Class<T> type; private static final ObjectMapper objectMapper = new ObjectMapper(); public JsonbTypeHandler(Class<T> type) { this.type = type; } @Override public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException { PGobject pgObject = new PGobject(); pgObject.setType("jsonb"); try { pgObject.setValue(objectMapper.writeValueAsString(parameter)); } catch (Exception e) { throw new SQLException("Error converting object to JSON", e); } ps.setObject(i, pgObject); } @Override public T getNullableResult(ResultSet rs, String columnName) throws SQLException { return parseJson(rs.getString(columnName)); } @Override public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException { return parseJson(rs.getString(columnIndex)); } @Override public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { return parseJson(cs.getString(columnIndex)); } private T parseJson(String json) { try { return json != null ? objectMapper.readValue(json, type) : null; } catch (Exception e) { throw new RuntimeException("Error parsing JSON", e); } } } ``` #### 2. 实体类使用 ```java public class UserProfile { private Long id; // 映射到 Map 类型 @TableField(typeHandler = JsonbTypeHandler.class) private Map<String, Object> preferences; // 对应 jsonb 字段 // 映射到自定义 DTO @TableField(typeHandler = AddressTypeHandler.class) private Address shippingAddress; } // 自定义地址DTO public class Address { private String street; private String city; private String zipCode; } // 特定类型的处理器 public class AddressTypeHandler extends JsonbTypeHandler<Address> { public AddressTypeHandler() { super(Address.class); } } ``` #### 3. XML 映射配置 ```xml <resultMap id="profileMap" type="UserProfile"> <result column="preferences" property="preferences" typeHandler="com.example.handler.JsonbTypeHandler"/> </resultMap> ``` --- ### 三、SQL 操作示例 #### 1. 数组类型查询 ```sql <select id="findDocumentsByTag" resultMap="documentMap"> SELECT * FROM documents WHERE #{tag} = ANY(tags) <!-- PostgreSQL 数组查询语法 --> </select> ``` #### 2. JSONB 查询 ```sql <select id="findUsersByCity" resultMap="profileMap"> SELECT * FROM user_profiles WHERE preferences->>'city' = #{city} <!-- JSONB 路径查询 --> </select> ``` --- ### 四、关键注意事项 1. **依赖要求**: ```xml <!-- Pom.xml 依赖 --> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <version>42.3.1</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.13.0</version> </dependency> ``` 2. **类型处理器注册**: ```java // MyBatis 配置类 @Configuration public class MyBatisConfig { @Bean public ConfigurationCustomizer typeHandlerRegistry() { return configuration -> { configuration.getTypeHandlerRegistry() .register(TextArrayTypeHandler.class); configuration.getTypeHandlerRegistry() .register(new JsonbTypeHandler<>(Map.class)); }; } } ``` 3. **数据库表定义示例**: ```sql CREATE TABLE documents ( id BIGSERIAL PRIMARY KEY, tags TEXT[] NOT NULL DEFAULT '{}' ); CREATE TABLE user_profiles ( id BIGSERIAL PRIMARY KEY, preferences JSONB NOT NULL DEFAULT '{}'::jsonb ); ``` --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值