SpringBoot第25讲:SpringBoot对TypeHandler的使用

news2024/11/26 8:58:24

SpringBoot第25讲:SpringBoot对TypeHandler的使用

本文是SpringBoot第25讲,SpringBoot对TypeHandler的使用,TypeHandler就是当SpringBoot 实体类中字段类型和数据库中字段类型不一致时进行使用。

文章目录

  • SpringBoot第25讲:SpringBoot对TypeHandler的使用
    • 1、使用场景
    • 2、MyBatis下使用TypeHandler
    • 3、MyBatis plus下使用TypeHandler
    • 4、MyBatis实用场景-TypeHandler处理枚举
    • 5、商品中心对TypeHandler的使用
    • 6、白龙马对TypeHandler的使用

1、使用场景

TypeHandler,类型转换器,就是将数据库中的类型与Java中的类型进行相互转换的处理器。

有时候,我们进行存储时的字段类型和数据库最终存储的字段类型是不一致的:比如我们在springboot传入的是一个list,而在数据库中需要以VARCHAR类型保存;又比如我们在springboot传入一个JSON类型,而数据库中以String类型存储该字段;或者说数据库中是JSON类型,但java无找不到对应的JSON类型(直接使用JSONObject会报错),也可以使用typeHandler。

一般情况,我们可以强转String再存储该字段,但有时候查询数据时会出现问题:较为典型的是String类型的JSON串数据,以String类型返回至前端时,会由于进行两次json解析,导致返回结果反斜杆:

img

而这会导致前端解析时出错,所以我们需要用typeHandler对其进行转换:

img

简而言之,typeHandler就是当springboot后端实体类中字段类型和数据库中字段类型不一致时进行使用。

2、MyBatis下使用TypeHandler

首先,我们需要编写一个typeHandler(以JSON转VARCHAR为例):

@MappedTypes(JSONObject.class)
@MappedJdbcTypes(JdbcType.LONGVARCHAR)
public class JsonObjectTypeHandler extends BaseTypeHandler<JSONObject> {
 
    /**
     * 插入数据时,将JSONObject转String
     * @param ps
     * @param i
     * @param parameter
     * @param jdbcType
     * @throws SQLException
     */
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, JSONObject parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, JSONObject.toJSONString(parameter));
    }
 
    /**
     * 根据列名,获取可以为空的结果
     * @param rs
     * @param columnName
     * @return
     * @throws SQLException
     */
    @Override
    public JSONObject getNullableResult(ResultSet rs, String columnName) throws SQLException {
        String sqlJson = rs.getString(columnName);
        if (null != sqlJson){
            return JSONObject.parseObject(sqlJson);
        }
        return null;
    }
 
    /**
     * 根据列索引,获取可以为空的结果
     * @param rs
     * @param columnIndex
     * @return
     * @throws SQLException
     */
    @Override
    public JSONObject getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        String sqlJson = rs.getString(columnIndex);
        if (null != sqlJson){
            return JSONObject.parseObject(sqlJson);
        }
        return null;
    }
 
    @Override
    public JSONObject getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        String sqlJson = cs.getString(columnIndex);
        if (null != sqlJson){
            return JSONObject.parseObject(sqlJson);
        }
        return null;
    }
}

注意,该TypeHandler需要放在mapper文件夹下(即和mapper类放在一起,否则会找不到)

然后,在mapper.xml文件中,编写resultmap映射字段,并指定哪些字段需要使用typeHandler:

<!--字段映射,将数据库中String类型的json串映射为JSONObject,避免返回前段时两次序列化使得返回结果带反斜杠-->
    <resultMap id="illnessMap" type="com.seven.springcloud.dto.IllnessDTO">
        <result column="Weight" property="Weight" javaType="com.alibaba.fastjson.JSONObject" jdbcType="VARCHAR"
                typeHandler="com.seven.springcloud.mapper.JsonObjectTypeHandler"/>
        <result column="Add_date" property="Drug_Date"/>
    </resultMap>
    <!--查询所有病人的信息-->
    <select id="selectUserIllness" resultMap="illnessMap">
        select * from user_illness where Account=#{Account}
    </select>

然后,实体类中,将字段类型设为JSONObject:

@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonInclude(JsonInclude.Include.NON_EMPTY)     //空值不返回
public class IllnessDTO implements Serializable {
    private JSONObject Weight;
    private String Drug_Date;
}

然后,就可以成功映射数据啦。

查看数据库,保存的是一个String类型的JSON串:
img

3、MyBatis plus下使用TypeHandler

使用mybatis plus时,我们也可以使用mybatis的方式,xml文件中编写resultMap的形式映射字段;也可以选择以mybatis plus的注解的形式使用。

首先,我们仍然需要编写一个typeHandler类:

@MappedJdbcTypes(JdbcType.VARCHAR)  //数据库类型
@MappedTypes({JSONObject.class})    //java数据类型
public class JsonTypeHandler implements TypeHandler<JSONObject> {
    
  	@Override
    public void setParameter(PreparedStatement ps, int i, JSONObject parameter, JdbcType jdbcType) throws SQLException {
        if (parameter!=null){
            ps.setString(i, JSONObject.toJSONString(parameter));
        }
    }
 
    @Override
    public JSONObject getResult(ResultSet rs, String columnName) throws SQLException {
        return JSONObject.parseObject(rs.getString(columnName));
    }
 
    @Override
    public JSONObject getResult(ResultSet rs, int columnIndex) throws SQLException {
        return JSONObject.parseObject(rs.getString(columnIndex));
    }
 
    @Override
    public JSONObject getResult(CallableStatement cs, int columnIndex) throws SQLException {
        return JSONObject.parseObject(cs.getString(columnIndex));
    }
}

注:我们需要再application.yml文件中指定该typeHandler所在的包的位置,否则会找不到:

mybatis-plus:
type-handlers-package: com.seven.demo.typerHandler

然后,我们在实体类进行配置即可:

@TableName(value ="student",autoResultMap = true)    //autoResultMap为true才会自动配置resultMap映射字段
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student implements Serializable {
 
    @TableId
    private Long id;
 
    private String name;
 
    @TableField(value = "grade", typeHandler = JsonTypeHandler.class)    //指定typeHandler进行转换类型
    private JSONObject grade;
 
    @TableField(exist = false)
    private static final long serialVersionUID = 1L;
}

然后,我们就可以对类型进行转换了。

下面我们可以编写接口测试一下:

@RestController
public class StudentController {
    
  @Resource
    private StudentService studentService;
 
    @PostMapping("/add")
    public boolean add(@RequestBody Student student){
        return studentService.save(student);
    }
 
    @GetMapping("/get")
    public Student get(@RequestParam("id")int id){
        return studentService.getById(id);
    }
}

img

img

测试成功,查看数据库:

img

img

成功保存字符类型json串。

4、MyBatis实用场景-TypeHandler处理枚举

自定义TypeHandler

我们可以通过自定义TypeHandler的形式来在设置参数或者取出结果集的时候自定义参数封装策略。

步骤:

  • 1、实现TypeHandler接口或者继承BaseTypeHandler
  • 2、使用@MappedTypes定义处理的java类型,使用@MappedJdbcTypes定义jdbcType类型
  • 3、在自定义结果集标签或者参数处理的时候声明使用自定义TypeHandler进行处理或者在全局配置TypeHandler要处理的javaType。

示例:希望数据库保存的是100, 200这些状态码,而不是默认0,1 或者枚举的名字

public enum EmpStatus {
    LOGIN(100,"用户登录"),LOGOUT(200,"用户登出"),REMOVE(300,"用户不存在");

    private Integer code;
    private String msg;
    private EmpStatus(Integer code,String msg){
        this.code = code;
        this.msg = msg;
    }

    //按照状态码返回枚举对象
    public static EmpStatus getEmpStatusByCode(Integer code){
        switch (code) {
            case 100:
              return LOGIN;
            case 200:
              return LOGOUT;	
            case 300:
              return REMOVE;
            default:
              return LOGOUT;
        }
		}
	...

自定义Handler

public class MyEnumEmpStatusTypeHandler implements TypeHandler<EmpStatus> {

    /**
     * 定义当前数据如何保存到数据库中
     */
    @Override
    public void setParameter(PreparedStatement ps, int i, EmpStatus parameter, JdbcType jdbcType) throws SQLException {
        System.out.println("要保存的状态码:" + parameter.getCode());
        ps.setString(i, parameter.getCode().toString());
    }

