MybatisPlus通过@TableField注解typeHandler属性实现List<T>类型数据的数据库存储

一 属性添加注解

  1. 在类上面添加注解:

@TableName(autoResultMap = true)

  1. 在字段上面添加注解:

@TableField(value = "list", typeHandler = UserHandler.class) private List<User> list = new ArrayList<>();

二 创建 UserHandler 类

java 复制代码
package com.demo.handler;

import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler;
import com.demo.domain.pojo.User;
import org.apache.ibatis.type.MappedTypes;

import java.util.List;

@MappedTypes({List.class, User.class})
public class UserHandler extends AbstractJsonTypeHandler<List<User>> {

  @Override
  protected List<User> parse(String json) {
    return JsonUtils.fromString(json, List.class, User.class);
  }

  @Override
  protected String toJson(List<User> obj) {
    return JsonUtils.objectToString(obj);
  }
}

如果在使用改类时,只需将文件中的 User 替换为自己的类就行

概括下,这个解析器中的 parse 方法类似咱们的 set 方法,是将数据库中的 json 类型数据转为 List 类型数据;反之,toJson 是将 List 类型数据转为 json 数据格式,最后通过 mybatisplus 存储到数据库中. 具体实现可以看下面的 JsonUtils 工具类.

java 复制代码
package com.demo.handler;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;


public class JsonUtils {
  private static final 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;
    }
  }
}

该文件是工具类,配合 UserHandler 类,无需任何改动,直接粘贴即可

三 数据库字段设置

这里只需要将对应的字段类型设置为 json 即可,这里就不贴图了.

额外说明一个事情,可以说是一个 bug,上述效果在 selectById 查询时是成功的,但是在查询返回体是 List 时,list 字段会返回空集合,问了下 ai,说是数据库为了简洁高效,会跳过解析器,断点测试也确实是这样,至于如何解决,我现在是循环单个查询,最后合并,实属无奈,如果有哪位兄弟能搞定,还麻烦大致说下原因.

文章仓促就之,是在已经已经实现的代码上直接说明,如有读者发现文章有不足或是无法实现效果,可以留言.

如果文章确实有效,还望读者点赞收藏,感谢~

相关推荐
盼海27 分钟前
排序算法(五)--归并排序
数据结构·算法·排序算法
远歌已逝1 小时前
维护在线重做日志(二)
数据库·oracle
qq_433099402 小时前
Ubuntu20.04从零安装IsaacSim/IsaacLab
数据库
Dlwyz2 小时前
redis-击穿、穿透、雪崩
数据库·redis·缓存
Theodore_10223 小时前
4 设计模式原则之接口隔离原则
java·开发语言·设计模式·java-ee·接口隔离原则·javaee
工业甲酰苯胺4 小时前
Redis性能优化的18招
数据库·redis·性能优化
冰帝海岸4 小时前
01-spring security认证笔记
java·笔记·spring
世间万物皆对象5 小时前
Spring Boot核心概念:日志管理
java·spring boot·单元测试
‘’林花谢了春红‘’5 小时前
C++ list (链表)容器
c++·链表·list
没书读了5 小时前
ssm框架-spring-spring声明式事务
java·数据库·spring