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

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

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

相关推荐
Leo.yuan14 分钟前
数据库同步是什么意思?数据库架构有哪些?
大数据·数据库·oracle·数据分析·数据库架构
风铃儿~15 分钟前
Spring AI 入门:Java 开发者的生成式 AI 实践之路
java·人工智能·spring
Kookoos20 分钟前
ABP VNext 与 Neo4j:构建基于图数据库的高效关系查询
数据库·c#·.net·neo4j·abp vnext
斯普信专业组20 分钟前
Tomcat全方位监控实施方案指南
java·tomcat
忆雾屿31 分钟前
云原生时代 Kafka 深度实践:06原理剖析与源码解读
java·后端·云原生·kafka
武昌库里写JAVA44 分钟前
iview Switch Tabs TabPane 使用提示Maximum call stack size exceeded堆栈溢出
java·开发语言·spring boot·学习·课程设计
云之兕1 小时前
MyBatis 的动态 SQL
数据库·sql·mybatis
gaoliheng0061 小时前
Redis看门狗机制
java·数据库·redis
我是唐青枫1 小时前
.NET AOT 详解
java·服务器·.net
?ccc?1 小时前
MySQL主从复制与读写分离
数据库·mysql