【微服务】mybatis typehandler使用详解

目录

一、前言

二、TypeHandler简介

2.1 什么是TypeHandler

2.1.1 TypeHandler特点

2.2 TypeHandler原理

2.3 mybatis自带的TypeHandler

三、环境准备

3.1 准备一张数据表

3.2 搭建一个springboot工程

3.2.1 基础依赖如下

3.2.2 核心配置文件

3.2.3 测试接口

四、TypeHandler在项目中的使用

4.1 时间类型转换问题

4.1.1 添加自定义TypeHandler

4.1.2 添加扫描

4.1.3 xml配置字段引用自定义TypeHandler

4.1.4 接口效果测试

4.1.5 新增数据

4.2 常用的TypeHandler

4.2.1 List类型的处理

4.2.2 枚举类型的处理

4.2.3 json类型的处理

4.3 注意事项

4.3.1 自定义TypeHandler步骤

4.3.2 自定义TypeHandler使用注意事项

五、写在文末


一、前言

在springboot为基础框架的微服务开发中,经常要涉及到数据库的操作,此时mybatis或mybatis-plus便成为技术选型中必不可少的组件。基本上来说,springboot+mybatis成为很多公司微服务项目的基础选型。使用mybatis的好处很多,这里不再赘述了,接下来要探讨的是在业务代码中,如何使用mybtais完成一些字段的类型转换。

二、TypeHandler简介

2.1 什么是TypeHandler

TypeHandler,即:类型转换器,在mybatis中用于实现 java类型 和 JDBC类型 的相互转换,在 Java 与mybatis的整合开发中,TypeHandler 是 MyBatis 框架的一个核心组件,用于实现数据库表字段类型与和Java 数据类型之间的相互转换。这个组件的存在,它允许开发人员自定义类型处理器,以满足特定场景下的业务需求。

TypeHandler 作用是在 MyBatis 执行 数据查询或更新操作时,将数据库中的列值转换为 Java 对象,并在将 Java 对象写入数据库时执行相反的转换。它提供了一种灵活且可扩展的方式,用于处理数据库类型与 Java 类型之间的映射关系。

 

2.1.1 TypeHandler特点

TypeHandler在实际项目中该如何使用呢?需要搞清它的特点才能更好的掌握其使用场景,以下列举了TypeHandler 一些特点和使用方式;

  • 类型转换,TypeHandler 负责将数据库表中的字段值转换为 Java 对象,并将 Java 对象转换为数据库表中可被接受的类型;
  • 映射规则,TypeHandler 是基于映射规则的(在下文的表中详细给出了常用的映射规则),即在 MyBatis 映射文件中,需要指定表字段和对象属性之间的映射关系,这样TypeHandler才能正确进行类型转换;
  • 支持自定义类型转换器,MyBatis 提供了一些默认的类型处理器,例如整数、字符串、日期等常见的数据类型,同时,开发人员可根据业务需要自定义类型处理器,实现特定类型的转换逻辑,比如你可以在自定义处理器中处理字段加密的逻辑;
  • 注册类型处理器,自定义类型处理器需要在 MyBatis 配置文件中进行注册,这样MyBatis 在执行数据库操作时才能找到并使用它们;
  • 支持复杂的类型,TypeHandler 不仅可以处理基本数据类型,还可以处理复杂类型,例如枚举、自定义对象等;

2.2 TypeHandler原理

还记得在使用jdbc操作数据库的时候,我们是如何设置一个查询的sql语句中的字段参数值的吗,没错,就是使用PrepareStatement对象中的set方法。

对mybatis原理有所了解的同学应该不陌生,其实mybatis框架就是对jdbc的完美封装。所以,mybatis使用 prepareStatement 进行参数设置的时候,需要通过 TypeHandler 将传入的java参数设置成合适的jdbc类型参数,这个过程实际上是通过调用 PrepareStatement 不同的set方法实现的。

String sql = "SELECT * tb_user where usernmae=? and pwd=?";
PreparedStatement pstmt = connection.prepareStatement(sql);
pstmt.setString(1, "jerry");
pstmt.setString(1, "root@321");
ResultSet rs = pstmt.executeQuery();

