【微服务】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.mzph.cn/news/702047.shtml

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

相关文章

JAVA工程师面试专题-《Mysql》篇

目录 一、基础 1、mysql可以使用多少列创建索引&#xff1f; 2、mysql常用的存储引擎有哪些 3、MySQL 存储引擎&#xff0c;两者区别 4、mysql默认的隔离级别 5、数据库三范式 6、drop、delete 与 truncate 区别&#xff1f; 7、IN与EXISTS的区别 二、索引 1、索引及索…

接口测试实战--自动化测试流程

一、项目前期准备 常见项目软件架构: springMvc:tomcat里运行war包(在webapps目录下) springboot:java -jar xx.jar -xms(**) 运行参数 springCloud:k8s部署,使用kubectl create -f xx.yaml 接口自动化测试介入需越早越好,只要api定义好就可以编写自动化脚本; 某个…

五种多目标优化算法(MOBA、NSWOA、MOJS、MOAHA、MOPSO)性能对比(提供MATLAB代码)

一、5种多目标优化算法简介 多目标优化算法是用于解决具有多个目标函数的优化问题的一类算法。其求解流程通常包括以下几个步骤: 1. 定义问题:首先需要明确问题的目标函数和约束条件。多目标优化问题通常涉及多个目标函数,这些目标函数可能存在冲突,需要在不同目标之间进…

1_怎么看原理图之GPIO和门电路笔记

一、GPIO类 如下图&#xff1a;芯片输出高电平/3.3V&#xff0c;LED亮&#xff1b;当芯片输出低电平&#xff0c;则LED暗 如下图&#xff1a;输入引脚&#xff0c;当开关闭合&#xff0c;则输入为低电平/0V&#xff0c;当开关打开&#xff0c;则输入为高电平/3.3V 现在的引脚都…

Stable Diffusion 3 发布及其重大改进

1. 引言 就在 OpenAI 发布可以生成令人瞠目的视频的 Sora 和谷歌披露支持多达 150 万个Token上下文的 Gemini 1.5 的几天后&#xff0c;Stability AI 最近展示了 Stable Diffusion 3 的预览版。 闲话少说&#xff0c;我们快来看看吧&#xff01; 2. 什么是Stable Diffusion…

微信小程序 uniapp+vue餐厅美食就餐推荐系统

本论文根据系统的开发流程以及一般论文的结构分为三个部分&#xff0c;第一个部分为摘要、外文翻译、目录&#xff1b;第二个部分为正文&#xff1b;第三个部分为致谢和参考文献。其中正文部分包括&#xff1a; &#xff08;1&#xff09;绪论&#xff0c;对课题背景、意义、目…

uvloop,一个强大的 Python 异步IO编程库!

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站零基础入门的AI学习网站~。 目录 ​编辑 前言 什么是uvloop库&#xff1f; 安装uvloop库 使用uvloop库 uvloop库的功能特性 1. 更…

Spring ReflectionUtils 反射工具介绍和使用

一、ReflectionUtils 在 Java 中&#xff0c;反射&#xff08;Reflection&#xff09;是一种强大的机制&#xff0c;允许程序在运行时动态地检查类、获取类的信息、调用类的方法、访问或修改类的属性等。Java 的反射机制提供了一组类和接口&#xff0c;位于 java.lang.reflect…

【二分查找】【浮点数的二分查找】【二分答案查找】

文章目录 前言一、二分查找&#xff08;Binary Search&#xff09;二、浮点数的二分查找三、二分答案总结 前言 今天记录一下基础算法之二分查找 一、二分查找&#xff08;Binary Search&#xff09; 二分查找&#xff08;Binary Search&#xff09;是一种在有序数组中查找目…

Nodejs+vue图书阅读评分个性化推荐系统

此系统设计主要采用的是nodejs语言来进行开发&#xff0c;采用 vue框架技术&#xff0c;对于各个模块设计制作有一定的安全性&#xff1b;数据库方面主要采用的是MySQL来进行开发&#xff0c;其特点是稳定性好&#xff0c;数据库存储容量大&#xff0c;处理能力快等优势&#x…

