一、TypeHandler
TypeHandler 在MyBatis中进行数据库类型和JavaBean类型的映射。
TypeHandler<T> 接口:
该接口中声明了四个抽象方法,说明如下:
//将parameter对象转换为字符串存入到 ps 对象的i位置
public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
//从结果集中获取数据库对应查询结果
//将字符串还原为原始的T类型对象
public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;
public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException;
public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException;
BaseHandler<T> 类:
BaseTypeHandler 抽象类实现了上面的四个抽象方法,做了简单的包装,然后又留出四个抽象方法。
TypeHandle 的子类:
所有的类型处理器都直接或间接继承了 TypeHandler类。
二、MyBatis 处理枚举类型
如果我们在 Employee 中又新增一个枚举类型的属性来表示员工的状态,那么在 MyBatis 里面会如何保存的呢?
员工状态枚举类 EmpStatus:
public enum EmpStatus {
LOGIN(100, "用户登录"),
LOGOUT(200, "用户退出"),
REMOVE(300, "用户不存在");
private Integer code;
private String msg;
private EmpStatus(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
//根据状态码返回枚举对象
public static EmpStatus getEmpStatusByCode(Integer code) {
switch (code) {
case 100:
return LOGIN;
case 200:
return LOGOUT;
case 300:
return REMOVE;
default:
return LOGOUT;
}
}
}
Employee 类:
public class Employee {
private Integer id;
private String lastName;
private String gender;
private String email;
private EmpStatus empStatus = EmpStatus.LOGOUT;
}
接口中添加方法:
public Long addEmp(Employee employee);
SQL映射文件配置:
<!--
public Long addEmp(Employee employee);
-->
<insert id="addEmp" parameterType="com.njf.mybatis.bean.Employee" useGeneratedKeys="true" keyProperty="id">
insert into tbl_employee(`last_name`, `email`, `gender`, `empStatus`)
values(#{lastName}, #{email}, #{gender}, #{empStatus})
</insert>
测试:
@Test
public void testEnum() throws IOException {
//1、获取 sqlSessionFactory
SqlSessionFactory sqlSessionFactory = getsqlSessionFactory();
//2、获取 sqlSession 实例,获取批量执行器
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
//3、获取接口的实现类对象
EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
Employee emp = new Employee(null, "test_enum", "1", "enum@126.com", EmpStatus.LOGIN);
employeeMapper.addEmp(emp);
sqlSession.commit();
} finally {
sqlSession.close();
}
}
运行结果:
可以看到默认情况下MyBatis处理枚举类型使用的是 EnumTypeHandler。
如果要使用 EnumOrdinalTypeHandler 类型呢?
可以看到如果配置的 EnumOridnalTypeHandler 类型处理器,保存的就会是枚举的索引值?
如何配置呢?
在 MyBatis 的核心配置文件中配置如下内容:
<typeHandlers>
<typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="com.njf.mybatis.bean.EmpStatus" />
</typeHandlers>
测试效果:
这是在数据库保存的就是枚举的索引值了。
小结:
默认myBatis在处理枚举的时候保存的是枚举的名字:EnumTypeHandler。
改变使用:EnumOrdinalTypeHandler,在核心配置文件中进行设置
<typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="com.njf.mybatis.bean.EmpStatus" />
三、自定义 TypeHandler 处理枚举
如果我们想要自己自定义保存的数据呢?
我们可以通过自定义TypeHandler的形式来在设置参数或者取出结果集的时候自定义参数封装策略。
步骤:
(1)实现TypeHandler接口或者继承BaseTypeHandler;
(2)使用@MappedTypes定义处理的java类型,使用@MappedJdbcTypes定义jdbcType类型;
(3)在自定义结果集标签或者参数处理的时候声明使用自定义TypeHandler进行处理或者在全局配置TypeHandler要处理的javaType
自定义枚举类型的 TypeHandler:
/**
*
* 1、实现 TypeHandler<T> 接口或者继承 BaseTypeHandler<T>
*/
public class MyEnumEmpStatusTypeHandler implements TypeHandler<EmpStatus> {
/**
* 定义当前数据如何保存到数据库中
* @param ps
* @param i
* @param parameter
* @param jdbcType
* @throws SQLException
*/
@Override
public void setParameter(PreparedStatement ps, int i, EmpStatus parameter, JdbcType jdbcType) throws SQLException {
System.out.println("要保存的状态码:" + parameter.getCode());
ps.setString(i, parameter.getCode().toString());
}
@Override
public EmpStatus getResult(ResultSet rs, String columnName) throws SQLException {
int code = rs.getInt(columnName);
//需要根据从数据库中拿到的枚举状态码返回一个枚举对象
EmpStatus empStatus = EmpStatus.getEmpStatusByCode(code);
return empStatus;
}
@Override
public EmpStatus getResult(ResultSet rs, int columnIndex) throws SQLException {
int code = rs.getInt(columnIndex);
EmpStatus empStatus = EmpStatus.getEmpStatusByCode(code);
return empStatus;
}
@Override
public EmpStatus getResult(CallableStatement cs, int columnIndex) throws SQLException {
int code = cs.getInt(columnIndex);
EmpStatus empStatus = EmpStatus.getEmpStatusByCode(code);
return empStatus;
}
}
在 MyBatis 的核心配置文件中进行配置:
<typeHandlers>
<!--
1、配置自定义的TypeHandler
2、也可以在处理某个字段的时候告诉 MyBatis 用什么类型的处理器
保存:#{empStatus, typeHandler="xxx"}
查询:使用自定义 resultMap 使用 Handler
注意:如果在参数位置修改TypeHandler,应该保证保存数据和查询数据使用的TypeHandler一样。
-->
<typeHandler handler="com.njf.mybatis.typeHandler.MyEnumEmpStatusTypeHandler" javaType="com.njf.mybatis.bean.EmpStatus" />
</typeHandlers>
其他方式:
保存的时候给字段执行处理器:
<!--
public Long addEmp(Employee employee);
-->
<insert id="addEmp" parameterType="com.njf.mybatis.bean.Employee" useGeneratedKeys="true" keyProperty="id">
insert into tbl_employee(`last_name`, `email`, `gender`, `empStatus`)
values(#{lastName}, #{email}, #{gender}, #{empStatus, typeHandler="全类名"})
</insert>
在查询的时候通过自定义 resultMap 来给字段指定处理器:
<resultMap id="MyEmp" type="com.njf.mybatis.bean.Employee">
<id column="id" property="id"/>
<result column="empStatus" property="empStatus" typeHandler="com.njf.mybatis.typeHandler.MyEnumEmpStatusTypeHandler"/>
</resultMap>
<select id="getEmpById" resultMap="MyEmp">
select id, last_name lastName, email, gender, empStatus from tbl_employee where id = #{id}
</select>
测试插入与查询:
@Test
public void testEnum() throws IOException {
//1、获取 sqlSessionFactory
SqlSessionFactory sqlSessionFactory = getsqlSessionFactory();
//2、获取 sqlSession 实例,获取批量执行器
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
//3、获取接口的实现类对象
EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
//Employee emp = new Employee(null, "test_enum", "1", "enum@126.com", EmpStatus.LOGIN);
//employeeMapper.addEmp(emp);
sqlSession.commit();
Employee empById = employeeMapper.getEmpById(13);
System.out.println("empById = " + empById);
System.out.println("员工状态:" + empById.getEmpStatus());
} finally {
sqlSession.close();
}
}
运行结果:
可以看到数据库保存的是我们自定义的值。
控制台打印的也是根据 code 获取的枚举类型。