在获取到返回结果之后,也需要将返回的结果转换成需要的java数据类型,这时候是通过调用 ResultSet 对象不同类型的get方法实现的。比如我们在得到jdbc的结果集之后,会通过下面的方式获得某个字段的结果:

String id=rs.getString("id");
int number=rs.getInt("number");

所以不同类型的 TypeHandler 其实就是调用 PrepareStatement 和 ResultSet 的不同方法来进行类型的转换,有些时候会在调用 PrepareStatement 和 ResultSet 的相关方法之前,可以对传入的参数进行一定的处理.。

而当我们没有指定 TypeHandler 时 ,mybatis 会根据入参类型和返回值类型调用默认的 TypeHandler 进行处理。

对于一个 TypeHandler来说, 需要配置 java类型(javaType) 和 JDBC类型(jdbcType),TypeHandler 作用就是实现这两种类型的转换,在传入参数为指定的Java类型时,通过TypeHandler ,将其转换为指定的JDBC类型,当返回值为指定JDBC类型时,TypeHandler 再将其转换为配置的Java类型。

2.3 mybatis自带的TypeHandler

MyBatis 默认已经内置了一些常用的TypeHandler,大部分场景下无须显式声明 jdbcType 和 javaType,或者用 TypeHandler 去指定对应的字段来实现数据类型转换,因为 mybatis会自动探测待映射的数据类型,从而自动选择合适的 TypeHandler。下面列举了mybatis常用的TypeHandler

类型处理器Java类型JDBC类型
BooleanTypeHandlerjava.lang.Boolean,boolean数据库兼容的 BOOLEAN
ByteTypeHandlerjava.lang.Byte, byte数据库兼容的 NUMERIC 或 BYTE
ShortTypeHandlerjava.lang.Short, short数据库兼容的 NUMERIC 或 SHORT INTEGER
IntegerTypeHandlerjava.lang.Integer, int数据库兼容的 NUMERIC 或 INTEGER
LongTypeHandlerjava.lang.Long, long数据库兼容的 NUMERIC 或 LONG INTEGER
FloatTypeHandlerjava.lang.Float, float数据库兼容的 NUMERIC 或 FLOAT
DoubleTypeHandlerjava.lang.Double,double数据库兼容的 NUMERIC 或 DOUBLE
BigDecimalTypeHandlerjava.math.BigDecimal数据库兼容的 NUMERIC 或 DECIMAL
StringTypeHandlerjava.lang.StringCHAR, VARCHAR
ClobReaderTypeHandlerjava.io.Reader-
ClobTypeHandlerjava.lang.StringCLOB, LONGVARCHAR
NStringTypeHandlerjava.lang.StringNVARCHAR, NCHAR
NClobTypeHandlerjava.lang.StringNCLOB
BlobInputStreamTypeHandlerjava.io.InputStream-
ByteArrayTypeHandlerbyte[]数据库兼容的字节流类型
BlobTypeHandlerbyte[]BLOB, LONGVARBINARY
DateTypeHandlerjava.util.DateTIMESTAMP
DateOnlyTypeHandlerjava.util.DateDATE
TimeOnlyTypeHandlerjava.util.DateTIME
SqlTimestampTypeHandlerjava.sql.TimestampTIMESTAMP
SqlDateTypeHandlerjava.sql.DateDATE
SqlTimeTypeHandlerjava.sql.TimeTIME
ObjectTypeHandlerAnyOTHER 或未指定类型
EnumTypeHandlerEnumeration TypeVARCHAR-任何兼容的字符串类型,存储枚举的名称(而不是索引)
EnumOrdinalTypeHandlerEnumeration Type任何兼容的 NUMERIC 或 DOUBLE 类型,存储枚举的索引(而不是名称)

三、环境准备

3.1 准备一张数据表

为了方便后面实验演示,这里准备一张数据表,建表sql如下

