mybatis typehandler
建立TypeHandler
我们知道java有java的数据类型,数据库有数据库的数据类型,那么我们在往数据库中插入数据的时候是如何把java类型当做数据库类型插入数据库,在从数据库读取数据的时候又是如何把数据库类型当做java类型来处理呢?这中间必然要经过一个类型转换。在Mybatis中我们可以定义一个叫做TypeHandler类型处理器的东西,通过它可以实现Java类型跟数据库类型的相互转换。下面将就如何建立自己的TypeHandler做一个简要介绍。
TypeHandler接口
在Mybatis中要实现自己的TypeHandler就需要实现Mybatis为我们提供的TypeHandler接口。在TypeHandler中定义了四个方法:
public interface TypeHandler<T> {
/**
* 用于定义在Mybatis设置参数时该如何把Java类型的参数转换为对应的数据库类型
* @param ps 当前的PreparedStatement对象
* @param i 当前参数的位置
* @param parameter 当前参数的Java对象
* @param jdbcType 当前参数的数据库类型
* @throws SQLException
*/
void setParameter(PreparedStatement ps, int i, T parameter,
JdbcType jdbcType) throws SQLException;
/**
* 用于在Mybatis获取数据结果集时如何把数据库类型转换为对应的Java类型
* @param rs 当前的结果集
* @param columnName 当前的字段名称
* @return 转换后的Java对象
* @throws SQLException
*/
T getResult(ResultSet rs, String columnName) throws SQLException;
/**
* 用于在Mybatis通过字段位置获取字段数据时把数据库类型转换为对应的Java类型
* @param rs 当前的结果集
* @param columnIndex 当前字段的位置
* @return 转换后的Java对象
* @throws SQLException
*/
T getResult(ResultSet rs, int columnIndex) throws SQLException;
/**
* 用于Mybatis在调用存储过程后把数据库类型的数据转换为对应的Java类型
* @param cs 当前的CallableStatement执行后的CallableStatement
* @param columnIndex 当前输出参数的位置
* @return
* @throws SQLException
*/
T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}
现在假设我们有一个实体对象User,其中有一个属性interests是String数组类型,如下所示:
public class User {
private int id;
private String name;
private int age;
private String[] interests;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String[] getInterests() {
return interests;
}
public void setInterests(String[] interests) {
this.interests = interests;
}
@Override
public String toString() {
return "User [age=" + age + ", id=" + id + ", interests="
+ Arrays.toString(interests) + ", name=" + name + "]";
}
}
我们需要把它以拼接字符串的形式存到数据库中,然后在取出来的时候又把它还原为一个String数组。这个时候我们就可以给它定义一个TypeHandler专门来处理String数组类型和数据库VARCHAR类型的相互转换。在这里我们建立一个名叫StringArrayTypeHandler的TypeHandler,代码如下所示:
package com.tiantian.mybatis.handler;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
public class StringArrayTypeHandler implements TypeHandler<String[]> {
public String[] getResult(ResultSet rs, String columnName)
throws SQLException {
String columnValue = rs.getString(columnName);
return this.getStringArray(columnValue);
}
public String[] getResult(ResultSet rs, int columnIndex)
throws SQLException {
String columnValue = rs.getString(columnIndex);
return this.getStringArray(columnValue);
}
public String[] getResult(CallableStatement cs, int columnIndex)
throws SQLException {
// TODO Auto-generated method stub
String columnValue = cs.getString(columnIndex);
return this.getStringArray(columnValue);
}
public void setParameter(PreparedStatement ps, int i, String[] parameter,
JdbcType jdbcType) throws SQLException {
if (parameter == null)
ps.setNull(i, Types.VARCHAR);
else {
StringBuffer result = new StringBuffer();
for (String value : parameter)
result.append(value).append(",");
result.deleteCharAt(result.length()-1);
ps.setString(i, result.toString());
}
}
private String[] getStringArray(String columnValue) {
if (columnValue == null)
return null;
return columnValue.split(",");
}
}
BaseTypeHandler抽象类
在实现自己的TypeHandler时,除了上面提到的实现最原始的接口之外,Mybatis还为我们提供了一个实现了TypeHandler接口的抽象类BaseTypeHandler。所以我们也可以通过继承BaseTypeHandler来实现自己的TypeHandler。
我们先来看一下BaseTypeHandler类的定义:
public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {
protected Configuration configuration;
public void setConfiguration(Configuration c) {
this.configuration = c;
}
public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
if (parameter == null) {
if (jdbcType == null) {
throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
}
try {
ps.setNull(i, jdbcType.TYPE_CODE);
} catch (SQLException e) {
throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
"Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " +
"Cause: " + e, e);
}
} else {
setNonNullParameter(ps, i, parameter, jdbcType);
}
}
public T getResult(ResultSet rs, String columnName) throws SQLException {
T result = getNullableResult(rs, columnName);
if (rs.wasNull()) {
return null;
} else {
return result;
}
}
public T getResult(ResultSet rs, int columnIndex) throws SQLException {
T result = getNullableResult(rs, columnIndex);
if (rs.wasNull()) {
return null;
} else {
return result;
}
}
public T getResult(CallableStatement cs, int columnIndex) throws SQLException {
T result = getNullableResult(cs, columnIndex);
if (cs.wasNull()) {
return null;
} else {
return result;
}
}
public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
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;
}
我们可以看到BaseTypeHandler对TypeHandler接口的四个方法做了一个简单的选择,把null值的情况都做了一个过滤,核心的取值和设值的方法还是抽象出来了供子类来实现。使用BaseTypeHandler还有一个好处是它继承了另外一个叫做TypeReference的抽象类,通过TypeReference的getRawType()方法可以获取到当前TypeHandler所使用泛型的原始类型。这对Mybatis在注册TypeHandler的时候是非常有好处的。在没有指定javaType的情况下,Mybatis在注册TypeHandler时可以通过它来获取当前TypeHandler所使用泛型的原始类型作为要注册的TypeHandler的javaType类型,这个在讲到Mybatis注册TypeHandler的方式时将讲到。
当通过继承BaseTypeHandler来实现自己的TypeHandler时,我们的StringArrayTypeHandler应该这样写:
public class StringArrayTypeHandler extends BaseTypeHandler<String[]> {
@Override
public String[] getNullableResult(ResultSet rs, String columnName)
throws SQLException {
return getStringArray(rs.getString(columnName));
}
@Override
public String[] getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
return this.getStringArray(rs.getString(columnIndex));
}
@Override
public String[] getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
return this.getStringArray(cs.getString(columnIndex));
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i,
String[] parameter, JdbcType jdbcType) throws SQLException {
//由于BaseTypeHandler中已经把parameter为null的情况做了处理,所以这里我们就不用再判断parameter是否为空了,直接用就可以了
StringBuffer result = new StringBuffer();
for (String value : parameter)
result.append(value).append(",");
result.deleteCharAt(result.length()-1);
ps.setString(i, result.toString());
}
private String[] getStringArray(String columnValue) {
if (columnValue == null)
return null;
return columnValue.split(",");
}
}
注册TypeHandler
建立了自己的TypeHandler之后就需要把它注册到Mybatis的配置文件中,让Mybatis能够识别并使用它。注册TypeHandler主要有两种方式,一种是通过在Mybatis配置文件中定义typeHandlers元素的子元素typeHandler来注册;另一种是通过在Mybatis配置文件中定义typeHandlers元素的子元素package来注册。使用typeHandler子元素注册时一次只能注册一个TypeHandler,而使用package子元素注册时,Mybatis会把指定包里面的所有TypeHandler都注册为TypeHandler。使用typeHandler子元素注册时我们需要通过它的handler属性来指明当前要注册的TypeHandler的全名称,这个属性是必须要的。另外还有两个附加属性可以指定,一个是javaType,用以指定对应的java类型;另一个是jdbcType,用以指定对应的jdbc类型。使用package子元素注册时需要我们通过它的name属性来指定要扫描的包,如果这个时候我们也需要指定对应TypeHandler的javaType和jdbcType的话就需要我们在TypeHandler类上使用注解来定义了。Mybatis注册TypeHandler最基本的方式就是建立一个javaType、jdbcType和TypeHandler的对应关系。在使用typeHandler子元素进行注册的时候,有三种类型的注册方式:
1.如果我们指定了javaType和jdbcType,那么Mybatis会注册一个对应javaType和jdbcType的TypeHandler。
2.如果我们只指定了javaType属性,那么这个时候又分两种情况:
(1)如果我们通过注解的形式在TypeHandler类上用@MappedJdbcTypes指定了对应的jdbcType,那么Mybatis会一一注册指定的javaType、jdbcType和TypeHandler的组合,也包括使用这种形式指定了jdbcType为null的情况。现假设我们有如下这样一个StringArrayTypeHandler:
@MappedJdbcTypes({JdbcType.VARCHAR})
public class StringArrayTypeHandler implements TypeHandler<String[]> {
//..中间的实现代码省略了
//..
}
然后我们在Mybatis的配置文件中这样注册它:
<typeHandlers> <typeHandler handler="com.tiantian.mybatis.handler.StringArrayTypeHandler" javaType="[Ljava.lang.String;"/> </typeHandlers>
则Mybatis在实际注册的时候是以javaType为String数组,jdbcType为VARCHAR来注册StringArrayTypeHandler的。
mybatis typehandler的更多相关文章
- mybatis typeHandler类型转换器
typeHandler类型转换器 在JDBC中,需要在PreparedStatement对象中设置那些已经预编译过的SQL语句的参数.执行SQL后,会通过ResultSet对象获取得到数据库的数据,而 ...
- mybatis基础,mybatis配置文件核心组件typeHandler元素
无论是从预处理语句中设置一个值,还是从结果集里取出一个值,都会用类型处理器将获取的值以合适的方式转换成 Java 类型 可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型 实现 o ...
- mybatis 枚举typeHandler
枚举typeHandler 在绝大多数情况下,typeHandler因为枚举而使用,MyBatis已经定义了两个类作为枚举类型的支持,这两个类分别是: •EnumOrdinalTypeHandler. ...
- 使用Mybatis的TypeHandler加解密数据
使用Mybatis的TypeHandler加解密数据 一.背景 二.解决方案 三.需求 四.实现思路 1.编写一个实体类,凡是此实体类的数据都表示需要加解密的 2.编写一个加解密的`TypeHandl ...
- TypeHandler的简单实例
转自:http://ccchhhlll1988-163-com.iteye.com/blog/1420149 TypeHandler是MyBatis config文件中可选的配置选项,其可以对实体属性 ...
- MyBatis里字段到枚举类型的转换/映射
一.简介 我们在用MyBatis里,很多时间有这样一个需求:bean里有个属性是枚举,在DB存储时我们想存的枚举的代号,从DB拿出来时想直接映射成目标枚举类型,也即代号字段与Java枚举类的相互类型转 ...
- MyBatis里json型字段到Java类的映射
一.简介 我们在用MyBatis里,很多时间有这样一个需求:bean里有个属性是非基本数据类型,在DB存储时我们想存的是json格式的字符串,从DB拿出来时想直接映射成目标类型,也即json格式的字符 ...
- mybatis(二)
一级缓存和二级缓存 mybatis一二级缓存测试实例: package com.atguigu.mybatis.test; import java.io.IOException; import jav ...
- 深入理解MyBatis的原理(三):配置文件用法(续)
前言:前文讲解了 MyBatis 的配置文件一部分用法,本文将继续讲解 MyBatis 的配置文件的用法. 目录 1.typeHandler 类型处理器 2.ObjectFactory 3.插件 4. ...
随机推荐
- Mysql 掌握要点
1. 引擎 InnoDB与MyISAM的最大不同有两点:一是支持事务(TRANSACTION):二是采用了行级锁. 行级锁和表级锁本来就有许多不同之处,另外,事务的引入也带来了一些新问题. 1.1 I ...
- win7系统盘扩容后不识别修复方法
谢天谢地 终于在不重装系统的情况下,把C盘修复好了. win7系统,C盘之前是200G,使用中慢慢的就用完, 虽然把几乎所有的软件都移植到D盘了但是还是不能释放更多的空间.剩下60多G的空间,一般够用 ...
- jsp、freemarker、velocity区别详解
详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt172 在java领域,表现层技术主要有三种:jsp.freemarker.v ...
- 第4阶段——制作根文件系统之分析init进程(2)
本节目标: (1) 了解busybox(init进程和命令都放在busybox中) (2) 创建SI工程,分析busybox源码来知道init进程做了哪些事情 (3) 分析busybox中init进 ...
- python怎么导入自定义函数
python 编程中经常需要调用自己定义的函数,在大型程序中自定义的函数一般会和main函数分开,这么主要讲下在不同文件下定义的函数怎么调用: 首先在有文件夹test_python文件夹下有main. ...
- cocos2dx lua中异步加载网络图片,可用于显示微信头像
最近在做一个棋牌项目,脚本语言用的lua,登录需要使用微信登录,用户头像用微信账户的头像,微信接口返回的头像是一个url,那么遇到的一个问题就是如何在lua中异步加载这个头像,先在引擎源码里找了下可能 ...
- Nhibernate学习教程(2)-- 第一个NHibernate程序
NHibernate之旅(2):第一个NHibernate程序 本节内容 开始使用NHibernate 1.获取NHibernate 2.建立数据库表 3.创建C#类库项目 4.设计Domain 4- ...
- 对eigrp默认网络的理解!
EIGRP 默认网络设置的个人总结 (了解即可) //该机制同rip和ospf的default-informationoriginate,原理相同,只是配置格式不同. //!!(唯一作用)该命令起到减 ...
- 团队作业4——第一次项目冲刺(Alpha版本)第六天and第七天
团队作业4--第一次项目冲刺(Alpha版本)第六天and第七天 第一次项目冲刺(Alpha版本)第六天 一.Daily Scrum Meeting照片 二.燃尽图 1.解释说明横纵坐标代表的含义 ...
- mysql外键设置选项
. cascade方式 在父表上update/delete记录时,同步update/delete掉子表的匹配记录 . set null方式 在父表上update/delete记录时,将子表上匹配记录的 ...