    @Override
    public EmpStatus getResult(ResultSet rs, String columnName) throws SQLException {
        //需要根据从数据库中拿到的枚举的状态码返回一个枚举对象
        int code = rs.getInt(columnName);
        System.out.println("从数据库中获取的状态码:"+code);
        EmpStatus status = EmpStatus.getEmpStatusByCode(code);
        return status;
    }

    @Override
    public EmpStatus getResult(ResultSet rs, int columnIndex) throws SQLException {
        int code = rs.getInt(columnIndex);
        System.out.println("从数据库中获取的状态码:"+code);
        EmpStatus status = EmpStatus.getEmpStatusByCode(code);
        return status;
    }

    @Override
    public EmpStatus getResult(CallableStatement cs, int columnIndex) throws SQLException {
        int code = cs.getInt(columnIndex);
        System.out.println("从数据库中获取的状态码:"+code);
        EmpStatus status = EmpStatus.getEmpStatusByCode(code);
        return status;
    }
}

注册自定义TypeHandler:

1.MyBatis全局配置文件 中注册

<configuration>
	<typeHandlers>
		<typeHandler handler="com.lun.c09.other.typehandler.MyEnumEmpStatusTypeHandler" 
			javaType="com.lun.c09.other.bean.EmpStatus"/>
	</typeHandlers>
	...

2、也可以在处理某个字段的时候告诉MyBatis用什么类型处理器

  • 保存:#{empStatus,typeHandler=xxxx}
  • 查询:
<resultMap type="com.lun.bean.Employee" id="MyEmp">
		<id column="id" property="id"/>
		<result column="empStatus" property="empStatus" typeHandler="xxxxx"/>
</resultMap>

注意:如果在参数位置修改TypeHandler,应该保证保存数据和查询数据用的TypeHandler是一样的。

5、商品中心对TypeHandler的使用

使用场景是将db中的String类型字段转换为Json类型

TypeHandler的定义

public class ListOtherAttributeHandler extends AbstractJsonTypeHandler<List<OtherAttribute>> {

    @Override
    protected List<OtherAttribute> parse(String json) {
        return ItemPlatformJsonUtil.jsonToList(json, OtherAttribute.class);
    }

    @Override
    protected String toJson(List<OtherAttribute> obj) {
        return ItemPlatformJsonUtil.objToJson(obj);
    }
}

TypeHandler的注册

<resultMap id="ItemAttributeMap" type="ItemAttributeDO">
  typeHandler="ListOtherAttributeHandler"
</resultMap>

TypeHandler的使用

@TableField(value = "key_attributes", typeHandler = ListOtherAttributeHandler.class)
private List<OtherAttribute> keyAttributes;

6、白龙马对TypeHandler的使用

定义

/**
 * json字段和java对象转换器
 * 为节省存储空间,MappedTypes里的对象设置JsonInclude.Include.NON_NULL
 *
 * @param <T>
 */
@MappedTypes(value = {FareAttachmentExtraInfo.class})
@MappedJdbcTypes(value = {JdbcType.VARCHAR}, includeNullJdbcType = true)
public class JsonTypeHandler<T> extends BaseTypeHandler<T> {
    private final Class<T> clazz;

    public JsonTypeHandler(Class<T> clazz) {
        if (clazz == null) {
            throw new IllegalArgumentException("Type argument cannot be null");
        }
        this.clazz = clazz;
    }

    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, Object o, JdbcType jdbcType) throws SQLException {
        preparedStatement.setString(i, JsonUtils.toJson(o));
    }

    @Override
    public T getNullableResult(ResultSet resultSet, String s) throws SQLException {
        String value = resultSet.getString(s);
        return value == null ? null : JsonUtils.fromJson(value, clazz);
    }

    @Override
    public T getNullableResult(ResultSet resultSet, int i) throws SQLException {
        String value = resultSet.getString(i);
        return value == null ? null : JsonUtils.fromJson(value, clazz);
    }

    @Override
    public T getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
        String value = callableStatement.getString(i);
        return value == null ? null : JsonUtils.fromJson(value, clazz);
    }
}

注册

