MyBatis 类型处理器如何让代码实现更优雅的转换?

2026-04-18 00:231阅读0评论SEO教程
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1712个文字,预计阅读时间需要7分钟。

MyBatis 类型处理器如何让代码实现更优雅的转换?

原文:来源:https://www.cnblogs.com/zwwhnly/p/11238131.本篇主要讲解在MyBatis中如何使用类型处理器。

1.明确需求

在设计之初,sys_role表的enabled字段有2个可选值,其中0代表禁用,1代表启用。

改写后:来源:[链接](https://www.cnblogs.com/zwwhnly/p/11238131.)本文主要介绍MyBatis中类型处理器的使用。

1.需求说明

在设计阶段,sys_role表的enabled字段有0和1两个选项,分别代表禁用和启用。

来源:www.cnblogs.com/zwwhnly/p/11238131.html

本篇主要讲解在MyBatis中如何使用类型处理器。

1. 明确需求

在设计之初,sys_role表的enabled字段有2个可选值,其中0 代表禁用,1代表启用,而且实体类中我们使用的是Interger类型:

/** * 有效标志 */ private Integer enabled; public Integer getEnabled() { return enabled; } public void setEnabled(Integer enabled) { this.enabled = enabled; }

如果要新增或者更新角色信息,我们肯定要校验enabled字段的值必须是0或者1,所以最初的部分代码可能是这样的:

if (sysRole.getEnabled() == 0 || sysRole.getEnabled() == 1) { sysRoleMapper.updateById(sysRole); sysRole = sysRoleMapper.selectById(2L); Assert.assertEquals(0, sysRole.getEnabled()); } else { throw new Exception("无效的enabled值"); }

这种硬编码的方式不仅看起来不友好,而且不利于后期维护,如果维护的程序员脾气不好,还会骂你,哈哈。

所以我们的需求就是,拒绝硬编码,使用友好的编码方式来校验enabled字段的值是否有效。

2. 使用MyBatis提供的枚举类型处理器

我们通常会使用枚举来解决这种场景。

首先新建com.zwwhnly.mybatisaction.type包,然后在该包下新建枚举Enabled:

package com.zwwhnly.mybatisaction.type; public enum Enabled { /** * 禁用 */ disabled, /** * 启用 */ enabled; }

其中,disabled对应的索引为0,enabled对应的索引为1。

然后将SysRole类中原来为Integer类型的enabled字段修改为:

/** * 有效标志 */ private Enabled enabled; public Enabled getEnabled() { return enabled; } public void setEnabled(Enabled enabled) { this.enabled = enabled; }

此时原本硬编码的代码就可以修改为:

MyBatis 类型处理器如何让代码实现更优雅的转换?

if (sysRole.getEnabled() == Enabled.disabled || sysRole.getEnabled() == Enabled.enabled) { sysRoleMapper.updateById(sysRole); sysRole = sysRoleMapper.selectById(2L); Assert.assertEquals(Enabled.disabled, sysRole.getEnabled()); } else { throw new Exception("无效的enabled值"); }

虽然上面的代码很完美的解决了硬编码的问题,但此时又引出一个新的问题:

数据库并不能识别Enabled枚举类型,在新增,更新或者作为查询条件时,需要将枚举值转换为数据库中的int类型,在查询数据时,需要将数据库的int类型的值转换为Enabled枚举类型。

带着这个问题,我们在SysRoleMapperTest测试类中添加如下测试方法:

@Test public void testUpdateById() { SqlSession sqlSession = getSqlSession(); try { SysRoleMapper sysRoleMapper = sqlSession.getMapper(SysRoleMapper.class); // 先查询出id=2的角色,然后修改角色的enabled值为disabled SysRole sysRole = sysRoleMapper.selectById(2L); Assert.assertEquals(Enabled.enabled, sysRole.getEnabled()); // 修改角色的enabled为disabled sysRole.setEnabled(Enabled.disabled); if (sysRole.getEnabled() == Enabled.disabled || sysRole.getEnabled() == Enabled.enabled) { sysRoleMapper.updateById(sysRole); sysRole = sysRoleMapper.selectById(2L); Assert.assertEquals(Enabled.disabled, sysRole.getEnabled()); } else { throw new Exception("无效的enabled值"); } } catch (Exception e) { e.printStackTrace(); } finally { sqlSession.close(); } }

运行测试代码,发现抛出如下异常:

Error querying database. Cause: org.apache.ibatis.executor.result.ResultMapException: Error attempting to get column 'enabled' from result set. Cause: java.lang.IllegalArgumentException: No enum constant com.zwwhnly.mybatisaction.type.Enabled.1

这是因为MyBatis在处理Java类型和数据库类型时,使用TypeHandler(类型处理器)对这两者进行转换。

MyBatis为Java类型和数据库JDBC中的常用类型类型提供了TypeHandler接口的实现。

MyBatis在启动时会加载所有的JDBC对应的类型处理器,在处理枚举类型时默认使用org.apache.ibatis.type.EnumTypeHandler处理器,这个处理器会将枚举类型转换为字符串类型的字面值使用,对于Enabled枚举来说,就是“disabled"和”enabled"字符串。

而数据库中enabled字段的类型是int,所以在查询到角色信息将int类型的值1转换为Enabled类型报错。

那么如何解决这个问题呢?

MyBatis还提供了另一个枚举处理器:org.apache.ibatis.type.EnumOrdinalTypeHandler,这个处理器使用枚举的索引进行处理,可以解决此处转换报错的问题。

使用这个处理器,需要在之前的resources/mybatis-config.xml中添加如下配置:

<typeHandlers> <typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="com.zwwhnly.mybatisaction.type.Enabled"/> </typeHandlers>

再次运行测试代码,测试通过,输出日志如下:

DEBUG [main] - ==> Preparing: SELECT id,role_name,enabled,create_by,create_time FROM sys_role WHERE id = ?

DEBUG [main] - ==> Parameters: 2(Long)

TRACE [main] - <== Columns: id, role_name, enabled, create_by, create_time

TRACE [main] - <== Row: 2, 普通用户, 1, 1, 2019-06-27 18:21:12.0

DEBUG [main] - <== Total: 1

DEBUG [main] - ==> Preparing: UPDATE sys_role SET role_name = ?,enabled = ?,create_by=?, create_time=? WHERE id=?

DEBUG [main] - ==> Parameters: 普通用户(String), 0(Integer), 1(Long), 2019-06-27 18:21:12.0(Timestamp), 2(Long)

DEBUG [main] - <== Updates: 1

从日志中可以看出,在查询角色信息时,MyBatis将1转换为了Enabled.enabled,在更新角色信息时,MyBatis将Enabled.disabled转换为了0。

3. 使用自定义的类型处理器

假设enabled字段的值既不是枚举的字面值,也不是枚举的索引值,此时org.apache.ibatis.type.EnumTypeHandlerorg.apache.ibatis.type.EnumOrdinalTypeHandler都不能满足我们的需求,这种情况下我们就需要自己来实现类型处理器了。

首先修改下枚举类Enabled代码:

package com.zwwhnly.mybatisaction.type; public enum Enabled { /** * 启用 */ enabled(1), /** * 禁用 */ disabled(0); private final int value; private Enabled(int value) { this.value = value; } public int getValue() { return value; } }

然后在com.zwwhnly.mybatisaction.type包下新建类型处理器EnabledTypeHandler:

package com.zwwhnly.mybatisaction.type; 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.util.HashMap; import java.util.Map; /** * Enabled类型处理器 */ public class EnabledTypeHandler implements TypeHandler<Enabled> { private final Map<Integer, Enabled> enabledMap = new HashMap<Integer, Enabled>(); public EnabledTypeHandler() { for (Enabled enabled : Enabled.values()) { enabledMap.put(enabled.getValue(), enabled); } } @Override public void setParameter(PreparedStatement preparedStatement, int i, Enabled enabled, JdbcType jdbcType) throws SQLException { preparedStatement.setInt(i, enabled.getValue()); } @Override public Enabled getResult(ResultSet resultSet, String s) throws SQLException { Integer value = resultSet.getInt(s); return enabledMap.get(value); } @Override public Enabled getResult(ResultSet resultSet, int i) throws SQLException { Integer value = resultSet.getInt(i); return enabledMap.get(value); } @Override public Enabled getResult(CallableStatement callableStatement, int i) throws SQLException { Integer value = callableStatement.getInt(i); return enabledMap.get(value); } }

自定义类型处理器实现了TypeHandler接口,重写了接口中的4个方法,并且在无参构造函数中遍历了枚举类型Enabled并对字段enabledMap进行了赋值。

想要使用自定义的类型处理器,也需要在resources/mybatis-config.xml中添加如下配置:

<typeHandlers> <!--其他配置--> <typeHandler handler="com.zwwhnly.mybatisaction.type.EnabledTypeHandler" javaType="com.zwwhnly.mybatisaction.type.Enabled"/> </typeHandlers>

运行测试代码,输出日志和上面的输出日志一样,这里不再重复贴出。

近期热文推荐:

1.1,000+ 道 Java面试题及答案整理(2022最新版)

2.劲爆!Java 协程要来了。。。

3.Spring Boot 2.x 教程,太全了!

4.别再写满屏的爆爆爆炸类了,试试装饰器模式,这才是优雅的方式!!

5.《Java开发手册(嵩山版)》最新发布,速速下载!

觉得不错,别忘了随手点赞+转发哦!

标签:代码

本文共计1712个文字,预计阅读时间需要7分钟。

MyBatis 类型处理器如何让代码实现更优雅的转换?

原文:来源:https://www.cnblogs.com/zwwhnly/p/11238131.本篇主要讲解在MyBatis中如何使用类型处理器。

1.明确需求

在设计之初,sys_role表的enabled字段有2个可选值,其中0代表禁用,1代表启用。

改写后:来源:[链接](https://www.cnblogs.com/zwwhnly/p/11238131.)本文主要介绍MyBatis中类型处理器的使用。

1.需求说明

在设计阶段,sys_role表的enabled字段有0和1两个选项,分别代表禁用和启用。

来源:www.cnblogs.com/zwwhnly/p/11238131.html

本篇主要讲解在MyBatis中如何使用类型处理器。

1. 明确需求

在设计之初,sys_role表的enabled字段有2个可选值,其中0 代表禁用,1代表启用,而且实体类中我们使用的是Interger类型:

/** * 有效标志 */ private Integer enabled; public Integer getEnabled() { return enabled; } public void setEnabled(Integer enabled) { this.enabled = enabled; }

如果要新增或者更新角色信息,我们肯定要校验enabled字段的值必须是0或者1,所以最初的部分代码可能是这样的:

if (sysRole.getEnabled() == 0 || sysRole.getEnabled() == 1) { sysRoleMapper.updateById(sysRole); sysRole = sysRoleMapper.selectById(2L); Assert.assertEquals(0, sysRole.getEnabled()); } else { throw new Exception("无效的enabled值"); }

这种硬编码的方式不仅看起来不友好,而且不利于后期维护,如果维护的程序员脾气不好,还会骂你,哈哈。

所以我们的需求就是,拒绝硬编码,使用友好的编码方式来校验enabled字段的值是否有效。

2. 使用MyBatis提供的枚举类型处理器

我们通常会使用枚举来解决这种场景。

首先新建com.zwwhnly.mybatisaction.type包,然后在该包下新建枚举Enabled:

package com.zwwhnly.mybatisaction.type; public enum Enabled { /** * 禁用 */ disabled, /** * 启用 */ enabled; }

其中,disabled对应的索引为0,enabled对应的索引为1。

然后将SysRole类中原来为Integer类型的enabled字段修改为:

/** * 有效标志 */ private Enabled enabled; public Enabled getEnabled() { return enabled; } public void setEnabled(Enabled enabled) { this.enabled = enabled; }

此时原本硬编码的代码就可以修改为:

MyBatis 类型处理器如何让代码实现更优雅的转换?

if (sysRole.getEnabled() == Enabled.disabled || sysRole.getEnabled() == Enabled.enabled) { sysRoleMapper.updateById(sysRole); sysRole = sysRoleMapper.selectById(2L); Assert.assertEquals(Enabled.disabled, sysRole.getEnabled()); } else { throw new Exception("无效的enabled值"); }

虽然上面的代码很完美的解决了硬编码的问题,但此时又引出一个新的问题:

数据库并不能识别Enabled枚举类型,在新增,更新或者作为查询条件时,需要将枚举值转换为数据库中的int类型,在查询数据时,需要将数据库的int类型的值转换为Enabled枚举类型。

带着这个问题,我们在SysRoleMapperTest测试类中添加如下测试方法:

@Test public void testUpdateById() { SqlSession sqlSession = getSqlSession(); try { SysRoleMapper sysRoleMapper = sqlSession.getMapper(SysRoleMapper.class); // 先查询出id=2的角色,然后修改角色的enabled值为disabled SysRole sysRole = sysRoleMapper.selectById(2L); Assert.assertEquals(Enabled.enabled, sysRole.getEnabled()); // 修改角色的enabled为disabled sysRole.setEnabled(Enabled.disabled); if (sysRole.getEnabled() == Enabled.disabled || sysRole.getEnabled() == Enabled.enabled) { sysRoleMapper.updateById(sysRole); sysRole = sysRoleMapper.selectById(2L); Assert.assertEquals(Enabled.disabled, sysRole.getEnabled()); } else { throw new Exception("无效的enabled值"); } } catch (Exception e) { e.printStackTrace(); } finally { sqlSession.close(); } }

运行测试代码,发现抛出如下异常:

Error querying database. Cause: org.apache.ibatis.executor.result.ResultMapException: Error attempting to get column 'enabled' from result set. Cause: java.lang.IllegalArgumentException: No enum constant com.zwwhnly.mybatisaction.type.Enabled.1

这是因为MyBatis在处理Java类型和数据库类型时,使用TypeHandler(类型处理器)对这两者进行转换。

MyBatis为Java类型和数据库JDBC中的常用类型类型提供了TypeHandler接口的实现。

MyBatis在启动时会加载所有的JDBC对应的类型处理器,在处理枚举类型时默认使用org.apache.ibatis.type.EnumTypeHandler处理器,这个处理器会将枚举类型转换为字符串类型的字面值使用,对于Enabled枚举来说,就是“disabled"和”enabled"字符串。

而数据库中enabled字段的类型是int,所以在查询到角色信息将int类型的值1转换为Enabled类型报错。

那么如何解决这个问题呢?

MyBatis还提供了另一个枚举处理器:org.apache.ibatis.type.EnumOrdinalTypeHandler,这个处理器使用枚举的索引进行处理,可以解决此处转换报错的问题。

使用这个处理器,需要在之前的resources/mybatis-config.xml中添加如下配置:

<typeHandlers> <typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="com.zwwhnly.mybatisaction.type.Enabled"/> </typeHandlers>

再次运行测试代码,测试通过,输出日志如下:

DEBUG [main] - ==> Preparing: SELECT id,role_name,enabled,create_by,create_time FROM sys_role WHERE id = ?

DEBUG [main] - ==> Parameters: 2(Long)

TRACE [main] - <== Columns: id, role_name, enabled, create_by, create_time

TRACE [main] - <== Row: 2, 普通用户, 1, 1, 2019-06-27 18:21:12.0

DEBUG [main] - <== Total: 1

DEBUG [main] - ==> Preparing: UPDATE sys_role SET role_name = ?,enabled = ?,create_by=?, create_time=? WHERE id=?

DEBUG [main] - ==> Parameters: 普通用户(String), 0(Integer), 1(Long), 2019-06-27 18:21:12.0(Timestamp), 2(Long)

DEBUG [main] - <== Updates: 1

从日志中可以看出,在查询角色信息时,MyBatis将1转换为了Enabled.enabled,在更新角色信息时,MyBatis将Enabled.disabled转换为了0。

3. 使用自定义的类型处理器

假设enabled字段的值既不是枚举的字面值,也不是枚举的索引值,此时org.apache.ibatis.type.EnumTypeHandlerorg.apache.ibatis.type.EnumOrdinalTypeHandler都不能满足我们的需求,这种情况下我们就需要自己来实现类型处理器了。

首先修改下枚举类Enabled代码:

package com.zwwhnly.mybatisaction.type; public enum Enabled { /** * 启用 */ enabled(1), /** * 禁用 */ disabled(0); private final int value; private Enabled(int value) { this.value = value; } public int getValue() { return value; } }

然后在com.zwwhnly.mybatisaction.type包下新建类型处理器EnabledTypeHandler:

package com.zwwhnly.mybatisaction.type; 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.util.HashMap; import java.util.Map; /** * Enabled类型处理器 */ public class EnabledTypeHandler implements TypeHandler<Enabled> { private final Map<Integer, Enabled> enabledMap = new HashMap<Integer, Enabled>(); public EnabledTypeHandler() { for (Enabled enabled : Enabled.values()) { enabledMap.put(enabled.getValue(), enabled); } } @Override public void setParameter(PreparedStatement preparedStatement, int i, Enabled enabled, JdbcType jdbcType) throws SQLException { preparedStatement.setInt(i, enabled.getValue()); } @Override public Enabled getResult(ResultSet resultSet, String s) throws SQLException { Integer value = resultSet.getInt(s); return enabledMap.get(value); } @Override public Enabled getResult(ResultSet resultSet, int i) throws SQLException { Integer value = resultSet.getInt(i); return enabledMap.get(value); } @Override public Enabled getResult(CallableStatement callableStatement, int i) throws SQLException { Integer value = callableStatement.getInt(i); return enabledMap.get(value); } }

自定义类型处理器实现了TypeHandler接口,重写了接口中的4个方法,并且在无参构造函数中遍历了枚举类型Enabled并对字段enabledMap进行了赋值。

想要使用自定义的类型处理器,也需要在resources/mybatis-config.xml中添加如下配置:

<typeHandlers> <!--其他配置--> <typeHandler handler="com.zwwhnly.mybatisaction.type.EnabledTypeHandler" javaType="com.zwwhnly.mybatisaction.type.Enabled"/> </typeHandlers>

运行测试代码,输出日志和上面的输出日志一样,这里不再重复贴出。

近期热文推荐:

1.1,000+ 道 Java面试题及答案整理(2022最新版)

2.劲爆!Java 协程要来了。。。

3.Spring Boot 2.x 教程,太全了!

4.别再写满屏的爆爆爆炸类了,试试装饰器模式,这才是优雅的方式!!

5.《Java开发手册(嵩山版)》最新发布,速速下载!

觉得不错,别忘了随手点赞+转发哦!

标签:代码