mybatis使用typeHandler实现类型转换

使用mybatis作为操作数据库的orm框架,操作基本数据类型时可以通过内置的类型处理器完成java数据类型和数据库类型的转换,但是对于扩展的数据类型要实现与数据库类型的转换就需要自定义类型转换器完成,比如某个实体类型存储到数据库,可以转换为json字符串存储,读取数据时再转换为对应的实体类。
在mybatis中可以有两种方式实现上面的方案:
一、直接继承mybatis框架提供的 org.apache.ibatis.type.BaseTypeHandler 完成数据类型转换;
二、如果项目引入了mybatis-plus,也可以继承 com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler 实现数据类型转换。
接下来分别介绍上面两种方案的实现方式。
首先在数据库中创建一个表用于测试数据存取:

CREATE TABLE `demo_data`  (
  `id` int NOT NULL AUTO_INCREMENT,
  `detail` json NULL,
  `create_time` datetime NULL,
  PRIMARY KEY (`id`)
);
一、mybatis框架实现类型转换

使用mybatis实现类型转换,首先要自定义一个handler继承自基础的handler,再将自定义的handler注入到字段的typeHandler中就实现了类型转换:

自定义handler:

import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @Author xingo
 * @Date 2025/2/6
 */
@MappedJdbcTypes({JdbcType.VARCHAR, JdbcType.CHAR})
public class DetailTypeHandler extends BaseTypeHandler<DemoData.DetailInfo> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, DemoData.DetailInfo parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, JacksonUtils.toJSONString(parameter));
    }

    @Override
    public DemoData.DetailInfo getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return JacksonUtils.parseObject(rs.getString(columnName), DemoData.DetailInfo.class);
    }

    @Override
    public DemoData.DetailInfo getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return JacksonUtils.parseObject(rs.getString(columnIndex), DemoData.DetailInfo.class);
    }

    @Override
    public DemoData.DetailInfo getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return JacksonUtils.parseObject(cs.getString(columnIndex), DemoData.DetailInfo.class);
    }
}

上面就实现了java类型与数据库类型的对应关系,就是将实体类中的java对象与数据库中的字符串类型自动转换:

import lombok.Data;

import java.io.Serializable;
import java.time.LocalDateTime;

/**
 * @Author xingo
 * @Date 2025/2/6
 */
@Data
public class DemoData implements Serializable {

    private Integer id;

    private DemoData.DetailInfo detail;

    private LocalDateTime createTime;

    @Data
    public static class DetailInfo implements Serializable {

        private String name;

        private Integer age;

        private LocalDateTime dateTime;
    }
}

