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,说是数据库为了简洁高效,会跳过解析器,断点测试也确实是这样,至于如何解决,我现在是循环单个查询,最后合并,实属无奈,如果有哪位兄弟能搞定,还麻烦大致说下原因.

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

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

相关推荐
P7进阶路1 小时前
Tomcat异常日志中文乱码怎么解决
java·tomcat·firefox
小丁爱养花1 小时前
Spring MVC:HTTP 请求的参数传递2.0
java·后端·spring
CodeClimb1 小时前
【华为OD-E卷 - 第k个排列 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od
等一场春雨1 小时前
Java设计模式 九 桥接模式 (Bridge Pattern)
java·设计模式·桥接模式
带刺的坐椅2 小时前
[Java] Solon 框架的三大核心组件之一插件扩展体系
java·ioc·solon·plugin·aop·handler
加酶洗衣粉2 小时前
MongoDB部署模式
数据库·mongodb
Suyuoa2 小时前
mongoDB常见指令
数据库·mongodb
添砖,加瓦2 小时前
MongoDB详细讲解
数据库·mongodb
Zda天天爱打卡2 小时前
【趣学SQL】第二章:高级查询技巧 2.2 子查询的高级用法——SQL世界的“俄罗斯套娃“艺术
数据库·sql
我的运维人生2 小时前
MongoDB深度解析与实践案例
数据库·mongodb·运维开发·技术共享