mybatis plus 存储 list 数据

1.背景

在我们平时开发中需要向数据库存储 list 这种类型的数据,这种可以用关联表来做,也可以直接用json 类型的字段来存储,在这里我选择用json类型的字段来存储。因为关联数据太多会增加我们系统的复杂度,也会影响我们数据库的查询效率。

2. 如何用 mybaits plus 来实现

2.1 定义权限类


import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.util.CollectionUtils;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/***
 * className Permission
 * date: 2021-08-18 12:19
 * author: 杨兴
 **/
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class Permission implements Serializable {
    private static final long serialVersionUID = 7450078753438003785L;

    private String code;

    private String name;

    private Set<Permission> children;

    public Permission(String name, String code) {
        this.name = name;
        this.code = code;
    }

    public void addItem(Permission dto) {
        if (children == null) {
            children = new HashSet<>();
        }

        children.add(dto);
    }

    @JsonIgnore
    public List<String> getPermissionStrings() {
        List<String> permissions = new ArrayList<>();
        permissions.add(code);
        if (!CollectionUtils.isEmpty(getChildren())) {
            permissions.addAll(getChildren().stream().map(Permission::getCode).collect(Collectors.toList()));
        }

        return permissions;
    }

}

我设计的权限由权限码和权限标题组成,并且还有上下级关系,因此不能使用 mybatis plus 默认提供的转换器,因为它提供的转换器只支持 object的序列化

2.2 封装 Jackson工具类

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 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;
        }
    }
}

我的json序列化工具用的是jackson

2.3 定义 转换器


import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler;
import com.communal.common.utils.JsonUtils;
import com.comptroller.entities.Permission;
import org.apache.ibatis.type.MappedTypes;

import java.util.List;

@MappedTypes({List.class, Permission.class})
public class PermissionTypeHandler extends AbstractJsonTypeHandler<List<Permission>> {
    @Override
    protected List<Permission> parse(String json) {
        return JsonUtils.fromString(json, List.class, Permission.class);
    }

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

2.4 使用转换器

    @TableField(typeHandler = PermissionTypeHandler.class)
    private List<Permission> permissions = new ArrayList<>();

注意: 在引用实体类上一定要加 @TableName(autoResultMap = true) 注解, 否则查询出来的结果会为 null

  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值