iBatis的扩展组件主要有TypeHandlerCallback、CacheController、DataSourceFactory、TransactionConfig。其中TypeHandlerCallback可以实现自定义的类型处理逻辑,以便处理非标准数据库、驱动程序和(或)数据类型。
场景举例
有如下数据表,请注意字段status的类型及含义:
CREATE DATABASE ibatis;
USE test_ibatis;
CREATE TABLE person
(
id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(20) NOT NULL comment '姓名',
status TINYINT NOT NULL comment '状态(1可用,-1不可用,0默认)',
PRIMARY KEY (id)
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=utf8;
对应的实体模型类里将status定义为枚举型,如:
public class Person {
private int id;
private String name;
private Status status; // 注意这里
// getters and setters...
}
注:如果将status定义为整型,虽然与表中的字段类型一致了,但在赋值时setStatus(int status),只能传数字,不易被理解,没有人会明白应该传什么值,1或-1又代表了什么含义,且易产生脏数据,比如有意无意间会传321。
用枚举型给status赋值,既直观又限定范围。如:
......
Person person = new Person();
person.setName("yumin");
person.setStatus(Status.AVAILABLE);; // 注意这里
boolean result = DAO.insert(person);
.......
代码里出现的枚举型Status,可以这样进行定义:
public enum Status {
// 默认0, 可用1, 不可用-1
DEFAULT(0), AVAILABLE(1), UNAVAILABLE(-1);
}
但按上述做法执行写入操作时,发生了如下异常:
Caused by: java.sql.SQLException:
Incorrect integer value: 'AVAILABLE' for column 'status' at row 1
类型不匹配,AVAILABLE用在了整型字段status。
异常出现的症结在于,数据表里的字段是用整型存储这没问题,但Java代码里应该用枚举型会更友好,两者间在数据类型上就存在一定的矛盾,即"javaType=enum"而"jdbcType=integer"。这时就需用到TypeHandlerCallback,通过实现该接口来处理中间的转换。
题外话:还有一种做法,定义Person类时,定义两个status,第一个status为整型,但取消setStatus方法,只负责在数据表存储时用,另一个叫status2为枚举型,负责与外部交互,赋值时只能用status2,内部将status2转换为status,这样既能保证友好又能通过转换保持类型的一致。但暂时让我们看看若通过iBatis的扩展应该如何实现。
实例演示
TypeHandlerCallback接口,下面是它的源代码:
public abstract interface TypeHandlerCallback {
public abstract void setParameter(ParameterSetter setter, Object object)
throws SQLException;
public abstract Object getResult(ResultGetter getter) throws SQLException;
public abstract Object valueOf(String string);
}
接下来我们一起Step-by-step操作 ——
第一步:基于TypeHandlerCallback实现类
/**
*
*/
package me.yumin.java.ibatis.handler;
import com.ibatis.sqlmap.client.extensions.ParameterSetter;
import com.ibatis.sqlmap.client.extensions.ResultGetter;
import com.ibatis.sqlmap.client.extensions.TypeHandlerCallback;
import java.sql.SQLException;
import java.sql.Types;
import me.yumin.java.ibatis.enumtype.Status;
/**
* @author yumin
*
*/
public class StatusHandler implements TypeHandlerCallback {
@Override
public void setParameter(ParameterSetter setter, Object parameter)
throws SQLException {
/*
* 在sqlMap中配parameterMap的属性typeHandler
* 和sqlMapConfig中配全局typeHandler时才会触发
*/
if (null == parameter) {
setter.setNull(Types.INTEGER); // 若没有传值则null,表字段不允许则异常
} else {
setter.setInt(((Status) parameter).getValue());
}
}
@Override
public Object getResult(ResultGetter getter) throws SQLException {
/*
* 仅在sqlMap中配置resultMap的属性typeHandler
* 和在sqlMapConfig中配全局typeHandler才会触发
*/
Object result = null;
//if (!getter.wasNull() && null != getter.getObject()) {
// 上面有问题,修复如下.wasNull是判断前一个字段是否为null
if (null != getter.getObject()) {
result = getStatus(getter.getInt());
}
return result;
}
@Override
public Object valueOf(String string) {
/*
* 处理属性或参数为null情况
* 入参即为配置项nullValue
*/
Object result = null;
int integer = 0;
if (null != string && 0 < string.length()) {
try {
integer = Integer.parseInt(string);
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
result = getStatus(integer);
return result;
}
/**
*
* @param value
* @return
*/
private Object getStatus(int value) {
Object result = null;
for (Status status : Status.values()) {
if (value == status.getValue()) {
result = status;
break;
}
}
return result;
}
}
第二步:在sqlMap中指定属性typeHandler
所有完整的sqlMap配置文件如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap namespace="Person">
<typeAlias alias="Person" type="me.yumin.java.ibatis.domain.Person" />
<parameterMap class="Person" id="person">
<parameter property="name" />
<parameter property="status" typeHandler="me.yumin.java.ibatis.handler.StatusHandler" />
<!-- 若没有使用parameterMap且未给属性指定typeHandler
则在给SQL传值时不会调Handler中的setParameter方法 -->
</parameterMap>
<resultMap class="Person" id="person">
<result property="id" column="id" />
<result property="name" column="name" />
<result property="status" column="status" typeHandler="me.yumin.java.ibatis.handler.StatusHandler" nullValue="0" />
<!-- 同上若没用resultMap且未给属性指定typeHandler
则在返回表数据时不会调Handler中的getResult方法 -->
</resultMap>
<insert id="insert" parameterMap="person">
<![CDATA[
INSERT INTO person (name, status) VALUES (?, ?)
]]>
<selectKey keyProperty="id" resultClass="java.lang.Integer">
SELECT LAST_INSERT_ID()
</selectKey>
</insert>
<select id="query" parameterClass="java.lang.Integer" resultMap="person">
<![CDATA[
SELECT id, name, status FROM person WHERE id=#id#
]]>
</select>
</sqlMap>
主要处理过程是,因在sqlMap中给属性配置了typeHandler,所以在写或改数据前,先调用setParameter方法,再生成SQL,这时已将枚举型转换为整型;在读取数据后映射类时,会调用getResult方法,将整型转换为枚举型。
完整的示例请见附件,导入到IDE即可运行,IBatisTest是测试用例。
上述实例满足了一开始的需求,即Java类中将属性定义为枚举型,在存储时仍用整型,既友好直观地让使用者给类赋值又满足了存储需要。应用场景还有很多,如:对所有字符串型数据先压缩再存储,则可在sqlMapConfig中定义全局typeHandler,凡"javaType=java.lang.String"时,都通过该扩展在写之前先压缩,读取后先解压再返回。
若将Handler定义在sqlMapConfig,则该typeHandler是全局可用的。
全局typeHandler定义:
<sqlMapConfig>
......
<typeHandler javaType="package.Type" callback="package.Handler" />
......
</sqlMapConfig>
分享到:
相关推荐
ibatis简易使用ibatis简易使用ibatis简易使用
ibatis学习 ibatis总结 ibatis ibatis ibatis
ibatis 相关使用文档及安装包ibatis 相关使用文档及安装包
12.2 使用自定义类型处理器 217 12.2.1 实现自定义类型处理器 217 12.2.2 创建TypeHandlerCallback 218 12.2.3 注册TypeHandlerCallback以供使用 221 12.3 使用CacheController 222 12.3.1 创建CacheController 223 ...
使用ibatis和Oracle数据库实现的小项目,供初学者参考
在.net中使用iBATISNET技术!该例子使用vs2008开发。现在调整资源分为之前的一半,方便大家下载吧。
ibatis教程 主要是对这个软件的入门的介绍和如何加载和使用
使用Ibatis对数据库的访问,实现增删改查的操作 ;
ibatis 使用手册:ibatis 开发指南、ibatis sql maps、ibatis sql maps 入门教程。
ibatis资料ibatis资料ibatis资料ibatis资料ibatis资料ibatis资料ibatis资料ibatis资料ibatis资料
ibatis 读取oracle clob类型
ibatis中自己能运行的,还有能跟SSI整合的类库都在里面
ibatis的例子,快速使用ibatis,直接就能用
Ibatis3,手册,Ibatis3手册,Ibatis3使用手册,Ibatis3参考手册
ibatis中输入输出各种类型的参数分析及#与$区别
ibatis demo,ibatis例子,ibatis示例
iBATIS 级联iBATIS 级联iBATIS 级联
ibatis的原码 ibatis源码 ibatis源码 ibatis源码
” <br>但别犯愁:SQL本身具备了一些重要的功能,并且通过模板的使用,在Spring应用中采用iBATIS显得轻而易举。在此摘录中,两位作者将和你一起安装iBATIS并将其集成进你的Spring应用中。他们也阐明了怎样取得你...