CREATE TABLE `tb_user` (`id` int(12) NOT NULL,`user_name` varchar(32) DEFAULT NULL,`pwd` varchar(64) DEFAULT NULL,`user_image` blob,`create_time` datetime DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

3.2 搭建一个springboot工程

技术栈,springboot+mybatis+mysql

3.2.1 基础依赖如下

    <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.5</version><relativePath/> <!-- lookup parent from repository --></parent><dependencies><dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>2.0.23</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.2</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.17</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency></dependencies><build><finalName>hook-service</finalName><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>

3.2.2 核心配置文件

主要是配置连接mysql与mybatis的配置

server:port: 8088spring:application:name: user-servicedatasource:url: jdbc:mysql://IP:3306/db-basedriverClassName: com.mysql.jdbc.Driverusername: rootpassword: rootmybatis:mapper-locations: classpath:mapper/*.xml#目的是为了省略resultType里的代码量type-aliases-package: com.congge.entityconfiguration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

3.2.3 测试接口

@RestController
@RequestMapping("/user")
public class TbUserController {@Autowiredprivate TbUserService tbUserService;//http:localhost:8088/user/get?id=1@GetMapping("/get")public TbUser getById(@RequestParam int id){return tbUserService.getById(id);}}

业务实现逻辑比较简单就忽略了,最后在mybatis的查询文件中,一般建议配置一resultMap,在这个resultMap中,编写Java实体类属性与数据表的映射关系对应规则,而后面将要说到的自定义TypeHandler也将在这里进行配置,参考如下:

<?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="com.congge.dao.TbUserDao"><resultMap id="baseMap" type="com.congge.entity.TbUser"><id column="id" jdbcType="INTEGER" property="id" /><result column="user_name" jdbcType="VARCHAR" property="userName" /><result column="pwd" jdbcType="VARCHAR" property="pwd" /><result column="user_image" jdbcType="BLOB" property="userImage" /><result column="create_time" jdbcType="TIMESTAMP" property="createTime" /></resultMap><select id="getById" resultMap="baseMap">select * from tb_user where id = #{id}</select>
</mapper>

启动工程后,测试一下接口,可以看到直接报错了

为什么会出现这个问题呢?这该如何处理呢?

四、TypeHandler在项目中的使用

4.1 时间类型转换问题

在上面的接口测试中,发现接口返回的时间类型并不是我们想要的,遇到这个问题,就是数据库的字段类型与java中的时间字段类型不匹配,或者说无法通过默认的TypeHandler进行转换了,该如何解决呢?

4.1.1 添加自定义TypeHandler

自定义一个TypeHandler类,只需要继承BaseTypeHandler类,然后重写里面的setNonNullParameter和getNullableResult方法,在重写的方法里面编写相关的业务逻辑即可。

import org.apache.ibatis.type.*;import java.sql.*;/*** 自定义类型转换:将数据库中的日期类型,转换成long类型的时间戳** 三种注册方式:* 1.直接在 result标签中,指定typeHandler,如@Result(property = "createTime", column = "create_time", jdbcType = JdbcType.TIMESTAMP, typeHandler = Timestamp2LongHandler.class)* 2.在SqlSessionFactory实例中,注册 在SqlSessionFactory实例中.setTypeHandlers(new Timestamp2LongHandler());* 3.mapper 的xml配置,<typeHandler handler="com.congge.handler.Timestamp2LongHandler"/>*/
@MappedTypes(value = Long.class)
@MappedJdbcTypes(value = {JdbcType.DATE, JdbcType.TIME, JdbcType.TIMESTAMP})
public class Timestamp2LongHandler extends BaseTypeHandler<Long> {/*** 将java类型,转换为jdbc类型** @param preparedStatement* @param i* @param aLong             毫秒时间戳* @param jdbcType          db字段类型* @throws SQLException*/@Overridepublic void setNonNullParameter(PreparedStatement preparedStatement, int i, Long aLong, JdbcType jdbcType) throws SQLException {if (jdbcType == JdbcType.DATE) {preparedStatement.setDate(i, new Date(aLong));} else if (jdbcType == JdbcType.TIME) {preparedStatement.setTime(i, new Time(aLong));} else if (jdbcType == JdbcType.TIMESTAMP) {preparedStatement.setTimestamp(i, new Timestamp(aLong));}}@Overridepublic Long getNullableResult(ResultSet resultSet, String s) throws SQLException {return parse2time(resultSet.getObject(s));}@Overridepublic Long getNullableResult(ResultSet resultSet, int i) throws SQLException {return parse2time(resultSet.getObject(i));}@Overridepublic Long getNullableResult(CallableStatement callableStatement, int i) throws SQLException {return parse2time(callableStatement.getObject(i));}private Long parse2time(Object value) {if (value instanceof Date) {return ((Date) value).getTime();} else if (value instanceof Time) {return ((Time) value).getTime();} else if (value instanceof Timestamp) {return ((Timestamp) value).getTime();}return null;}
}

对自定义的TypeHandler类,对类上的两个注解做如下补充说明:

  • @MappedJdbcTypes,代表对应的数据库中字段的类型;

  • @MappedTypes,代表要转换后的的JavaBean中的属性(对象)类型;

4.1.2 添加扫描

当一个自定义的TypeHandler编写完成后,如何生效并被使用呢?在上述代码的注释中,其实给出了好几种方式,一般比较常用的像注册到bean中,或者配置到mybatis的全局配置文件中,这里提供另一种方式,就是直接在application.yaml中配置一下扫描路径也可以。(最后一行)

mybatis:mapper-locations: classpath:mapper/*.xml#目的是为了省略resultType里的代码量type-aliases-package: com.congge.entityconfiguration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpltype-handlers-package: com.congge.handler

4.1.3 xml配置字段引用自定义TypeHandler

然后再在上述的UserMapper.xml文件中,在resultMap中的createTime字段映射上引用上述的TypeHandler

<resultMap id="baseMap" type="com.congge.entity.TbUser"><id column="id" jdbcType="INTEGER" property="id" /><result column="user_name" jdbcType="VARCHAR" property="userName" /><result column="pwd" jdbcType="VARCHAR" property="pwd" /><result column="user_image" jdbcType="BLOB" property="userImage" /><result column="create_time" jdbcType="TIMESTAMP" property="createTime" /><result property="createTime" column="create_time" typeHandler="com.congge.handler.Timestamp2LongHandler"/></resultMap>

4.1.4 接口效果测试

以上配置完成后,再次启动工程调用接口,此时可以看到返回的时间可以正确的被转换为Long类型

4.1.5 新增数据

如果是新增数据时,由于在自定义TypeHandler已经对传入数据库的时间字段进行了转换处理,所以按照下面的操作即可。

public int save(TbUser tbUser) {Integer count = tbUserDao.selectCount();Integer id = count+1;tbUser.setId(count+1);tbUser.setCreateTime(System.currentTimeMillis());tbUserDao.save(tbUser);return id;
}

再在xml文件的sql里面,针对createTime字段添加自定义的TypeHandler

<insert id="save">insert into tb_user(id,user_name,pwd,user_image,create_time) values(#{id},#{userName},#{pwd},#{userImage},#{createTime,javaType=Long,jdbcType=TIMESTAMP,typeHandler=com.congge.handler.Timestamp2LongHandler})</insert>

然后调用数据插入接口时,就能正常的被解析为数据库的TIMESTAMP类型

4.2 常用的TypeHandler

在真实的业务场景中,涉及到需要进行转换的字段类型可能非常多,下面总结了一些常用的开发中使用到的数据类型转换的自定义TypeHandler。

4.2.1 List<String>类型的处理

当处理 List 类型时,可使用自定义的TypeHandler 将 List 对象转换为字符串存储,或者从数据库中读取字符串并还原为 List 对象。

比如在一个用户表中,关联了多个角色,这个角色字段relate_roles,存储多个数据时以","分割,而在用户BizUser对象中,包含了一个List<String>类型的属性与relate_roles对应,我们就可以自定义一个handler来实现入库时和查询映射到对象属性时的转换。

mysql建表语句

CREATE TABLE `biz_user` (`id` int(12) NOT NULL,`user_name` varchar(32) DEFAULT NULL,`relate_roles` varchar(132) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

自定义以TypeHandler类,代码如下

import org.apache.ibatis.type.BaseTypeHandler;import java.sql.PreparedStatement;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;import org.apache.ibatis.type.*;import java.sql.*;//@MappedTypes(value = Long.class)
//@MappedJdbcTypes(value = {JdbcType.DATE, JdbcType.TIME, JdbcType.TIMESTAMP})public class StringListTypeHandler extends BaseTypeHandler<List<String>> {private static final String DELIMITER = ",";@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, List<String> parameter, JdbcType jdbcType) throws SQLException {// 将 List<String> 转换为以逗号分隔的字符串String rolesString = String.join(DELIMITER, parameter);ps.setString(i, rolesString);}@Overridepublic List<String> getNullableResult(ResultSet rs, String columnName) throws SQLException {// 从 ResultSet 中获取字符串值String rolesString = rs.getString(columnName);// 转换为 List<String>return parseRolesString(rolesString);}@Overridepublic List<String> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {String rolesString = rs.getString(columnIndex);return parseRolesString(rolesString);}@Overridepublic List<String> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {String rolesString = cs.getString(columnIndex);return parseRolesString(rolesString);}private List<String> parseRolesString(String rolesString) {if (rolesString == null || rolesString.isEmpty()) {return Collections.emptyList();}return Arrays.asList(rolesString.split(DELIMITER));}
}

最后在mybatis的配置文件中做一下配置即可

<resultMap id="baseMap" type="com.congge.entity.BizUser"><id column="id" jdbcType="INTEGER" property="id" /><result column="user_name" jdbcType="VARCHAR" property="userName" /><result column="relate_roles" jdbcType="VARCHAR" property="relateRoles" typeHandler="com.congge.handler.StringListTypeHandler" /></resultMap>

接口测试,提前在数据库中准备一条数据,然后通过接口调用,可以看到下面的效果,说明成功转换

4.2.2 枚举类型的处理

假设我们有一个用户的实体类对象 BizUser,其中包含一个 SexEnum枚举类型的状态字段,在数据库中,我们将状态字段存储为一个字符串类型的列,例如 gender

@Getter
public enum SexEnum {MALE(1, "男"),FEMALE(0, "女");private int id;private String name;SexEnum(int id, String name) {this.id = id;this.name = name;}public static SexEnum getSexById(int id) {for (SexEnum sex : SexEnum.values()) {if (sex.getId() == id) {return sex;}}return null;}}

实体类

@Getter
@Setter
public class BizUser {private int id;private String userName;private List<String>  relateRoles;private SexEnum sex;}

可以使用自定义 TypeHandler 来处理 UserStatus枚举类型与数据库列值之间的转换,参考如下示例的 TypeHandler 实现

public class SexEnumTypeHandler extends BaseTypeHandler<SexEnum> {@Overridepublic SexEnum getNullableResult(ResultSet rs, String columnName) throws SQLException {// 从 ResultSet 中获取列值int data = rs.getInt(columnName);// 将列值转换为枚举对象return SexEnum.getSexById(data);}@Overridepublic SexEnum getNullableResult(ResultSet rs, int columnIndex) throws SQLException {int data = rs.getInt(columnIndex);return SexEnum.getSexById(data);}@Overridepublic SexEnum getNullableResult(CallableStatement callableStatement, int columnIndex) throws SQLException {int data = callableStatement.getInt(columnIndex);return SexEnum.getSexById(data);}@Overridepublic void setNonNullParameter(PreparedStatement preparedStatement, int i, SexEnum status, JdbcType jdbcType) throws SQLException {// 将枚举值的code存储到数据库中preparedStatement.setInt(i, status.getId());}
}

4.2.3 json类型的处理

假如在biz_user表中有一个描述的信息json,是BizUser对象关联的一个对象存储进来的,如下:BizUser对象中持有一个Content的对象引用

@Getter
@Setter
public class BizUser {private int id;private String userName;private List<String>  relateRoles;private SexEnum sex;private Content content;
}

Content对象

@Getter
@Setter
public class Content {private String desc;private String level;private String hit;
}

对应的数据表如下

CREATE TABLE `biz_user` (`id` int(12) NOT NULL,`user_name` varchar(32) DEFAULT NULL,`relate_roles` varchar(132) DEFAULT NULL,`gender` int(1) DEFAULT NULL,`content` json DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

要完成这样一个场景下字段到实体对象的映射,可以参考下面的做法,自定义一个ObjectJSONTypeHandler

import com.congge.entity.Content;
import com.google.gson.Gson;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;public class ObjectJSONTypeHandler extends BaseTypeHandler<Content> {private Gson gson = new Gson();@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, Content parameter, JdbcType jdbcType) throws SQLException {ps.setString(i, gson.toJson(parameter));}@Overridepublic Content getNullableResult(ResultSet rs, String columnName) throws SQLException {return gson.fromJson(rs.getString(columnName), Content.class);}@Overridepublic Content getNullableResult(ResultSet rs, int columnIndex) throws SQLException {return gson.fromJson(rs.getString(columnIndex), Content.class);}@Overridepublic Content getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {return gson.fromJson(cs.getString(columnIndex), Content.class);}
}

最后再在mybatis的映射文件中将自定义TypeHandler加进去即可

<resultMap id="baseMap" type="com.congge.entity.BizUser"><id column="id" jdbcType="INTEGER" property="id" /><result column="user_name" jdbcType="VARCHAR" property="userName" /><result column="relate_roles" jdbcType="VARCHAR" property="relateRoles" typeHandler="com.congge.handler.StringListTypeHandler" /><result column="gender" jdbcType="INTEGER" property="sex" typeHandler="com.congge.handler.SexEnumTypeHandler" /><result column="content" property="content" typeHandler="com.congge.handler.ObjectJSONTypeHandler" /></resultMap>

测试一下接口可以看到下面的效果

4.3 注意事项

4.3.1 自定义TypeHandler步骤

通过上面的案例介绍了几种常用的TypeHandler类型处理方式,如果还有其他的特殊场景也可以参考进行处理,总结一下自定义的TypeHandler使用流程主要分为下面几个步骤:

  1. 确定数据库字段的类型,以及要转换为JavaBean中的属性类型;

  2. 自定义一个TypeHandler类,继承这个BaseTypeHandler抽象类;

  3. 注册TypeHandler(ssm项目中,如果有mybatis的全局配置文件的话需要注册一下);

  4. 再在mybatis的业务xml文件中,属性与字段映射的位置,将需要转换的字段和属性使用这个自定义TypeHandler即可;

通常来说,通过上面的几步,就可以完成插入数据时,数据库字段是预期类型的值,而查询数据时,接口返回的是java中预期类型的值,但是在某些情况下,比如像上面的enum这种枚举类型的,还需要再返回值中进一步处理;

4.3.2 自定义TypeHandler使用注意事项

在 MyBatis 中,如果你使用了自定义 TypeHandler 进行类型转换,下面有一些注意事项和建议:

数据库字段与 Java 对象属性类型的匹配

如果数据库字段的类型与自定义 TypeHandler 处理的 Java 对象类型不一致,可能会导致转换错误或数据丢失。

处理 NULL 值

在自定义 TypeHandler 中, NULL 值的处理很重要,可通过判断传入的参数或结果集中的值是否为 NULL,并在处理过程中进行相应的处理。例如,可以将 NULL 值转换为 Java 对象的默认值,或者通过特定的标识值来表示 NULL。

注册 TypeHandler

确保在 MyBatis 的配置文件中正确注册自定义的 TypeHandler,如果没有正确注册,MyBatis 将无法找到并使用该 TypeHandler而导致类型转换失败。

测试和验证

在使用自定义 TypeHandler 进行类型转换之前,建议进行充分的测试和验证。确保自定义 TypeHandler 能够正确地将数据库类型转换为相应的 Java 对象类型,并且在查询和更新操作中能够正常工作。

了解官方提供的默认 TypeHandler

MyBatis 提供了一些默认的 TypeHandler,用于常见的数据类型转换,如字符串、整数、日期等。在自定义 TypeHandler 之前,建议先查看官方文档,了解是否已经有现成的 TypeHandler 可以满足你的需求。

可重用性和扩展性

在设计自定义 TypeHandler 时,考虑其可重用性和扩展性。如果有多个字段需要进行相同的类型转换,可以将转换逻辑封装在一个通用的 TypeHandler 中,并在映射文件中多次引用它。另外,可以通过继承或组合的方式扩展已有的 TypeHandler,以满足不同的业务需求。

注意线程安全性

自定义 TypeHandler 应该是线程安全的,因为 MyBatis 在多线程环境下可能会同时使用同一个 TypeHandler 实例进行类型转换。确保在实现自定义 TypeHandler 时考虑线程安全性,并采取相应的线程安全措施,如使用线程局部变量或同步机制。

五、写在文末

自定义TypeHandler在实际的开发中可以说是一个很常见的问题,虽然并不难,但是如果不了解其底层原理得话,使用起来很容易陷入忙乱中,有必要深入学习和掌握,本篇到此结束,感谢观看。

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

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

相关文章

apache 模式、优化、功能 与 nginx优化、应用

一、I/O模型——Input/Output模型 1.同步/异步 A程序需要调用B程序的某一个功能&#xff0c;A发送一个请求需要B完成一个任务 同步&#xff1a;B不会主动去通知A是否完成需要A自己去问 异步&#xff1a;B会主动通知A是否完成 2.阻塞/非阻塞 A发送一个请求需要B完成一个任务 …

【Oracle】玩转Oracle数据库(五):PL/SQL编程

前言 嗨&#xff0c;各位数据库达人&#xff01;准备好迎接数据库编程的新挑战了吗&#xff1f;今天我们要探索的是Oracle数据库中的神秘魔法——PL/SQL编程&#xff01;&#x1f52e;&#x1f4bb; 在这篇博文【Oracle】玩转Oracle数据库&#xff08;五&#xff09;&#xff1…

算法打卡day1|数组篇|Leetcode 704.二分查找、27.移除元素

数组理论基础 数组是存放在连续内存空间上的相同类型数据的集合&#xff0c;可以方便的通过下标索引的方式获取到下标下对应的数据。 1.数组下标都是从0开始的。 2.数组内存空间的地址是连续的。 正是因为数组的在内存空间的地址是连续的&#xff0c;所以我们在删除或者增添…

Django定时任务之django_apscheduler使用

Django定时任务之django_apscheduler使用 今天在写一个任务需求时需要用到定时任务来做一部分数据处理与优化&#xff0c;于是在了解完现有方法&#xff0c;结合自己需求决定使用django_apscheduler&#xff0c;记录一下过程&#xff0c;有几篇值得参考的文章放在结尾&#xf…

Qt应用软件【协议篇】MQTT官方源码编译安装

文章目录 QT官方代码选择对应的版本Qt Creator编译代码代码下载与编译安装mqtt命令行方式编译与安装代码示例QT官方代码 https://github.com/qt/qtmqtt/tree/5.15.2 选择对应的版本 我们可以在github上切换分支,切换到我们需要的版本上 Qt Creator编译代码 代码下载与编译…

LeetCode 1637.两点之间不包含任何点的最宽垂直区域

给你 n 个二维平面上的点 points &#xff0c;其中 points[i] [xi, yi] &#xff0c;请你返回两点之间内部不包含任何点的 最宽垂直区域 的宽度。 垂直区域 的定义是固定宽度&#xff0c;而 y 轴上无限延伸的一块区域&#xff08;也就是高度为无穷大&#xff09;。 最宽垂直区…

Python入门必学:单引号、双引号与三引号的差异与应用

Python入门必学&#xff1a;单引号、双引号与三引号的差异与应用 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程 &#x1f448; 希望得…

bugku3

前女友 md5 进去又是讴歌乱进的东西 源代码 看到code.txt,访问一下 <?php if(isset($_GET[v1]) && isset($_GET[v2]) && isset($_GET[v3])){$v1 $_GET[v1];$v2 $_GET[v2];$v3 $_GET[v3];if($v1 ! $v2 && md5($v1) md5($v2)){if(!strcmp($v3,…

Unity与Android交互通信系列(5)

在前述文章中&#xff0c;已经使用了AndroidJavaProxy代理接口&#xff0c;本节我们将详细的介绍AndroidJavaProxy代理的用法。正如其名&#xff0c;AndroidJavaProxy是一个代理&#xff0c;它在Android端代码与Unity端代码交互中起一个桥接作用。其一般用法为在Java代码中定义…

牛客周赛 Round 34 解题报告 | 珂学家 | 构造思维 + 置换环

前言 整体评价 好绝望的牛客周赛&#xff0c;彻底暴露了CF菜菜的本质&#xff0c;F题没思路&#xff0c;G题用置换环骗了50%, 这大概是唯一的亮点了。 A. 小红的字符串生成 思路: 枚举 a,b两字符在相等情况下比较特殊 a, b input().split() if a b:print (2)print (a)pri…

三种方法用c语言求最大公约数以及最小公倍数

学习目标&#xff1a; 掌握求最大公约数&#xff08;最小公倍数&#xff09;的三种基本方法 学习内容&#xff1a; 1.一大一小取其小&#xff0c;剖根问底取公约 意思是从一大一小两个数当中&#xff0c;我们取较小的那个数&#xff08;min&#xff09;进行剖析&#xff0c;试…

django rest framework 学习笔记-实战商城

01项目环境搭建_哔哩哔哩_bilibili 本博客借鉴至大佬的视频学习笔记 # 创建项目 django-admin startproject MyShop# 创建app E:\desktop\my_drf\MyShop>django-admin startapp goodsE:\desktop\my_drf\MyShop>django-admin startapp orderE:\desktop\my_drf\MyShop>…

搭建私有Git服务器:GitLab部署详解

引言&#xff1a; 为了方便团队协作和代码管理&#xff0c;许多组织选择搭建自己的私有Git服务器。GitLab是一个集成了Git版本控制、项目管理、代码审查等功能的开源平台&#xff0c;是搭建私有Git服务器的理想选择。 目录 引言&#xff1a; 一、准备工作 在开始部署GitLab之…

Linux之ACL权限chmod命令

一. chmod命令 chmod命令来自英文词组change mode的缩写&#xff0c;其功能是改变文件或目录权限的命令。默认只有文件的所有者和管理员可以设置文件权限&#xff0c;普通用户只能管理自己文件的权限属性。 设置权限时可以使用数字法&#xff0c;亦可使用字母表达式&#xff0…

RisingWave最佳实践-利用Dynamic filters 和 Temporal filters 实现监控告警

心得的体会 刚过了年刚开工&#xff0c;闲暇之余调研了分布式SQL流处理数据库–RisingWave&#xff0c;本人是Flink&#xff08;包括FlinkSQL和Flink DataStream API&#xff09;的资深用户&#xff0c;但接触到RisingWave令我眼前一亮&#xff0c;并且拿我们生产上的监控告警…

Android Gradle 开发与应用 (一) : Gradle基础

1. Gradle是什么 Gradle是一个通用的构建工具&#xff0c;支持诸多主要的 IDE&#xff0c;包括 Android Studio、IntelliJ IDEA、Visual Studio 等 Gradle 的底层实现(核心引擎和框架)其实是用 Java 编写的开发者通常使用 Groovy 或 Kotlin 来编写构建脚本 1.1 那么为什么Gra…

Order By Limit不稳定性

文章目录 前置解决不确定性场景1 Order By索引1.1 背景1.2 不确定性产生原因1.2.1 正常情况下1.2.2 但是 1.3 补充1.4 场景1总结 场景2 Order by id2.1 背景2.2 不会产生不确定性原因1原因2 2.3 推荐使用方式 场景3 filesort3.1 背景3.2 不确定性产生原因3.3 内存排序和磁盘临时…

Jmeter基础(3) 发起一次请求

目录 Jmeter 一次请求添加线程组添加HTTP请求添加监听器 Jmeter 一次请求 用Jmeter进行一次请求的过程&#xff0c;需要几个步骤呢&#xff1f; 1、添加线程组2、添加HTTP请求3、添加监听器&#xff0c;查看结果树 现在就打开jmeter看下如何创建一个请求吧 添加线程组 用来…

【Java程序设计】【C00282】基于Springboot的校园台球厅人员与设备管理系统(有论文)

基于Springboot的校园台球厅人员与设备管理系统&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的校园台球厅人员与设备管理系统 本系统分为系统功能模块、管理员功能模块以及用户功能模块。 系统功能模块&#xf…

【数据结构与算法】(16)基础算法 之哈希表 相关示例 详细代码讲解

目录 3.6 哈希表第一版生成 hashCode思考习题E01. 两数之和-Leetcode 1E02. 无重复字符的最长字串-Leetcode 3E03. 字母异位词分组-Leetcode 49E04. 判断有没有重复元素-Leetcode 217E05. 找出出现一次的数字-Leetcode 136E06. 判断字母异位词-Leetcode 242E07. 第一个不重复字…