Mybatis自定义typehandler
自定义typehandler
官方的建议是继承BaseTypeHandler,其中Typehandler可以受一个type参数,Mybatis会自动传进来实际的class类型。/** * 用来处理我们自定义的枚举 * @author sut * @version $Revision:$ */ public class GenericEnumUserType<E extends StringEnumTypeImp> extends BaseTypeHandler<E>{ //mybatis will pass actual class when constructing TypeHandler private Class<E> type; private static final String fromStringCode = "fromStringCode"; public GenericEnumUserType(Class<E> type){ Preconditions.checkNotNull(type, "Type argument cannot be null"); this.type = type; } /** * 设置非空参数 * @see org.apache.ibatis.type.BaseTypeHandler#setNonNullParameter(java.sql.PreparedStatement, int, java.lang.Object, org.apache.ibatis.type.JdbcType) */ @Override public void setNonNullParameter(PreparedStatement ps, int i, StringEnumTypeImp parameter, JdbcType jdbcType) throws SQLException { ps.setString(i, parameter.getStoreValue()); } /** * 得到非空结果,然后转换成E<br> * 利用反射调用静态的方法<br> * @see org.apache.ibatis.type.BaseTypeHandler#getNullableResult(java.sql.ResultSet, java.lang.String) */ @Override @SuppressWarnings("unchecked") public E getNullableResult(ResultSet rs, String columnName) throws SQLException { String storeValue = rs.getString(columnName); Preconditions.checkNotNull(type, "Type argument cannot be null"); try { Method fromMethod = type.getMethod(fromStringCode, String.class); return (E) fromMethod.invoke(null, storeValue); } catch (IllegalAccessException | NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException e) { e.printStackTrace(); } return null; } /** * 空的结果返回类型 * @see org.apache.ibatis.type.BaseTypeHandler#getNullableResult(java.sql.ResultSet, int) */ @Override public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException { try { return type.newInstance(); } catch (InstantiationException | IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } /** * 针对callback的空值结果 * @see org.apache.ibatis.type.BaseTypeHandler#getNullableResult(java.sql.CallableStatement, int) */ @Override public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { try { return type.newInstance(); } catch (InstantiationException | IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } }
- 注册TypeHandler
<!-- 类型处理器 -->
<typeHandlers>
<!-- 是否枚举 -->
<typeHandler handler="com.sut.util.enumerate.mybatis.GenericEnumUserType"
javaType="com.sut.util.meta.WhetherTypeEnum" jdbcType="VARCHAR"/>
<!-- 性别枚举 -->
<typeHandler handler="com.sut.util.enumerate.mybatis.GenericEnumUserType"
javaType="com.sut.util.meta.SexTypeEnum" jdbcType="VARCHAR" />
</typeHandlers>
这样我们在mapper就可以使用了。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.sut.persist.dao.UserDao"> <!-- 定义User实体 --> <resultMap type="com.sut.persist.entity.User" id="userResult"> <id property="id" javaType="int" column="id" /> <result property="username" javaType="String" column="USERNAME" /> <result property="password" javaType="String" column="PASSWORD" /> <result property="enable" javaType="com.sut.util.meta.WhetherTypeEnum" column="enabled" typeHandler="com.sut.util.enumerate.mybatis.GenericEnumUserType" /> <result property="createTime" javaType="java.util.Date" column="CREATE_TIME" /> <result property="lastLoginTime" javaType="java.util.Date" column="LAST_LOGIN_DATE" /> <result property="sex" javaType="com.sut.util.meta.SexTypeEnum" column="SEX" typeHandler="com.sut.util.enumerate.mybatis.GenericEnumUserType" /> <result property="email" javaType="String" column="EMAIL" /> <result property="mobile" javaType="String" column="MOBILE" /> <result property="province" javaType="String" column="PROVINCE" /> <result property="city" javaType="String" column="CITY" /> <result property="area" javaType="String" column="AREA" /> <result property="address" javaType="String" column="ADDRESS" /> <result property="remark" javaType="String" column="REMARK" /> </resultMap> <!-- query user by id --> <select id="getById" parameterType="int" resultMap="userResult"> select * from user where id = #{id} </select> <!-- query user by name --> <select id="getByName" parameterType="String" resultMap="userResult"> select * from user where username = #{username} </select> <!-- query all user.这里将所有列写出来了,因为最初会报错。字段好像映射的顺序不一样 --> <select id="getAll" resultMap="userResult"> select id as id, username as username, password as password, enabled as enable, create_time as createTime, last_login_date as lastLoginTime, email as email, mobile as mobile, province as province, city as city, area as area, address as address, remark as remark from user </select> <!-- delete by id --> <delete id="deleteById" parameterType="int"> delete from user where id = #{id} </delete> <!-- insert User --> <insert id="insert"> insert into user( username, password, enabled, create_time, last_login_date, sex, email, mobile, province, city, area, address, remark )values( #{username}, #{password}, #{enable, typeHandler=com.sut.util.enumerate.mybatis.GenericEnumUserType, jdbcType=VARCHAR, javaType=com.sut.util.meta.WhetherTypeEnum}, #{createTime}, #{lastLoginDate}, #{sex, typeHandler=com.sut.util.enumerate.mybatis.GenericEnumUserType, jdbcType=VARCHAR, javaType=com.sut.util.meta.SexTypeEnum}, #{email}, #{mobile}, #{province}, #{city}, #{area}, #{address}, #{remark} ) </insert> </mapper>
- 测试insert和getAll()方法是可以的。
UPDATE:
2017/01/07 有一个问题:
当使用自定义typeHandler的时候,如果bean的属性名和表字段的名称是一样的话,就会被mybatis分为mappedColumns,获取的typeHandler的rawType就不正确,如果是不一样的话,就会成为unMappedColumns, 获取的就是对的. 在测试过程中,即便设置AutoMappingBehavior=NONE,也还是会执行column分类. 最后解决办法只能是改变bean的property名称。2017/01/23 来自SO的回答说:
说是将ResultMapping定义的时候,将education的TypeHandler移除掉,然后指定好javaType,jdbcType就可以了。试了一下,果然可以。但是不明白为什么这样。