SpringBoot第25讲:SpringBoot对TypeHandler的使用

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*/@Overridepublic 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*/@Overridepublic 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*/@Overridepublic JSONObject getNullableResult(ResultSet rs, int columnIndex) throws SQLException {String sqlJson = rs.getString(columnIndex);if (null != sqlJson){return JSONObject.parseObject(sqlJson);}return null;}@Overridepublic 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> {@Overridepublic void setParameter(PreparedStatement ps, int i, JSONObject parameter, JdbcType jdbcType) throws SQLException {if (parameter!=null){ps.setString(i, JSONObject.toJSONString(parameter));}}@Overridepublic JSONObject getResult(ResultSet rs, String columnName) throws SQLException {return JSONObject.parseObject(rs.getString(columnName));}@Overridepublic JSONObject getResult(ResultSet rs, int columnIndex) throws SQLException {return JSONObject.parseObject(rs.getString(columnIndex));}@Overridepublic 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 {@TableIdprivate 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 {@Resourceprivate 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> {/*** 定义当前数据如何保存到数据库中*/@Overridepublic void setParameter(PreparedStatement ps, int i, EmpStatus parameter, JdbcType jdbcType) throws SQLException {System.out.println("要保存的状态码:" + parameter.getCode());ps.setString(i, parameter.getCode().toString());}@Overridepublic EmpStatus getResult(ResultSet rs, String columnName) throws SQLException {//需要根据从数据库中拿到的枚举的状态码返回一个枚举对象int code = rs.getInt(columnName);System.out.println("从数据库中获取的状态码:"+code);EmpStatus status = EmpStatus.getEmpStatusByCode(code);return status;}@Overridepublic EmpStatus getResult(ResultSet rs, int columnIndex) throws SQLException {int code = rs.getInt(columnIndex);System.out.println("从数据库中获取的状态码:"+code);EmpStatus status = EmpStatus.getEmpStatusByCode(code);return status;}@Overridepublic 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>> {@Overrideprotected List<OtherAttribute> parse(String json) {return ItemPlatformJsonUtil.jsonToList(json, OtherAttribute.class);}@Overrideprotected 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;}@Overridepublic void setNonNullParameter(PreparedStatement preparedStatement, int i, Object o, JdbcType jdbcType) throws SQLException {preparedStatement.setString(i, JsonUtils.toJson(o));}@Overridepublic T getNullableResult(ResultSet resultSet, String s) throws SQLException {String value = resultSet.getString(s);return value == null ? null : JsonUtils.fromJson(value, clazz);}@Overridepublic T getNullableResult(ResultSet resultSet, int i) throws SQLException {String value = resultSet.getString(i);return value == null ? null : JsonUtils.fromJson(value, clazz);}@Overridepublic 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: truelog-impl: ${falcon.mybatis-plus.log}mapper-locations: classpath*:/mapper/**/*Mapper.xmltype-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.hqwc.cn/news/20516.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ELK-日志服务【logstash-安装与使用】

目录 【1】安装logstash logstash input 插件的作用与使用方式 【2】input --> stdin插件&#xff1a;从标准输入读取数据&#xff0c;从标准输出中输出内容 【3】input -- > file插件&#xff1a;从文件中读取数据 【4】input -- > beat插件&#xff1a;从filebe…

MongoDB 事务与数据落盘

文章目录 概要一、持久性1.1、journal log刷盘机制1.2、数据刷盘机制1.3、复制集下的写安全机制 二、隔离性总结 概要 MongoDB并不像MySQL一样天然支持多文档事务&#xff0c;其演变过程如下&#xff1a; MongoDB4.0之前只支持单文档事务&#xff0c;在单个文档上支持ACID原子…

vs2013 英文语言包

因为安装其它插件报错&#xff0c;有文档说需要更换为英文包。我电脑只有中文&#xff0c;于是开始了英文语言包下载。 官网下载 https://my.visualstudio.com/Downloads?qVisual%20Studio%202013%20Language%20Pack 刚开始没有使用官网&#xff0c;因为除了要注册登陆&…

Redis_客户端命令和数据操作(3)

目录 切换数据库 键命令 数据结构 string类型 hash类型 list类型 set类型 zset类型 查看中文value 源码等资料获取方法 切换数据库 redis数据库没有名称&#xff0c;默认有16个&#xff0c;通过0-15来标识&#xff0c;连接redis默认选择第一个数据库&#xff0c;可以…

Nacos(服务注册与发现)+SpringBoot+openFeign项目集成

&#x1f4dd; 学技术、更要掌握学习的方法&#xff0c;一起学习&#xff0c;让进步发生 &#x1f469;&#x1f3fb; 作者&#xff1a;一只IT攻城狮 &#xff0c;关注我&#xff0c;不迷路 。 &#x1f490;学习建议&#xff1a;1、养成习惯&#xff0c;学习java的任何一个技术…

安卓JNI从0到1入门教程(二)

经过上一篇《安卓JNI从0到1入门教程&#xff08;一&#xff09;》介绍&#xff0c;我们对JNI有了初步认识&#xff0c;接下来我会从ndk-build方式和cmake方式分别来介绍怎么构建native库&#xff1a; 一、ndk-build ndk-build依赖配置文件Android.mk&#xff0c;存放代码的位…

感受C++模版的所带来的魅力

一、泛型编程思想 首先我们来看一下下面这三个函数&#xff0c;如果学习过了 C函数重载 和 C引用 的话&#xff0c;就可以知道下面这三个函数是可以共存的&#xff0c;而且传值会很方便 void Swap(int& left, int& right) {int temp left;left right;right temp; }…

HarmonyOS/OpenHarmony应用开发-Stage模型UIAbility组件使用(四)

UIAbility组件与UI的数据同步 基于HarmonyOS的应用模型&#xff0c;可以通过以下两种方式来实现UIAbility组件与UI之间的数据同步。 1.EventHub&#xff1a;基于发布订阅模式来实现&#xff0c;事件需要先订阅后发布&#xff0c;订阅者收到消息后进行处理。 2.globalThis&…

macOS Sonoma 14beta 3 (23A5286i)第二个更新「附黑/白苹果镜像下载」

系统镜像下载&#xff1a; 系统介绍 黑果魏叔 7 月12 日消息&#xff0c;苹果今天发布 macOS Sonoma 14.0 Beta 3&#xff08;内部版本号&#xff1a;23A5286i&#xff09;第二个更新。 目前尚不清楚苹果为什么要发布 macOS Sonoma Beta 3 的第二个版本&#xff0c;但它可能…

MQ的优劣势及RabbitMQ相关概念

一&#xff0c;MQ 1&#xff0c;MQ 的概念 MQ 全称 Message Queue&#xff08;消息队列&#xff09;&#xff0c;是用来存储消息数据的容器&#xff08;是一个中间件&#xff09;&#xff0c;一般用于分布式系统间的通信&#xff1b;MQ主要介于生产者和消费者之间&#xff0c…

微服务系列文章之 Redisson实现分布式锁

一、高效分布式锁 当我们在设计分布式锁的时候&#xff0c;我们应该考虑分布式锁至少要满足的一些条件&#xff0c;同时考虑如何高效的设计分布式锁&#xff0c;这里我认为以下几点是必须要考虑的。 1、互斥 在分布式高并发的条件下&#xff0c;我们最需要保证&#xff0c;同…

Redis_设置密码

目录 一、临时设置密码 二、永久设置密码 源码等资料获取方法 一、临时设置密码 # 获取密码 config get requirepass # 设置密码为123456 config set requirepass 123456 # 验证密码。当设置密码后&#xff0c;进入redis就要验证 auth 密码 # 取消密码 config set requirep…