赞
踩
目录
(四)在mybatis的配置文件config.xml中配置typeHandlers
MyBatis 是一款优秀的持久层框架,它的核心原理包括以下几个方面:
总的来说,MyBatis 的核心原理是基于配置文件和接口定义的,通过解析配置文件、创建工厂、会话管理和数据库操作等步骤来实现与数据库的交互。它提供了灵活的 SQL 映射、参数处理、结果集映射、缓存和事务管理等功能,让开发者能够方便地进行数据库操作。同时,MyBatis 还支持插件机制,提供了扩展性和定制化的能力,使得开发者能够根据具体需求进行功能的增强和定制。
在 Java 开发中,TypeHandler 是 MyBatis 框架中的一个核心组件,用于实现数据库与 Java 类型之间的相互转换。它允许开发人员自定义类型处理器,以满足特定的业务需求。
TypeHandler 的作用是在 MyBatis 执行 SQL 查询或更新操作时,将数据库中的列值转换为 Java 对象,并在将 Java 对象写入数据库时执行相反的转换。它提供了一种灵活且可扩展的方式,用于处理数据库类型与 Java 类型之间的映射关系。
以下是 TypeHandler 的一些关键特点和使用方式:
在 MyBatis 中使用自定义 TypeHandler 进行类型转换时,还有一些注意事项和建议:
以上是关于使用自定义 TypeHandler 进行类型转换的注意事项和建议。根据具体的业务需求,可以根据以上的指导来创建适合自己的自定义 TypeHandler,并在 MyBatis 中应用它。但是如果害怕出错还是建议直接使用默认的来处理即可,相关不合理的可能就直接通过查询或插入时进行转换了。
自定义TypeHandler实现类,通常有两种方式:实现TypeHandler接口;继承BaseTypeHandler类。以扩展BaseTypeHandler类为例,说明如何实现想要转换成的数据库对象的具体逻辑。
假设我们有一个订单实体类 Order,其中包含一个 Status 枚举类型的状态字段,在数据库中,我们将状态字段存储为一个字符串类型的列,例如 status。
- package org.zyf.javabasic.mybatis;
-
- /**
- * @author yanfengzhang
- * @description 订单实体类 Order
- * 其中包含一个 Status 枚举类型
- * @date 2023/5/13 23:34
- */
- public enum Status {
- CREATED,
- PROCESSING,
- COMPLETED,
- CANCELLED
- }
我们可以使用自定义的 TypeHandler 来实现 Status 枚举类型与数据库列值之间的转换。以下是一个示例的 TypeHandler 实现:
- package org.zyf.javabasic.mybatis;
-
- 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;
-
- /**
- * @author yanfengzhang
- * @description 自定义的 TypeHandler 来实现 Status 枚举类型与数据库列值之间的转换
- * @date 2023/5/13 23:36
- */
- public class StatusTypeHandler extends BaseTypeHandler<Status> {
-
- @Override
- public Status getNullableResult(ResultSet rs, String columnName) throws SQLException {
- // 从 ResultSet 中获取列值
- int data = rs.getInt(columnName);
- // 将列值转换为枚举对象
- return Status.getEnumById(data);
- }
-
- @Override
- public Status getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
- int data = rs.getInt(columnIndex);
- return Status.getEnumById(data);
- }
-
- @Override
- public Status getNullableResult(CallableStatement callableStatement, int columnIndex) throws SQLException {
- int data = callableStatement.getInt(columnIndex);
- return Status.getEnumById(data);
- }
-
- @Override
- public void setNonNullParameter(PreparedStatement preparedStatement, int i, Status status, JdbcType jdbcType) throws SQLException {
- // 将枚举值的code存储到数据库中
- preparedStatement.setInt(i, status.getCode());
- }
- }
在上述示例中,我们自定义了一个 TypeHandler 类 StatusTypeHandler,用于处理 Status 枚举类型与数据库列值之间的转换。我们需要继承 BaseTypeHandler 并重写相应的方法来实现转换逻辑。
假设我们有一个订单实体类 Order,其中包含一个 LocalDateTime 类型的创建时间字段,在数据库中,我们将创建时间字段存储为一个 DATETIME 类型的列,例如 create_time。
我们可以使用自定义的 TypeHandler 来实现 LocalDateTime 类型与数据库列值之间的转换。以下是一个示例的 TypeHandler 实现:
- package org.zyf.javabasic.mybatis;
-
- 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;
- import java.sql.Timestamp;
- import java.time.LocalDateTime;
-
- /**
- * @author yanfengzhang
- * @description 自定义的 TypeHandler 来实现 LocalDateTime 类型与数据库列值之间的转换
- * @date 2023/5/14 10:28
- */
- public class LocalDateTimeTypeHandler extends BaseTypeHandler<LocalDateTime> {
- @Override
- public void setNonNullParameter(PreparedStatement ps, int i, LocalDateTime parameter, JdbcType jdbcType) throws SQLException {
- // 将 LocalDateTime 转换为 Timestamp 并设置到 PreparedStatement 中
- ps.setTimestamp(i, Timestamp.valueOf(parameter));
- }
-
- @Override
- public LocalDateTime getNullableResult(ResultSet rs, String columnName) throws SQLException {
- // 从 ResultSet 中获取 Timestamp 值
- Timestamp timestamp = rs.getTimestamp(columnName);
- // 将 Timestamp 转换为 LocalDateTime
- return timestamp != null ? timestamp.toLocalDateTime() : null;
- }
-
- @Override
- public LocalDateTime getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
- Timestamp timestamp = rs.getTimestamp(columnIndex);
- return timestamp != null ? timestamp.toLocalDateTime() : null;
- }
-
- @Override
- public LocalDateTime getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
- Timestamp timestamp = cs.getTimestamp(columnIndex);
- return timestamp != null ? timestamp.toLocalDateTime() : null;
- }
- }
-
在上述示例中,我们自定义了一个 TypeHandler 类 LocalDateTimeTypeHandler,用于处理 LocalDateTime 类型与数据库列值之间的转换。我们需要继承 BaseTypeHandler 并重写相应的方法来实现转换逻辑。
现在,当使用 MyBatis 进行数据库查询时,会自动将数据库中的列值转换为对应的 LocalDateTime 对象,使我们能够方便地在 Java 代码中使用 LocalDateTime 类型。
当处理 List 类型时,可以使用自定义的 TypeHandler 将 List 对象转换为字符串进行存储,或者从数据库中读取字符串并还原为 List 对象。
假设有一个订单实体类 Order,其中包含一个 List<String> 类型的商品列表字段(products),我们可以使用自定义的 TypeHandler 来实现 List<String> 类型与数据库列值之间的转换。以下是一个示例的 TypeHandler 实现:
- package org.zyf.javabasic.mybatis;
-
- 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;
- import java.util.Arrays;
- import java.util.Collections;
- import java.util.List;
-
- /**
- * @author yanfengzhang
- * @description 自定义的 TypeHandler 来实现 List<String> 类型与数据库列值之间的转换
- * @date 2023/5/14 10:39
- */
- public class StringListTypeHandler extends BaseTypeHandler<List<String>> {
- private static final String DELIMITER = ",";
-
- @Override
- public 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);
- }
-
- @Override
- public List<String> getNullableResult(ResultSet rs, String columnName) throws SQLException {
- // 从 ResultSet 中获取字符串值
- String rolesString = rs.getString(columnName);
- // 将字符串转换为 List<String>
- return parseRolesString(rolesString);
- }
-
- @Override
- public List<String> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
- String rolesString = rs.getString(columnIndex);
- return parseRolesString(rolesString);
- }
-
- @Override
- public 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();
- }
- // 将字符串按逗号分隔转换为 List<String>
- return Arrays.asList(rolesString.split(DELIMITER));
- }
- }
-
在上述示例中,我们自定义了一个 TypeHandler 类 StringListTypeHandler,用于处理 List<String> 类型与数据库列值之间的转换。我们需要继承 BaseTypeHandler 并重写相应的方法来实现转换逻辑。现在,当使用 MyBatis 进行数据库查询时,会自动将数据库中的列值转换为对应的 List<String> 对象,使我们能够方便地在 Java 代码中使用 List<String> 类型。
同样的,可以自定义 TypeHandler 中将 VARCHAR 类型的数据库列与 List<Long> 类型的 Java 对象进行转换
- package org.zyf.javabasic.mybatis;
-
- 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;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.stream.Collectors;
-
- /**
- * @author yanfengzhang
- * @description 自定义 TypeHandler 中将 VARCHAR 类型的数据库列与 List<Long> 类型的 Java 对象进行转换
- * @date 2023/5/14 10:48
- */
- public class LongListTypeHandler extends BaseTypeHandler<List<Long>> {
-
- private static final String DELIMITER = ",";
-
- @Override
- public void setNonNullParameter(PreparedStatement ps, int i, List<Long> parameter, JdbcType jdbcType) throws SQLException {
- String joinedString = parameter.stream().map(String::valueOf).collect(Collectors.joining(DELIMITER));
- ps.setString(i, joinedString);
- }
-
- @Override
- public List<Long> getNullableResult(ResultSet rs, String columnName) throws SQLException {
- String columnValue = rs.getString(columnName);
- return convertStringToList(columnValue);
- }
-
- @Override
- public List<Long> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
- String columnValue = rs.getString(columnIndex);
- return convertStringToList(columnValue);
- }
-
- @Override
- public List<Long> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
- String columnValue = cs.getString(columnIndex);
- return convertStringToList(columnValue);
- }
-
- private List<Long> convertStringToList(String columnValue) {
- if (columnValue == null || columnValue.isEmpty()) {
- return null;
- }
- List<Long> resultList = new ArrayList<>();
- String[] values = columnValue.split(DELIMITER);
- for (String value : values) {
- resultList.add(Long.valueOf(value));
- }
- return resultList;
- }
- }
同样的,可以自定义 TypeHandler 中将数据库中的时间段(以秒为单位)与 Java 对象中的 TimePeriod 类型进行转换。
首先,定义 TimePeriod 类表示时间段:
- package org.zyf.javabasic.mybatis;
-
- import lombok.Data;
-
- import java.time.LocalDateTime;
-
- /**
- * @author yanfengzhang
- * @description
- * @date 2023/5/14 10:58
- */
- @Data
- public class TimePeriod {
- private LocalDateTime startTime;
- private LocalDateTime endTime;
-
- public TimePeriod(LocalDateTime startTime, LocalDateTime endTime) {
- this.startTime = startTime;
- this.endTime = endTime;
- }
- }
然后,定义 TimePeriodTypeHandler 类实现 TypeHandler 接口来处理 TimePeriod 类型的转换:
- package org.zyf.javabasic.mybatis;
-
- import org.apache.ibatis.type.JdbcType;
- import org.apache.ibatis.type.TypeHandler;
-
- import java.sql.CallableStatement;
- import java.sql.PreparedStatement;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- import java.sql.Timestamp;
- import java.sql.Types;
- import java.time.LocalDateTime;
-
- /**
- * @author yanfengzhang
- * @description 自定义 TypeHandler 中将数据库中的时间段(以秒为单位)与 Java 对象中的 TimePeriod 类型进行转换
- * @date 2023/5/14 10:52
- */
- public class TimePeriodTypeHandler implements TypeHandler<TimePeriod> {
- @Override
- public void setParameter(PreparedStatement ps, int i, TimePeriod parameter, JdbcType jdbcType) throws SQLException {
- // 将 Java 对象转换为数据库类型,并设置到 PreparedStatement 中的指定参数位置
- if (parameter != null) {
- ps.setTimestamp(i, Timestamp.valueOf(parameter.getStartTime()));
- ps.setTimestamp(i + 1, Timestamp.valueOf(parameter.getEndTime()));
- } else {
- ps.setNull(i, Types.TIMESTAMP);
- ps.setNull(i + 1, Types.TIMESTAMP);
- }
- }
-
- @Override
- public TimePeriod getResult(ResultSet rs, String columnName) throws SQLException {
- // 从 ResultSet 中获取指定列名的数据库值,并将其转换为对应的 Java 对象
- LocalDateTime startTime = rs.getTimestamp(columnName).toLocalDateTime();
- LocalDateTime endTime = rs.getTimestamp(columnName + "_end").toLocalDateTime();
- return new TimePeriod(startTime, endTime);
- }
-
- @Override
- public TimePeriod getResult(ResultSet rs, int columnIndex) throws SQLException {
- // 从 ResultSet 中获取指定列索引的数据库值,并将其转换为对应的 Java 对象
- LocalDateTime startTime = rs.getTimestamp(columnIndex).toLocalDateTime();
- LocalDateTime endTime = rs.getTimestamp(columnIndex + 1).toLocalDateTime();
- return new TimePeriod(startTime, endTime);
- }
-
- @Override
- public TimePeriod getResult(CallableStatement cs, int columnIndex) throws SQLException {
- // 从 CallableStatement 中获取指定列索引的数据库值,并将其转换为对应的 Java 对象
- LocalDateTime startTime = cs.getTimestamp(columnIndex).toLocalDateTime();
- LocalDateTime endTime = cs.getTimestamp(columnIndex + 1).toLocalDateTime();
- return new TimePeriod(startTime, endTime);
- }
- }
假设我们有一个订单实体类 Order,其中包含一个 InetAddress 类型的 IP 地址字段,在数据库中,我们将 IP 地址存储为一个字符串类型的列,例如 ip_address。
我们可以使用自定义的 TypeHandler 来实现 InetAddress 类型与数据库列值之间的转换。以下是一个示例的 TypeHandler 实现:
- package org.zyf.javabasic.mybatis;
-
- import org.apache.ibatis.type.BaseTypeHandler;
- import org.apache.ibatis.type.JdbcType;
-
- import java.net.InetAddress;
- import java.net.UnknownHostException;
- import java.sql.CallableStatement;
- import java.sql.PreparedStatement;
- import java.sql.ResultSet;
- import java.sql.SQLException;
-
- /**
- * @author yanfengzhang
- * @description 用于处理 InetAddress 类型与数据库列值之间的转换
- * @date 2023/5/14 11:20
- */
- public class InetAddressTypeHandler extends BaseTypeHandler<InetAddress> {
- @Override
- public void setNonNullParameter(PreparedStatement ps, int i, InetAddress parameter, JdbcType jdbcType) throws SQLException {
- // 获取 IP 地址的字符串表示
- String ipAddressString = parameter.getHostAddress();
- // 将字符串存储到数据库中
- ps.setString(i, ipAddressString);
- }
-
- @Override
- public InetAddress getNullableResult(ResultSet rs, String columnName) throws SQLException {
- // 从 ResultSet 中获取字符串值
- String ipAddressString = rs.getString(columnName);
- // 将字符串转换为 InetAddress
- return parseIpAddressString(ipAddressString);
- }
-
- @Override
- public InetAddress getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
- String ipAddressString = rs.getString(columnIndex);
- return parseIpAddressString(ipAddressString);
- }
-
- @Override
- public InetAddress getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
- String ipAddressString = cs.getString(columnIndex);
- return parseIpAddressString(ipAddressString);
- }
-
- private InetAddress parseIpAddressString(String ipAddressString) {
- try {
- // 将字符串解析为 InetAddress
- return InetAddress.getByName(ipAddressString);
- } catch (UnknownHostException e) {
- // 处理解析错误,例如返回默认的 IP 地址或抛出异常
- return null;
- }
- }
- }
该写法只对返回此resultMap的查询语句有效
- <resultMap id="BaseResultMap" type="org.zyf.javabasic.mybatis.domain.Order">
- <id column="id" jdbcType="BIGINT" property="id"/>
- <result column="name" jdbcType="VARCHAR" property="name"/>
- <result column="status" jdbcType="INTEGER" property="status"
- typeHandler="org.zyf.javabasic.mybatis.StatusTypeHandler" />
- <result column="limit_period" jdbcType="VARCHAR" property="limitPeriod"
- typeHandler="org.zyf.javabasic.mybatis.TimePeriodHandler"/>
- <result column="create_time" jdbcType="DATETIME" property="createTime"
- typeHandler="org.zyf.javabasic.mybatis.LocalDateTimeTypeHandler"/>
- <result column="sku_ids" jdbcType="VARCHAR" property="skuIds"
- typeHandler="org.zyf.javabasic.mybatis.LongListTypeHandler"/>/>
- <result column="products" jdbcType="VARCHAR" property="products"
- typeHandler="org.zyf.javabasic.mybatis.StringListTypeHandler"/>
- <result column="ip_address" jdbcType="VARCHAR" property="ipAddress"
- typeHandler="org.zyf.javabasic.mybatis.InetAddress"/>
- <result column="cname" jdbcType="VARCHAR" property="cname"/>
- <result column="uname" jdbcType="VARCHAR" property="uname"/>
- </resultMap>
如果希望在写入数据库的时候,按照指定规则对写入对象进行序列化,则需要在写入sql语句处显式指定typeHandler
- <update id="update" parameterType="map" >
- UPDATE
- <include refid="Table_Name"/>
- SET
- name = #{order.name},
- limit_period = #{order.limitPeriod, typeHandler=org.zyf.javabasic.mybatis.TimePeriodHandlerer},
- sku_ids =#{order.skuIds, typeHandler=org.zyf.javabasic.mybatis.LongListTypeHandler},
- uname = #{order.uname},
- utime = unix_timestamp()
- where id = #{order.id}
- </update>
方法一:启动时会扫描包下的所有文件
- <typeHandlers>
- <package name="org.zyf.javabasic.mybatis"/>
- </typeHandlers>
方法二:或单个写入要注册的TypeHandler
- <typeHandlers>
- <typeHandler handler="org.zyf.javabasic.mybatis.TimePeriodHandler"
- javaType="org.zyf.javabasic.mybatis.TimePeriod"
- jdbcType="VARCHAR"/>
- <typeHandler handler="org.zyf.javabasic.mybatis.LongListHandler"
- javaType="java.util.List"
- jdbcType="VARCHAR"/>
- </typeHandlers>
参考文章链接
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。