1. 前言
刚入手spring-boot还不太熟练,先弄了个空的框架,然后写了个简单的用户查询,没啥挑战性。然后想起来之前一直对枚举不太了解,而用户的性别正好可以用枚举类型来表示(male, female)。于是就开始了自己的挖坑之旅。
本文主要分为三个部分:
- mybatis自带枚举类型转换
- 自定义枚举类型转换
- mybatis类型转换深入理解(将在下一篇介绍)
2. mybatis自带枚举类型转换
mybatis内置了两个枚举转换器分别是:org.apache.ibatis.type.EnumTypeHandler
和org.apache.ibatis.type.EnumOrdinalTypeHandler
。
EnumTypeHandler
这是默认的枚举转换器,该转换器将枚举实例转换为实例名称的字符串,即将ComputerState.OPEN
转换OPEN
。
EnumOrdinalTypeHandler
顾名思义这个转换器将枚举实例的ordinal属性作为取值,即ComputerState.OPEN
转换为0,ComputerState.CLOS
E转换为1。
使用方法
建表
CREATE TABLE `users` (
`id` int(13) NOT NULL AUTO_INCREMENT COMMENT '主键',
`username` varchar(33) DEFAULT NULL COMMENT '姓名',
`sex` varchar(8) DEFAULT NULL COMMENT '性别',
`age` int(3) DEFAULT NULL COMMENT '年龄',
`money` double DEFAULT NULL COMMENT '账户余额',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
创建枚举
public enum UserSexEnum {
MALE,
FEMALE;
}
创建实体类
public class User {
private int id;
private String username;
private UserSexEnum sex;
private int age;
private double money;
public User() {
super();
}
public User(String username, UserSexEnum sex, int age, double money) {
super();
this.username = username;
this.sex = sex;
this.age = age;
this.money = money;
}
// getter和setter方法不在此赘述
}
创建映射接口
public interface UserMapper {
List<User> getAll();
void insert(User user);
}
配置xml文件
<mapper namespace="com.**.mapper.UserMapper">
<resultMap id="BaseResultMap" type="com.**.entity.User">
<id column="id" property="id" jdbcType="BIGINT"/>
<result column="username" property="username" jdbcType="VARCHAR"/>
<result column="sex" property="sex" javaType="com.**.enums.UserSexEnum"/>
<result column="age" property="age" jdbcType="INTEGER"/>
<result column="money" property="money" jdbcType="DOUBLE"/>
</resultMap>
<sql id="Base_Column_List">
id, username, sex, age, money
</sql>
<select id="getAll" resultMap="BaseResultMap">
SELECT
<include refid="Base_Column_List" />
FROM users
</select>
<insert id="insert" parameterType="com.huawei.demo.entity.User" >
INSERT INTO
users
(username, sex, age, money)
VALUES
(#{username}, #{sex}, #{age}, #{money})
</insert>
</mapper>
测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
public void testInsert(){
userMapper.insert(new User("aaa", UserSexEnum.MALE, 18, 2.5));
userMapper.insert(new User("bbb", UserSexEnum.FEMALE, 24, 8.6));
userMapper.insert(new User("ccc", UserSexEnum.FEMALE, 21, 5.4));
Assert.assertEquals(3, userMapper.getAll().size());
}
}
使用EnumOrdinalTypeHandler的方法和默认的差不多,只是需要指定一下typeHandler
创建枚举
public enum UserSexEnum {
MALE(0,"男"),
FEMALE(1,"女");
private int sex;
private String sexName;
UserSexEnum(int sex, String sexName) {
this.sex = sex;
this.sexName = sexName;
}
public int getSex() {
return sex;
}
public String getSexName() {
return sexName;
}
}
修改xml配置
<!--resultMap里结果的映射-->
<result column="sex" property="sex" javaType="com.**.enums.UserSexEnum" typeHandler="org.apache.ibatis.type.EnumOrdinalTypeHandler"/>
<!--insert语句插入字段的设置-->
, #{sex, typeHandler=org.apache.ibatis.type.EnumOrdinalTypeHandler},
3. 自定义枚举类型转换
在日常编码的过程中,我们经常会遇到用某个数值来表示某种状态、类型或者阶段的情况,比如有这样一个枚举:
public enum OrderStatus {
CREATE(10, "创建"),
PAYING(13, "支付中"),
IN_PROGRESS(21, "支付成功"),
FAILED(36, "支付失败"),
REVERSED(48, "取消订单");
private int value;
private String desc;
OrderStatus(int value, String desc) {
this.value = value;
this.desc = desc;
}
public int getValue() {
return value;
}
public String getDesc() {
return desc;
}
}
通常我们希望将表示状态的数值存入数据库,即OrderStatus.PAYING存入数据库取值为13。很明显上述两个内置的枚举转换器都不能满足我们的需求,所以我们需要自定义一个枚举转换器。
MyBatis 提供了 org.apache.ibatis.type.BaseTypeHandler 类用于我们自己扩展类型转换器,上面的 EnumTypeHandler 和 EnumOrdinalTypeHandler 也都实现了这个接口。
public class EnumOrderStatusHandler extends BaseTypeHandler<OrderStatus> {
/**
* 设置配置文件设置的转换类以及枚举类内容,供其他方法更便捷高效的实现
* @param type 配置文件中设置的转换类
*/
public EnumOrderStatusHandler(Class<OrderStatus> type) {
if (type == null)
throw new IllegalArgumentException("Type argument cannot be null");
}
/**
* 用于定义设置参数时,该如何把Java类型的参数转换为对应的数据库类型
*/
@Override
public void setNonNullParameter(PreparedStatement ps, int i, OrderStatus parameter, JdbcType jdbcType)
throws SQLException {
// 根据数据库存储类型决定获取类型,本例子中数据库中存放int类型
// ps.setString
ps.setInt(i, parameter.getValue());
}
/**
* 用于定义通过字段名称获取字段数据时,如何把数据库类型转换为对应的Java类型
*/
@Override
public OrderStatus getNullableResult(ResultSet rs, String columnName) throws SQLException {
// 根据数据库存储类型决定获取类型,本例子中数据库中存放int类型
// String i = rs.getString(columnName);
int i = rs.getInt(columnName);
if (rs.wasNull()) {
return null;
} else {
// 根据数据库中的值,定位Enum子类
return locateEnum(i);
}
}
/**
* 用于定义通过字段索引获取字段数据时,如何把数据库类型转换为对应的Java类型
* @param rs
* @param columnIndex
* @return
* @throws SQLException
*/
@Override
public OrderStatus getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
// 根据数据库存储类型决定获取类型,本例子中数据库中存放int类型
// String i = rs.getString(columnIndex);
int i = rs.getInt(columnIndex);
if (rs.wasNull()) {
return null;
} else {
// 根据数据库中的值,定位Enum子类
return locateEnum(i);
}
}
/**
* 用定义调用存储过程后,如何把数据库类型转换为对应的Java类型
* @param cs
* @param columnIndex
* @return
* @throws SQLException
*/
@Override
public OrderStatus getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
// 根据数据库存储类型决定获取类型,本例子中数据库中存放int类型
// String i = cs.getString(columnIndex);
int i = cs.getInt(columnIndex);
if (cs.wasNull()) {
return null;
} else {
// 根据数据库中的值,定位Enum子类
return locateEnum(i);
}
}
/**
* 枚举类型转换
* @param value 数据库中存储的自定义属性
* @return value对应的枚举类
*/
private OrderStatus locateEnum(int value) {
for (OrderStatus status : OrderStatus.values()) {
if (status.getValue() == value) {
return status;
}
}
throw new IllegalArgumentException("未知的枚举类型:" + value);
}
}
!!!还有更高级的用法,可以简化MyBatis的配置文件维护。具体参考第三篇文章
参考链接
Java、Mysql、MyBatis 中枚举 enum 的使用
SSM(一)-MyBatis Enum 映射
如何在MyBatis中优雅的使用枚举