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

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

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

相关推荐
Algorithm15767 分钟前
JVM是什么,与Java的关系是什么,以及JVM怎么实现的跨平台性
java·开发语言·jvm
尘佑不尘20 分钟前
shodan5,参数使用,批量查找Mongodb未授权登录,jenkins批量挖掘
数据库·笔记·mongodb·web安全·jenkins·1024程序员节
传输能手29 分钟前
从三方云服务器将数据迁移至本地,如何保障安全高效?
大数据·服务器·数据库
BinTools图尔兹34 分钟前
CQ社区版 v2024.10 | 支持k8s、helm部署!
数据库·安全·k8s·helm·数据安全·数据库管理员
遇见你真好。1 小时前
SpringBoot整合quartz定时任务
java·springboot·quartz
一颗甜苞谷1 小时前
开源一款基于 JAVA 的仓库管理系统,支持三方物流和厂内物流,包含 PDA 和 WEB 端的源码
java·开发语言·开源
攻心的子乐1 小时前
idea使用svn
java·ide·intellij-idea
程序员大佬超1 小时前
IDEA解决 properties 文件乱码问题
java·ide·intellij-idea
秋恬意1 小时前
LinkedList 源码分析
java·开发语言·面试
隔窗听雨眠1 小时前
深入理解Redis的四种模式
java·redis·mybatis