一、typeHandler 的使用
1、存储json格式字段
如果字段需要存储为json格式,可以使用JacksonTypeHandler处理器。使用方式非常简单,如下所示:
只需要加上两个注解即可:
@TableName(autoResultMap = true)
表示自动映射resultMap
@TableField(typeHandler = JacksonTypeHandler.class)
表示将UserInfo对象转为json对象入库
2、自定义 typeHandler 实现类
例如当我们 某个字段存储的类型为List或者Map时,我们可以自定义一个TypeHandler,以 list 为例,我们想存储一个字段类型为 list ,在数据库中的存储的格式是 多条数据以逗号分割,当查询时会自动根据逗号分割成列表格式。
需要实现BaseTypeHandler
抽象类:
@MappedJdbcTypes(JdbcType.VARCHAR)
@MappedTypes({List.class})
public class ListTypeHandler extends BaseTypeHandler<List<String>> {
private static final String DELIM = ",";
@Override
public void setNonNullParameter(PreparedStatement preparedStatement, int i, List<String> strings, JdbcType jdbcType) throws SQLException {
String value = StringUtils.collectionToDelimitedString(strings, DELIM);
preparedStatement.setString(i, value);
}
@Override
public List<String> getNullableResult(ResultSet resultSet, String s) throws SQLException {
String value = resultSet.getString(s);
return Arrays.asList(StringUtils.tokenizeToStringArray(value, DELIM));
}
@Override
public List<String> getNullableResult(ResultSet resultSet, int i) throws SQLException {
String value = resultSet.getString(i);
return Arrays.asList(StringUtils.tokenizeToStringArray(value, DELIM));
}
@Override
public List<String> getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
String value = callableStatement.getString(i);
return Arrays.asList(StringUtils.tokenizeToStringArray(value, DELIM));
}
}
- @MappedJdbcTypes:表示
SQL
语句中查出来的类型; - @MappedTypes:表示要转成 Java 对象的类型;
- DELIM:表示字符串的分隔符,如果你是用空格分开的就赋值为空格。
-
setNonNullParameter(插入时设置参数类型)
-
getNullableResult(获取时转换回的自定义类型)
根据列名获取
resultSet:结果集
columnName:列名
public UserInfo getNullableResult(ResultSet resultSet, String columnName)
根据索引位置获取
resultSet:结果集
columnIndex:列索引
public UserInfo getNullableResult(ResultSet resultSet, int columnIndex)
根据存储过程获取
callableStatement:结果集
columnIndex:列索引
public UserInfo getNullableResult(CallableStatement callableStatement, int columnIndex)
然后再字段上指定该实现了即可:
@TableField(typeHandler = ListTypeHandler.class)
private List<String> job;
最后在数据库中存储格式为:
3、存储自定义对象字段
例如我们刚才使用json格式存储 Unit 字段,如果不加 @TableField(typeHandler = JacksonTypeHandler.class)
就会报错,因为 typeHandler 可以指定我们在Java实体类所包含的自定义类型存入数据库后的类型是什么,也可以从数据库中取出该数据后自动转换为我们自定义的Java类型。
我们虽然在 Java中定义了 Unit 类型,但是数据库不认识,我们现在就需要将 Unit 转换为数据库认识的类型。Java自带的类型在存取的时候不会出错,我们自定义的类型就会出错 是因为 mybatis已经将这些类型的TypeHandler提前写好了:
所以如果我们要存储 Unit 类型的字段,又不想用 默认的json 格式,我们也需要自定义一个 关于Unit 的 TypeHandler,如下:
public class JsonUtils {
private static ObjectMapper objectMapper = new ObjectMapper();
//初始化相关的配置
static {
//只引用不为空的值
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
//取消默认转换timestemp
objectMapper.configure(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS, false);
//忽略空bean转换错误
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
//忽略在json中存在,在java对象不存在的错误
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// 解决jackson2无法反序列化LocalDateTime的问题
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
objectMapper.registerModule(new JavaTimeModule());
}
/**
* 将java对象转换成json字符串
*
* @param obj
* java 对象
* @param <T>
* @return
*/
public static <T> String objectToString(T obj) {
if (obj == null) {
return null;
}
try {
return obj instanceof String ? (String) obj : objectMapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
e.printStackTrace();
return null;
}
}
/**
* 将json字符串转换成java对象
*
* @param json
* 字符串
* @param tClass
* 要转换的对象
* @param <T>
* @return
*/
public static <T> T getObjetFormString(String json, Class<T> tClass) {
if (StringUtils.isBlank(json) || tClass == null) {
return null;
}
try {
return tClass.equals(String.class) ? (T) json : objectMapper.readValue(json, tClass);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
/**
* 将字符串转换成java对象
* @param json
* 字符串
* @param tTypeReference
* 对象
*
* @param <T>
* @return
*/
public static <T> T fromString(String json, TypeReference<T> tTypeReference){
if (StringUtils.isBlank(json) || tTypeReference == null) {
return null;
}
try {
return tTypeReference.getType().equals(String.class) ? (T) json : objectMapper.readValue(json, tTypeReference);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
/**
* 将json字符串转换成java集合对象
* @param json
* 字符串
* @param collectionClass
* 集合类型
* @param elementClazzes
* 成员类型
* @param <T>
* @return
*/
public static <T> T fromString(String json, Class<?> collectionClass, Class<?> ... elementClazzes){
JavaType javaType = objectMapper.getTypeFactory().constructParametricType(collectionClass, elementClazzes);
try {
return objectMapper.readValue(json, javaType);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
@MappedTypes(UserInfo.class)
@MappedJdbcTypes(JdbcType.VARCHAR)
public class UserInfoTypeHandler extends BaseTypeHandler<UserInfo> {
@Override
public void setNonNullParameter(PreparedStatement preparedStatement, int i, UserInfo userInfo, JdbcType jdbcType) throws SQLException {
preparedStatement.setString(i, JsonUtils.objectToString(userInfo));
}
@Override
public UserInfo getNullableResult(ResultSet resultSet, String columnName) throws SQLException {
return JsonUtils.fromString(resultSet.getString(columnName),UserInfo.class);
}
@Override
public UserInfo getNullableResult(ResultSet resultSet, int columnIndex) throws SQLException {
return JsonUtils.fromString(resultSet.getString(columnIndex),UserInfo.class);
}
@Override
public UserInfo getNullableResult(CallableStatement callableStatement, int columnIndex) throws SQLException {
return
JsonUtils.fromString(callableStatement.getString(columnIndex),UserInfo.class);
}
}
其实还是将该对象转成了 json ,但是只不过转成 json 的工具类可以 由我们自己指定。
二、mybatisplus 存储枚举类型
存储枚举类型有两种形式,第一种就是自定义 typeHandler 跟上面一样,
还有一种是使用mybatis提供的默认的处理类,如下所所示:
数据库表结构:
mybatisPlus对枚举处理器进行了补充:
创建枚举类:
@Getter
public enum OrderState {
NORMAL(0, "正常"),
CANCEL(1, "取消"),
DELETE(2, "删除");
// 状态码
@EnumValue // 用于数据库存储
private Integer code;
// 描述
@JsonValue // 用于序列化返回的json数据
private String desc;
OrderState(Integer code, String desc) {
this.code = code;
this.desc = desc;
}
}
TOrder实体类:
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("t_order")
public class TOrder implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "order_id", type = IdType.AUTO)
private Integer orderId;
@TableField("user_id")
private Integer userId;
@TableField("price")
private BigDecimal price;
@TableField("status")
private OrderState status;
}
在配置文件中配置统一的枚举处理器,实现类型转换:
调用:
三、使用CommandLineRunner 修改 JacksonTypeHandler 实现类
@Component
public class JsonTypeHandlerConfig implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
JacksonTypeHandler.setObjectMapper(JsonUtils.getMapper());
}
}