数字热潮:iGaming 能否推动加密货币的普及?

过去十年&#xff0c;iGaming&#xff08;互联网游戏&#xff09;世界有了显著增长&#xff0c;每月有超过一百万的新用户加入。那么&#xff0c;这一主流的秘密是什么&#xff1f;让我们在本文中探讨一下。 领先一步&#xff1a;市场 数字时代正在重新定义娱乐&#xff0c;iG…

MySQL运维实战(7.2) MySQL复制server_id相关问题

作者&#xff1a;俊达 主库server_id没有设置 主库没有设置server_id Got fatal error 1236 from master when reading data from binary log: Misconfigured master - server_id was not set主库查看server_id mysql> show variables like server_id; ----------------…

如何在本地电脑部署HadSky论坛并发布至公网可远程访问【内网穿透】

文章目录 前言1. 网站搭建1.1 网页下载和安装1.2 网页测试1.3 cpolar的安装和注册 2. 本地网页发布2.1 Cpolar临时数据隧道2.2 Cpolar稳定隧道&#xff08;云端设置&#xff09;2.3 Cpolar稳定隧道&#xff08;本地设置&#xff09;2.4 公网访问测试 总结 前言 经过多年的基础…

Repeater:创建大量类似项

Repeater 类型用于创建大量类似项。与其它视图类型一样&#xff0c;Repeater有一个model和一个delegate。 首次创建Repeater时&#xff0c;会创建其所有delegate项。若存在大量delegate项&#xff0c;并且并非所有项都必须同时可见&#xff0c;则可能会降低效率。 有2种方式可…

【ubuntu】永久修改主机名

文章目录 1. 问题描述2. 解决方案 1. 问题描述 主机名过长&#xff08;后面的部分&#xff09; 2. 解决方案 查看主机名详情 hostnamectl修改指定主机名 hostnamectl set-hostname ubuntu2204 --static登出重进即可

冯诺依曼体系结构 与 操作系统

一、冯诺依曼体系结构 深入理解冯诺依曼体系结构 计算机的出现就是为了解决实际问题, 所以把问题交给计算机&#xff0c;计算机经过处理&#xff0c;得到一个结果反馈给我们&#xff0c;所以这中间就必然涉及到了输入设备&#xff0c;中央处理器(包括运算器和控制器)和输出设备…

【HarmonyOS】低代码开发—使用低代码开发服务卡片

DevEco Studio还支持使用低代码开发功能开发服务卡片&#xff0c;目前只支持JS语言&#xff0c;且compileSdkVersion必须为7或以上。 下面以创建一个新的服务卡片为例进行说明。 1.打开一个工程&#xff0c;创建服务卡片&#xff0c;创建方法包括如下两种方式&#xff1a; 选…

网络安全“三保一评”深度解析

“没有网络安全就没有国家安全”。近几年&#xff0c;我国法律法规陆续发布实施&#xff0c;为承载我国国计民生的重要网络信息系统的安全提供了法律保障&#xff0c;正在实施的“3保1评”为我国重要网络信息系统的安全构筑了四道防线。 什么是“3保1评”&#xff1f; 等保、分…

计算机网络-网络互联

文章目录 网络互联网络互联方法LAN-LAN&#xff1a;网桥及其互连原理使用网桥实现LAN-LAN使用交换机扩展局域网使用路由器连接局域网 LAN-WANWAN-WAN路由选择算法非自适应路由选择算法自适应路由选择算法广播路由选择算法&#xff1a;分层路由选择算法 网络互联 网络互联是指利…

我的128创作纪念日

目录 学习成长机遇个人数据一览榜单认可日常2024憧憬和规划创作纪念日总结 学习成长机遇 账号创建已经快9年了&#xff0c;以前一直在个人网站和简书上写文章&#xff0c;在CSDN真正写文竟然在2023年10月20&#xff0c;至今才128天&#xff0c;不过获得的数据还算可以&#xff…