当我们在javabean中自定义了枚举类型或者其它某个类型,但是在数据库中存储时往往需要转换成数据库对应的类型,并且在从数据库中取出来时也需要将数据库类型转换为javabean中的对应类型。比如:javabean中字段类型为Date,数据库中存储的是varchar类型;javabean中字段类型是Enum,数据库中存储的是String或者Integer。
因为有大量类似数据的转换,手动转换类型进行存储和查询已经过于麻烦。MyBatis为我们提供了解决办法:TypeHandler类型处理器。
MyBatis 内置的 TypeHandler
在 MyBatis 常见内的内置Ty’peHandler
BooleanTypeHandler:用于 java 类型 boolean,jdbc 类型 bit、boolean
ByteTypeHandler:用于 java 类型 byte,jdbc 类型 TINYINT
ShortTypeHandler:用于 java 类型 short,jdbc 类型 SMALLINT
IntegerTypeHandler:用于 INTEGER 类型
LongTypeHandler:用于 long 类型
FloatTypeHandler:用于 FLOAT 类型
DoubleTypeHandler:用于 double 类型
StringTypeHandler:用于 java 类型 string,jdbc 类型 CHAR、VARCHAR
ArrayTypeHandler:用于 jdbc 类型 ARRAY
BigDecimalTypeHandler:用于 java 类型 BigDecimal,jdbc 类型 REAL、DECIMAL、NUMERIC
DateTypeHandler:用于 java 类型 Date,jdbc 类型 TIMESTAMP
DateOnlyTypeHandler:用于 java 类型 Date,jdbc 类型 DATE
TimeOnlyTypeHandler:用于 java 类型 Date,jdbc 类型 TIME
对于常见的 Enum 类型,内置了 EnumTypeHandler 进行 Enum 名称的转换和 EnumOrdinalTypeHandler 进行 Enum 序数的转换。这两个类型处理器没有在 TypeHandlerRegistry 中注册,如果需要使用必须手动配置。
自定义一个TypeHandler:
本项目搭建源码:https://github.com/zhuquanwen/mybatis-learn/releases/tag/for-typehandler
搭建过程:
在https://blog.csdn.net/u011943534/article/details/104732289文章基础上搭建,有些过程不详细描述.
1、导入本文测试使用的sql脚本
/*
Navicat MySQL Data Transfer
Source Server : localhost
Source Server Version : 50722
Source Host : localhost:3306
Source Database : mybatis_learn
Target Server Type : MYSQL
Target Server Version : 50722
File Encoding : 65001
Date: 2020-03-16 23:06:58
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for bird
-- ----------------------------
DROP TABLE IF EXISTS `bird`;
CREATE TABLE `bird` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(80) DEFAULT NULL,
`sex` smallint(6) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Records of bird
-- ----------------------------
INSERT INTO `bird` VALUES ('1', '小黄', '1');
INSERT INTO `bird` VALUES ('2', '小绿', '0');
INSERT INTO `bird` VALUES ('3', '小黑', '1');
2、配置generator插件,之后运行com.learn.zqw.generator.Generator
的主函数将bird
表自动生成实体和mapper
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!-- context指定环境 -->
<context id="MyGererator" targetRuntime="MyBatis3">
<!-- 这个标签可以去掉各类元素生成的注释,默认是全部生成的 -->
<commentGenerator>
<!-- 去掉注释 -->
<property name="suppressAllComments" value="true"/>
<!-- 去掉时间戳 -->
<property name="suppressDate" value="true"/>
</commentGenerator>
<!-- 数据库连接信息 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis_learn?useUnicode=true&characterEncoding=utf8"
userId="root"
password="root">
</jdbcConnection>
<!-- JAVA JDBC数据类型转换,可以参照官方文档 -->
<javaTypeResolver >
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- javaModelGenerator javaBean配置
targetPackage 输入包名 输出路径
targetProject 输出项目位置 -->
<javaModelGenerator targetPackage="com.learn.zqw.generator.domain" targetProject="src/main/java">
<!-- enableSubPackages 是否开启子包名称 是否在包名后边加上scheme名称 -->
<property name="enableSubPackages" value="false" />
<!-- 在Set方法中加入.trim -->
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- 映射文件mapper.xml配置 -->
<sqlMapGenerator targetPackage="com.learn.zqw.generator.mapper" targetProject="src/main/java">
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<!-- 动态代理类接口,和mapper.xml在要同一个路径 -->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.learn.zqw.generator.mapper" targetProject="src/main/java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- 数据表 根据数据库中的表来生成 -->
<!--<table tableName="student"/>-->
<table tableName="bird"/>
<!-- 数据表更详细的属性参见官方文档,也可参照https://www.jianshu.com/p/e09d2370b796,里注释掉-->
<!-- <table schema="DB2ADMIN" tableName="ALLTYPES" domainObjectName="Customer" >
<property name="useActualColumnNames" value="true"/>
<generatedKey column="ID" sqlStatement="DB2" identity="true" />
<columnOverride column="DATE_FIELD" property="startDate" />
<ignoreColumn column="FRED" />
<columnOverride column="LONG_VARCHAR_FIELD" jdbcType="VARCHAR" />
</table> -->
</context>
</generatorConfiguration>
3、定义性别的枚举,并将Bird
实体的sex
字段改为此枚举类型
package com.learn.zqw.generator.domain;
/**
* //TODO
*
* @author zhuquanwen
* @vesion 1.0
* @date 2020/3/16 22:29
* @since jdk1.8
*/
public enum SexEnum {
MALE(0, "雄性"),
FEMALE(1, "雌性");
public int val;
public String label;
public int getVal() {
return val;
}
public String getLabel() {
return label;
}
SexEnum(int val, String label) {
this.val = val;
this.label = label;
}
}
package com.learn.zqw.generator.domain;
import lombok.Data;
@Data
public class Bird {
private Integer id;
private String name;
private SexEnum sex = SexEnum.MALE;
}
4、编写SexEnum
转换的SexEnumTypeHandler
package com.learn.zqw.handler;
import com.learn.zqw.generator.domain.SexEnum;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* //TODO
*
* @author zhuquanwen
* @vesion 1.0
* @date 2020/3/16 22:04
* @since jdk1.8
*/
@MappedTypes(value = {SexEnum.class})
@MappedJdbcTypes(JdbcType.SMALLINT)
public class SexEnumTypeHandler extends BaseTypeHandler<SexEnum> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, SexEnum parameter, JdbcType jdbcType) throws SQLException {
ps.setInt(i, parameter.getVal());
}
@Override
public SexEnum getNullableResult(ResultSet rs, String columnName) throws SQLException {
int val = rs.getInt(columnName);
return convert(val);
}
@Override
public SexEnum getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
int val = rs.getInt(columnIndex);
return convert(val);
}
@Override
public SexEnum getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
int val = cs.getInt(columnIndex);
return convert(val);
}
private SexEnum convert(int val) {
SexEnum[] objs = SexEnum.values();
for (SexEnum em : objs) {
if (em.getVal() == val) {
return em;
}
}
return null;
}
}
5、在SqlMapConfig.xml
中注册此SexEnumTypeHandler
与BirdMapper.xml
<configuration>
<typeHandlers>
<typeHandler handler="com.learn.zqw.handler.SexEnumTypeHandler"
javaType="com.learn.zqw.generator.domain.SexEnum"
jdbcType="SMALLINT"/>
typeHandlers>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_learn?useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="com/learn/zqw/IUserMapper.xml" />
<mapper resource="com/learn/zqw/generator/mapper/StudentMapper.xml" />
<mapper resource="com/learn/zqw/generator/mapper/BirdMapper.xml" />
<package name="com.learn.zqw.sqlannotation.mapper"/>
mappers>
configuration>
6、修改自动生成的BirdMapper
,将sex
字段的定义都加上typeHandler
<mapper namespace="com.learn.zqw.generator.mapper.BirdMapper">
<resultMap id="BaseResultMap" type="com.learn.zqw.generator.domain.Bird">
<id column="id" jdbcType="INTEGER" property="id" />
<result column="name" jdbcType="VARCHAR" property="name" />
<result column="sex" jdbcType="SMALLINT" property="sex" typeHandler="com.learn.zqw.handler.SexEnumTypeHandler" />
resultMap>
<sql id="Example_Where_Clause">
<where>
<foreach collection="oredCriteria" item="criteria" separator="or">
<if test="criteria.valid">
<trim prefix="(" prefixOverrides="and" suffix=")">
<foreach collection="criteria.criteria" item="criterion">
<choose>
<when test="criterion.noValue">
and ${criterion.condition}
when>
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value}
when>
<when test="criterion.betweenValue">
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
when>
<when test="criterion.listValue">
and ${criterion.condition}
<foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
#{listItem}
foreach>
when>
choose>
foreach>
trim>
if>
foreach>
where>
sql>
<sql id="Update_By_Example_Where_Clause">
<where>
<foreach collection="example.oredCriteria" item="criteria" separator="or">
<if test="criteria.valid">
<trim prefix="(" prefixOverrides="and" suffix=")">
<foreach collection="criteria.criteria" item="criterion">
<choose>
<when test="criterion.noValue">
and ${criterion.condition}
when>
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value}
when>
<when test="criterion.betweenValue">
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
when>
<when test="criterion.listValue">
and ${criterion.condition}
<foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
#{listItem}
foreach>
when>
choose>
foreach>
trim>
if>
foreach>
where>
sql>
<sql id="Base_Column_List">
id, name, sex
sql>
<select id="selectByExample" parameterType="com.learn.zqw.generator.domain.BirdExample" resultMap="BaseResultMap">
select
<if test="distinct">
distinct
if>
<include refid="Base_Column_List" />
from bird
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
if>
<if test="orderByClause != null">
order by ${orderByClause}
if>
select>
<select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from bird
where id = #{id,jdbcType=INTEGER}
select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
delete from bird
where id = #{id,jdbcType=INTEGER}
delete>
<delete id="deleteByExample" parameterType="com.learn.zqw.generator.domain.BirdExample">
delete from bird
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
if>
delete>
<insert id="insert" parameterType="com.learn.zqw.generator.domain.Bird">
insert into bird (id, name, sex
)
values (#{id,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR},
#{sex,jdbcType=SMALLINT, typeHandler=com.learn.zqw.handler.SexEnumTypeHandler}
)
insert>
<insert id="insertSelective" parameterType="com.learn.zqw.generator.domain.Bird">
insert into bird
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
id,
if>
<if test="name != null">
name,
if>
<if test="sex != null">
sex,
if>
trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
#{id,jdbcType=INTEGER},
if>
<if test="name != null">
#{name,jdbcType=VARCHAR},
if>
<if test="sex != null">
#{sex,jdbcType=SMALLINT},
if>
trim>
insert>
<select id="countByExample" parameterType="com.learn.zqw.generator.domain.BirdExample" resultType="java.lang.Long">
select count(*) from bird
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
if>
select>
<update id="updateByExampleSelective" parameterType="map">
update bird
<set>
<if test="record.id != null">
id = #{record.id,jdbcType=INTEGER},
if>
<if test="record.name != null">
name = #{record.name,jdbcType=VARCHAR},
if>
<if test="record.sex != null">
sex = #{record.sex,jdbcType=SMALLINT, typeHandler=com.learn.zqw.handler.SexEnumTypeHandler},
if>
set>
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
if>
update>
<update id="updateByExample" parameterType="map">
update bird
set id = #{record.id,jdbcType=INTEGER},
name = #{record.name,jdbcType=VARCHAR},
sex = #{record.sex,jdbcType=SMALLINT, typeHandler=com.learn.zqw.handler.SexEnumTypeHandler}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
if>
update>
<update id="updateByPrimaryKeySelective" parameterType="com.learn.zqw.generator.domain.Bird">
update bird
<set>
<if test="name != null">
name = #{name,jdbcType=VARCHAR},
if>
<if test="sex != null">
sex = #{sex,jdbcType=SMALLINT, typeHandler=com.learn.zqw.handler.SexEnumTypeHandler},
if>
set>
where id = #{id,jdbcType=INTEGER}
update>
<update id="updateByPrimaryKey" parameterType="com.learn.zqw.generator.domain.Bird">
update bird
set name = #{name,jdbcType=VARCHAR},
sex = #{sex,jdbcType=SMALLINT, typeHandler=com.learn.zqw.handler.SexEnumTypeHandler}
where id = #{id,jdbcType=INTEGER}
update>
mapper>
7、编写单元测试
package com.learn.zqw;
import com.learn.zqw.generator.domain.Bird;
import com.learn.zqw.generator.domain.BirdExample;
import com.learn.zqw.generator.domain.SexEnum;
import com.learn.zqw.generator.mapper.BirdMapper;
import lombok.Cleanup;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
*
* @author zhuquanwen
* @vesion 1.0
* @date 2020/3/16 22:14
* @since jdk1.8
*/
@RunWith(JUnit4.class)
public class TypeHandlerTests {
/**
* 测试查询所有
* */
@Test
public void test() throws IOException {
@Cleanup InputStream in = Resources.getResourceAsStream ("SqlMapConfig.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
SqlSession session = factory.openSession();
BirdMapper mapper = session.getMapper(BirdMapper.class);
BirdExample birdExample = new BirdExample();
List<Bird> birds = mapper.selectByExample(birdExample);
Assert.assertNotNull(birds);
if (birds != null) {
birds.forEach(System.out::println);
}
Bird bird = new Bird();
bird.setName("小紫");
bird.setSex(SexEnum.MALE);
mapper.insert(bird);
session.commit();
}
}