一:TypeHandler的定义
mybatis是orm(对象关系模型)框架,需要实现pojo与数据库jdbcType的转换,当设置参数时,会调用到DefaultParameterHandler的setParameter方法,根据参数寻找不同的TypeHandler,将参数设置到PreparedStatement语句中。当返回结果时,会调用DefaultResultSetHandler的getPropertyMappingValue方法,根据propertyMapping找到TypeHandler,
调用TypeHandler的getResult方法,从ResultSet中读取数据,然后封装到pojo上
二:自定义TypeHandler的使用
1: 定义一个币种的枚举
public enum Currency { cny("CNY","人民币"), usd("USD","美元"); private String code; private String desc; Currency(String code,String desc){ this.code = code; this.desc = desc; } private static Map<String,Currency> map = new ConcurrentHashMap<>(); static{ for(Currency currency:values()){ map.put(currency.code,currency); } } public static Currency getCurrencyByCode(String code){ return map.get(code); } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } }
定义一个关于币种转换的自定义TypeHandler,设置参数时,将枚举的币种值转换为字符串,设置到statement中,返回
结果时,将字符串的值转换为枚举值
public class CurrencyTypeHandler extends BaseTypeHandler<Currency> { @Override public void setNonNullParameter(PreparedStatement ps, int i, Currency parameter, JdbcType jdbcType) throws SQLException { ps.setString(i,parameter.getCode()); } @Override public Currency getNullableResult(ResultSet rs, String columnName) throws SQLException { String currency = rs.getString(columnName); return Currency.getCurrencyByCode(currency); } @Override public Currency getNullableResult(ResultSet rs, int columnIndex) throws SQLException { return null; } @Override public Currency getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { return null; } }
mybatis-config.xml中的配置:
<typeHandlers> <typeHandler javaType="com.example.mybatis.model.Currency" jdbcType="VARCHAR" handler="com.example.mybatis.model.CurrencyTypeHandler"/> </typeHandlers>
mapper.xml中resultMap元素直接配置result子元素:
<mapper namespace="com.example.mybatis.mapper.UserMapper"> <resultMap id="baseResultMap" type="com.example.mybatis.model.User"> <result column="id" property="id" jdbcType="INTEGER"></result> <result column="name" property="name" jdbcType="VARCHAR"></result> <result column="age" property="age" jdbcType="VARCHAR"></result> <result column="currency" property="currency" jdbcType="VARCHAR" typeHandler="com.example.mybatis.model.CurrencyTypeHandler"></result> </resultMap> <resultMap id="test" type="com.example.mybatis.model.User"/> <insert id="insert" parameterType="com.example.mybatis.model.User"> insert into user (name,age) value(#{name},#{age}) </insert> <select id="listUsers" resultMap="baseResultMap" parameterType="com.example.mybatis.model.User"> select * from user where name = #{name} and currency = #{currency,javaType=com.example.mybatis.model.Currency, jdbcType=VARCHAR,typeHandler=com.example.mybatis.model.CurrencyTypeHandler} </select> </mapper>
测试代码:
public static void main(String[] args) throws IOException { // 将mybatis-config的配置文件读入内存,生成字符流对象 Reader reader = Resources.getResourceAsReader("mybatis-config.xml"); // 解析全局配置文件mybatis-config.xml SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory = builder.build(reader); SqlSession sqlSession = sqlSessionFactory.openSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // PageHelper.startPage(1,2); // 测试一级缓存: List<User> list = userMapper.listUsers("hello105", Currency.USD); System.out.println("第一次查询结果:" + list.size()); System.out.println("第一次查询结果:" + list); }
运行结果:字符串已经转换为枚举值:
1:在mybatis-config.xml中配置 typeHandlers元素
2:在mapper.xml中配置result子元素,配置typeHandler属性,在#{currency}中配置currency的jdbcType、javaType以及typeHandler属性
或者不在mybatis-config.xml中配置typeHandler元素,直接在mapper.xml中的result子元素上配置typeHandler属性
三:从源码层面分析一下如何实现的
1:注册typeHandler
解析typeHandler元素,然后调用register方法注册
将解析后的javaType以及jdbcType ,handler缓存到map中
设置参数的时候,调用preparedStatement 的setParameters方法:
如果parameter是Currency类型,那么获取TypeHandler,调用typeHandler的setParameter方法:
调用到自定义的CurrencyTypeHandler类,将枚举值Currency获取String值,set到preparedStatement中:
返回值的时候,从resulstSet中取出字符串值,需要转换成Currency类型
从ResultSet中获取结果:
从resultSet中读取到字符串的值,然后根据字符串转为Currency类型对象
总结:
mybatis-config.xml中配置的typeHandlers元素可以不用配置,不配置就使用懒加载的方式,在解析sql时创建typeHandler对象,然后实现注册。
1:参数设置时的转换,需要在参数上配置属性#{jdbcType= javaType= typeHandler=},在设置参数使,ps会根据参数属性找到typeHandler,然后转换
2:获取结果时,需要在resultMap上面配置result子元素,在获取结果时,会根据参数属性找到typeHandler,然后拿到resultSet中的原始值,然后在自定义的
TypeHandler中实现转换。