接下来就是在编写的xml文件中将刚刚自定义的handler和实体类信息完成对应关系:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.xingo.demo.DemoDataMapper">

    <resultMap id="demoData" type="org.xingo.demo.DemoData">
        <id property="id" column="id"/>
        <result property="detail" column="detail" typeHandler="org.xingo.demo.DetailTypeHandler"/>
        <result property="createTime" column="create_time"/>
    </resultMap>

    <insert id="insertDemoData" useGeneratedKeys="true" keyProperty="id">
        insert into demo_data(detail, create_time)
        values (#{detail, typeHandler=org.xingo.demo.DetailTypeHandler}, #{createTime})
    </insert>

    <update id="updateDemoData">
        update demo_data
        set detail=#{detail, typeHandler=org.xingo.demo.DetailTypeHandler}
            create_time=#{createTime}
        where id=#{id}
    </update>

    <select id="findDemoDataById" resultMap="demoData">
        select *
        from demo_data
        where id=#{id}
    </select>
</mapper>

xml对应的接口:

import org.xingo.demo.DemoData;

/**
 * @Author xingo
 * @Date 2025/2/6
 */
public interface DemoDataMapper {

    void insertDemoData(DemoData data);

    void updateDemoData(DemoData data);

    DemoData findDemoDataById(Integer id);
}

上面的几步就实现了自定义数据类型与数据库中字符串类型的转换,测试上面接口可以完成数据的存取:请添加图片描述

二、mybatis-plus框架实现类型转换

使用mybatis实现自定义类型与数据库类型的转换相对来说还是有一点繁琐,如果在项目中引入了mybatis-plus,那么就可以减少xml文件的编写,直接在实体类的字段上添加注解完成xml文件的内容。

使用mybatis-plus实现类型转换首先也是自定义handler类:

import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;

/**
 * @Author xingo
 * @Date 2025/2/6
 */
@MappedTypes({DemoData.DetailInfo.class})
@MappedJdbcTypes({JdbcType.VARCHAR, JdbcType.CHAR})
public class DetailTypeHandler extends AbstractJsonTypeHandler<DemoData.DetailInfo> {

    @Override
    protected DemoData.DetailInfo parse(String json) {
        return JacksonUtils.parseObject(json, DemoData.DetailInfo.class);
    }

    @Override
    protected String toJson(DemoData.DetailInfo detail) {
        return JacksonUtils.toJSONString(detail);
    }
}

映射主要是通过实体类的注解完成的:

import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;

import java.io.Serializable;
import java.time.LocalDateTime;

/**
 * @Author xingo
 * @Date 2025/2/6
 */
@Data
@TableName(value = "demo_data", autoResultMap = true)
public class DemoData implements Serializable {

    @TableId(type = IdType.AUTO)
    private Integer id;

    @TableField(typeHandler = DetailTypeHandler.class)
    private DemoData.DetailInfo detail;

    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;

    @Data
    public static class DetailInfo implements Serializable {

        private String name;

        private Integer age;

        private LocalDateTime dateTime;
    }
}

mapper接口只需要继承mybatis-plus提供的基础mapper就可以:

import com.baomidou.mybatisplus.core.mapper.BaseMapper;

/**
 * @Author xingo
 * @Date 2025/2/6
 */
public interface DemoDataMapper extends BaseMapper<DemoData> {
}

通过上面的定义,所有基于mybatis-plus提供的增改查操作都可以完成字段类型转换。请添加图片描述
测试上面的内容在数据库中产生的数据:
请添加图片描述

附: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.*;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;

/**
 * json工具
 *
 * @Author xingo
 * @Date 2025/2/6
 */
@Slf4j
public class JacksonUtils {

    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    static {
        // Long类型处理,避免前端处理长整型时精度丢失
        SimpleModule module1 = new SimpleModule();
        module1.addSerializer(Long.class, ToStringSerializer.instance);
        module1.addSerializer(Long.TYPE, ToStringSerializer.instance);

        JavaTimeModule module2 = new JavaTimeModule();
        // java8日期处理
        module2.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        module2.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        module2.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
        module2.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        module2.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        module2.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss")));

        OBJECT_MAPPER
                // 添加modules
                .registerModules(module1, module2, new Jdk8Module())
                // 日期类型不转换为时间戳
                .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
                .configure(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS, false)
                // 反序列化的时候如果多了其他属性,不抛出异常
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                // 如果是空对象的时候,不抛异常
                .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
                // 空对象不序列化
                .setSerializationInclusion(JsonInclude.Include.NON_NULL)
                // 日期格式化
                .setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"))
                // 设置时区
                .setTimeZone(TimeZone.getTimeZone("GMT+8"))
                // 驼峰转下划线
                // .setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE)
                // 语言
                .setLocale(Locale.SIMPLIFIED_CHINESE);
    }

    /**
     * 反序列化对象
     */
    public static <T> T parseObject(String json, Class<T> clazz) {
        if (json == null) {
            return null;
        }
        try {
            return OBJECT_MAPPER.readValue(json, clazz);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 反序列化对象
     */
    public static JsonNode parseObject(String json) {
        if (json == null) {
            return null;
        }
        try {
            return OBJECT_MAPPER.readTree(json);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 反序列化对象
     */
    public static <T> T parseObject(String json, TypeReference<T> type) {
        if (json == null) {
            return null;
        }
        try {
            return OBJECT_MAPPER.readValue(json, type);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 反序列化对象
     */
    public static <T> T parseObject(byte[] bytes, TypeReference<T> type) {
        if (bytes == null) {
            return null;
        }
        try {
            return OBJECT_MAPPER.readValue(bytes, type);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 反序列化对象
     */
    public static <T> T parseObject(JsonNode jsonNode, Class<T> clazz) {
        return jsonNode == null ? null : OBJECT_MAPPER.convertValue(jsonNode, clazz);
    }

    /**
     * 反序列化列表
     */
    public static <T> List<T> parseArray(String json, Class<T> clazz) {
        if (json == null) {
            return null;
        }
        try {
            JavaType javaType = OBJECT_MAPPER.getTypeFactory().constructParametricType(List.class, clazz);
            return OBJECT_MAPPER.treeToValue(OBJECT_MAPPER.readTree(json), javaType);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 反序列化列表
     */
    public static <T> List<T> parseArray(JsonNode json, Class<T> clazz) {
        try {
            JavaType javaType = OBJECT_MAPPER.getTypeFactory().constructParametricType(List.class, clazz);
            return json == null ? null : OBJECT_MAPPER.treeToValue(json, javaType);
        } catch (JsonProcessingException e) {
            log.warn(e.getLocalizedMessage());
            return null;
        }
    }

    /**
     * 写为json串
     */
    public static String toJSONString(Object obj) {
        if (obj == null) {
            return null;
        }
        if (obj instanceof String) {
            return (String) obj;
        }
        try {
            return OBJECT_MAPPER.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 写为字节数组
     */
    public static byte[] toJSONBytes(Object obj) {
        if (obj == null) {
            return null;
        }
        try {
            return OBJECT_MAPPER.writeValueAsBytes(obj);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取jackson对象
     */
    public static ObjectMapper getObjectMapper() {
        return OBJECT_MAPPER;
    }

    /**
     * 美化输出json格式
     */
    public static String pretty(String json) throws IOException {
        return StringUtils.isBlank(json) ? json : pretty(JacksonUtils.getObjectMapper().readTree(json));
    }

    /**
     * 美化输出json格式
     */
    public static String pretty(JsonNode jsonNode) throws IOException {
        return null == jsonNode ? "" : JacksonUtils.getObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(jsonNode);
    }

    /**
     * 对象转json
     */
    public static JsonNode toJsonNode(Object obj) {
        if (obj instanceof String) {
            return parseObject((String) obj, JsonNode.class);
        }
        return obj == null ? null : OBJECT_MAPPER.convertValue(obj, JsonNode.class);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/972096.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

瑞萨RA-T系列芯片ADCGPT功能模块的配合使用

在马达或电源工程中&#xff0c;往往需要采集多路AD信号&#xff0c;且这些信号的优先级和采样时机不相同。本篇介绍在使用RA-T系列芯片建立马达或电源工程时&#xff0c;如何根据需求来设置主要功能模块ADC&GPT&#xff0c;包括采样通道打包和分组&#xff0c;GPT触发启动…

最新智能优化算法:牛优化( Ox Optimizer,OX)算法求解经典23个函数测试集,MATLAB代码

一、牛优化算法 牛优化&#xff08; OX Optimizer&#xff0c;OX&#xff09;算法由 AhmadK.AlHwaitat 与 andHussamN.Fakhouri于2024年提出&#xff0c;该算法的设计灵感来源于公牛的行为特性。公牛以其巨大的力量而闻名&#xff0c;能够承载沉重的负担并进行远距离运输。这种…

【linux】在 Linux 服务器上部署 DeepSeek-r1:70b 并通过 Windows 远程可视化使用

【linux】在 Linux 服务器上部署 DeepSeek-r1:70b 并通过 Windows 远程可视化使用 文章目录 【linux】在 Linux 服务器上部署 DeepSeek-r1:70b 并通过 Windows 远程可视化使用个人配置详情一、安装ollama二、下载deepseek版本模型三、在 Linux 服务器上配置 Ollama 以允许远程访…

【Linux网络编程】应用层协议HTTP(请求方法,状态码,重定向,cookie,session)

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;Linux网络编程 &#x1f337;追光的人&#xff0c;终会万丈光芒 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 ​ Linux网络编程笔记&#xff1a; https://blog.cs…

Chrome多开终极形态解锁!「窗口管理工具+IP隔离插件

Web3项目多开&#xff0c;继ads指纹浏览器钱包被盗后&#xff0c;更多人采用原生chrome浏览器&#xff0c;当然对于新手&#xff0c;指纹浏览器每月成本也是一笔不小开支&#xff0c;今天逛Github发现了这样一个解决方案&#xff0c;作者开发了窗口管理工具IP隔离插件&#xff…

Canal同步MySQL增量数据

引言 在现在的系统开发中&#xff0c;为了提高查询效率 , 以及搜索的精准度, 会大量的使用 redis 、memcache 等 nosql 系统的数据库 , 以及 solr 、 elasticsearch 类似的全文检索服务。 那么这个时候, 就又有一个问题需要我们来考虑, 就是数据同步的问题, 如何将实时变化的…

MacOS 15.3 卸载系统内置软件

1、关闭系统完整性&#xff08;SIP&#xff09; 进入恢复模式(recovery) 如果您使用的是黑苹果或者白苹果&#xff0c;可以选择 重启按住CommandR 进入&#xff0c;如果是M系列芯片&#xff0c;长按开机键&#xff0c;进入硬盘选择界面进入。 我是MacMini M4芯片&#xff0c;关…

【核心算法篇七】《DeepSeek异常检测:孤立森林与AutoEncoder对比》

大家好,今天我们来深入探讨一下《DeepSeek异常检测:孤立森林与AutoEncoder对比》这篇技术博客。我们将从核心内容、原理、应用场景等多个方面进行详细解析,力求让大家对这两种异常检测方法有一个全面而深入的理解。 一、引言 在数据科学和机器学习领域,异常检测(Anomaly…

Ubuntu24.04无脑安装docker(含图例)

centos系统请看这篇 Linux安装Docker教程&#xff08;详解&#xff09; 一. ubuntu更换软件源 请看这篇&#xff1a;Ubuntu24.04更新国内源 二. docker安装 卸载老版docker(可忽略) sudo apt-get remove docker docker-engine docker.io containerd runc更新软件库 sudo a…

thingboard告警信息格式美化

原始报警json内容&#xff1a; { "severity": "CRITICAL","acknowledged": false,"cleared": false,"assigneeId": null,"startTs": 1739801102349,"endTs": 1739801102349,"ackTs": 0,&quo…

✨2.快速了解HTML5的标签类型

✨✨HTML5 的标签类型丰富多样&#xff0c;每种类型都有其独特的功能和用途&#xff0c;以下是一些常见的 HTML5 标签类型介绍&#xff1a; &#x1f98b;结构标签 &#x1faad;<html>&#xff1a;它是 HTML 文档的根标签&#xff0c;所有其他标签都包含在这个标签内&am…

day12_调度和可视化

文章目录 day12_调度和可视化一、任务调度1、开启进程2、登入UI界面3、配置租户4、创建项目5、创建工作流5.1 HiveSQL部署&#xff08;掌握&#xff09;5.2 SparkDSL部署&#xff08;掌握&#xff09;5.3 SparkSQL部署&#xff08;熟悉&#xff09;5.4 SeaTunnel部署&#xff0…

使用nvm管理node.js版本,方便vue2,vue3开发

在Vue项目开发过程中&#xff0c;我们常常会遇到同时维护Vue2和Vue3项目的情况。由于不同版本的Vue对Node.js 版本的要求有所差异&#xff0c;这就使得Node.js 版本管理成为了一个关键问题。NVM&#xff08;Node Version Manager&#xff09;作为一款强大的Node.js 版本管理工具…

Java8适配的markdown转换html工具(FlexMark)

坐标地址&#xff1a; <dependency><groupId>com.vladsch.flexmark</groupId><artifactId>flexmark-all</artifactId><version>0.60.0</version> </dependency> 工具类代码&#xff1a; import com.vladsch.flexmark.ext.tab…

Qt开发①Qt的概念+发展+优点+应用+使用

目录 1. Qt的概念和发展 1.1 Qt的概念 1.2 Qt 的发展史&#xff1a; 1.3 Qt 的版本 2. Qt 的优点和应用 2.1 Qt 的优点&#xff1a; 2.2 Qt 的应用场景 2.3 Qt 的应用案例 3. 搭建 Qt 开发环境 3.1 Qt 的开发工具 3.2 Qt SDK 的下载和安装 3.3 Qt 环境变量配置和使…

vscode插件开发

准备 安装开发依赖 npm install -g yo generator-code 安装后&#xff0c;运行命令 yo code 运行 打开项目&#xff0c; 点击 vscode 调式 按 F5 或点击调试运行按钮 会打开一个新窗口&#xff0c;在新窗口按快捷键 CtrlShiftP &#xff0c;搜索 Hello World 选择执行 右下角出…

自制简单的图片查看器(python)

图片格式&#xff1a;支持常见的图片格式&#xff08;JPG、PNG、BMP、GIF&#xff09;。 import os import tkinter as tk from tkinter import filedialog, messagebox from PIL import Image, ImageTkclass ImageViewer:def __init__(self, root):self.root rootself.root.…

嵌入式 lwip http server makefsdata

背景&#xff1a; 基于君正X2000 MCU Freertoslwip架构 实现HTTP server服务&#xff0c;MCU作为HTTP服务器通过网口进行数据包的传输&#xff0c;提供网页服务。其中设计到LWIP提供的工具makefsdata&#xff0c;常用于将文件或目录结构转换为适合嵌入到固件中的二进制格式。 …

架构设计系列(三):架构模式

一、概述 关于移动应用开发中常见的架构模式&#xff0c;这些模式是为了克服早期模式的局限性而引入。常见的 架构模式有&#xff1a; MVC, MVP, MVVM, MVVM-C, and VIPER 二、MVC, MVP, MVVM, MVVM-C, and VIPER架构模式 MVC、MVP、MVVM、MVVM-C 和 VIPER 是移动应用开发中…

CSS盒模

CSS盒模型就像一个快递包裹&#xff0c;网页上的每个元素都可以看成是这样一个包裹&#xff0c;它主要由以下几个部分组成&#xff1a; 内容&#xff08;content&#xff09;&#xff1a;就像包裹里真正装的东西&#xff0c;比如文字、图片等。在CSS里&#xff0c;可用width&a…