mybatis-plus:
  configuration:
    mapUnderscoreToCamelCase: true
    log-impl: ${falcon.mybatis-plus.log}
  mapper-locations: classpath*:/mapper/**/*Mapper.xml
  type-handlers-package: com.huxun.convoy.finance.dao.handler

使用

@Getter
@Setter
@Accessors(chain = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class FareAttachmentExtraInfo {
    /**
     * 承运id
     */
    private Long subOrderCarrierId;
    /**
     * 司机id
     */
    private Long driverId;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基环树学习笔记

0.前言 只因环树学习笔只因。 如有错误欢迎指出。 1.基本概念 这名字读起来感觉有点矛盾&#xff0c;怎么可能树上面有一个环呢&#xff1f; 我们把它放到百度翻译里面看看。 顾名思义&#xff0c;基环树不是一棵树&#xff0c;而是一颗假树&#xff0c;他的定义是&#…

Acwing.861 二分图的最大匹配(匈牙利算法)

题目 给定一个二分图&#xff0c;其中左半部包含nq个点(编号1n1)&#xff0c;右半部包含ng个点(编号1n2)&#xff0c;二分图共包含m条边。 数据保证任意—条边的两个端点都不可能在同一部分中。请你求出二分图的最大匹配数。 给定一个二分图G&#xff0c;在G的一个子图M中&…

软件测试基础认知

日升时奋斗&#xff0c;日落时自省 目录 1、测试需求 2、测试用例 3、软件测试BUG 4、开发模型 4.1、软件的生命周期 4.2、瀑布模型&#xff08;waterFall Model&#xff09; 4.3、螺旋模型 4.4、 增量模型 4.4.1、增量开发 4.4.2、迭代开发 4.5、敏捷开发&#xf…

【PDFBox】PDFBox操作PDF文档之创建PDF文档、加载PDF文档、添加空白页面、删除页面、获取总页数、添加文本内容、PDFBox坐标系

这篇文章&#xff0c;主要介绍PDFBox操作PDF文档之创建PDF文档、加载PDF文档、添加空白页面、删除页面、获取总页数、添加文本内容、PDFBox坐标系。 目录 一、PDFBox组件 1.1、什么是PDFBox 1.2、创建PDF文档 1.3、加载PDF文档 1.4、添加空白页面 1.5、删除某个页面 1.6…

三分钟了解 SpringBoot 的启动流程

一、前言 背景&#xff1a;最近有位开发同学说面试被问到Spring Boot 的启动流程&#xff0c;以及被问到Spring Boot 的嵌入式Web容器是什么时候加载的。如何加载的。是怎么无缝切换的。 这些问题&#xff0c;其实回答起来也是比较复杂的。我们今天就从 SpringApplication.ru…

3D设计建模软件The Foundry Modo 16对Mac和Windows的系统要求

Foundry MODO是一款功能强大的三维建模、动画和渲染软件。它为艺术家和设计师提供了一套全面的工具&#xff0c;可以用来创建令人惊叹的视觉效果。无论是制作电影、电视节目、游戏还是其他数字媒体内容&#xff0c;MODO都可以满足您的需求。 MODO具有直观的用户界面&#xff0…

redis主从配置

从redis配置&#xff1a;redis6386.conf include /data/redis/redis6380.conf #主redis配置文件路径&#xff08;这里是引用主配置文件里的配置在修改从配置&#xff09; daemonize yes #在后台启动 protected-mode no #加密保护关闭 bind 192.168.3.*** requirepas…

云主机安全-私有密钥安全认证

场景描述 云主机凭借其性价比高、生配扩容便利、运维便捷、稳定性高等优势深受用户青睐&#xff0c;越来越多的企业开始租用云主机&#xff0c;将自己的服务器、业务系统等搭建或存储到云主机上。 用户痛点 用户租用或托管的云主机&#xff0c;运维端口&#xff08;远程桌面&…

SSM框架最新整合保姆级教程(IDEA版)

SSM框架最新整合保姆级教程(IDEA版) 一、环境要求 ​ 环境&#xff1a; IDEAMySQL 5.7.19Tomcat 9Maven 3.6 要求&#xff1a; 需要熟练掌握MySQL数据库&#xff0c;Spring&#xff0c;JavaWeb及MyBatis知识&#xff0c;简单的前端知识&#xff1b; 完整代码&#xff1a;…

Openlayers实战:绘制带箭头的线

Openlayers地图中有的时候会用到这样的场景,连续画几段线段,但是要知道绘制的方向,给人以指引的提示作用。 怎么绘制呢? 在本实战中,主要的是处理线段的显示方式,在线段的拐点处附加上箭头图片,具体看实际的源代码。 效果图 源代码 /* * @Author: 大剑师兰特(xiaozhu…

4 生成全排列

4 生成全排列 作者: 赵晓鹏时间限制: 1S章节: 递归与分治 输入范例 : 无 输出范例 : Perm1(1):123 Perm1(2):123 Perm1(2):213 Perm1(2):321 Perm1(3):123 Perm1(3):132 Perm1(3):213 Perm1(3):231 Perm1(3):321 Perm1(3):312 Online Judge 1.0 #include<iostream> …

【全栈开发指南】数据权限使用配置

数据权限配置有两种方式&#xff1a; 通过系统配置界面&#xff0c;实时配置生效。 通过代码注解配置。 一、通过系统配置界面配置数据权限 系统配置的数据权限是通过系统配置界面将配置信息保存在数据库&#xff0c;然后系统启动时&#xff0c;将配置信息保存到Redis缓存来…

SpringCloud分布式搜索引擎、数据聚合、ES和MQ的结合使用、ES集群的问题

目录 数据聚合 聚合的分类 ​编辑 DSL实现Bucket聚合 ​编辑 DSL实现Metrics聚合​编辑 RestAPI实现聚合 对接前端接口​编辑 自定义分词器​编辑 Completion suggester查询 Completion suggester查询 酒店数据自动补全 实现酒店搜索框界面输入框的自动补全 数据同步问…

Codeforces Round 875 (Div. 1) A. Copil Copac Draws Trees

题意 Copil Copac 给定了一个由 n−1 条边组成的列表&#xff0c;该列表描述了一棵由 n 个顶点组成的树。他决定用下面的算法来绘制它&#xff1a; 步骤 0&#xff1a;绘制第一个顶点&#xff08;顶点1&#xff09;。转到步骤1。 步骤 1&#xff1a;对于输入中的每一条边&#…

Window10 系统 RabbitMQ的安装和简单使用

1、下载 & 安装 Erlang 因为RabbitMQ的服务端是基于 Erlang编写的&#xff0c;所以&#xff0c;首先需要安装Erlang。 1&#xff09;下载 下载地址如下&#xff1a; https://www.erlang.org/downloads此处下载比较慢&#xff0c;可以参考如下百度网盘&#xff1a; 链接…

常用的缓存工具有ehcache、memcache和redis,这里介绍spring中ehcache的配置。

常用的缓存工具有ehcache、memcache和redis&#xff0c;这里介绍spring中ehcache的配置。 1.在pom添加依赖&#xff1a; <!-- ehcache 相关依赖 --><dependency><groupId>net.sf.ehcache</groupId><artifactId>ehcache</artifactId><ve…

正则表达式测试(二)

一、中括号的语法 匹配所有的字符&#xff0c;返回一个数组,包含匹配的所有字符内容&#xff0c;按顺序展开&#xff1b; 注意&#xff1a;空格也会被匹配到 匹配所有符合的字符&#xff0c;返回一个数组。 匹配空白字符 匹配非空白字符 匹配 空白字符 非空白字符 如上所示&am…

二叉树的简单遍历

假设节点数据类如下&#xff1a; public class TreeNode {String val;TreeNode left;TreeNode right;TreeNode() { }TreeNode(String val) {this.val val;}TreeNode(String val, TreeNode left, TreeNode right) {this.val val;this.left left;this.right right;} } 二叉…

Vue3+TS+Vite创建项目,并导入Element-plus和Sass

一、vue3创建项目 1.桌面新建一个文件夹Vue3-app 打开编辑器导入文件夹&#xff0c;编辑器打开终端输入或者命令行工具cd到项目目录下输入 npm init vuelatest 回车运行 这里我选择了TSVite来开发&#xff0c;并选择安装路由 2.cd到 vue-project目录下 输入 npm install 回车…

路径规划算法:基于晶体结构优化的路径规划算法- 附代码

路径规划算法&#xff1a;基于晶体结构优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于晶体结构优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能优化…