mybatis 比 ibatis 改进了很多,特别是支持了注解,支持了plugin inteceptor,也给开发者带来了更多的灵活性,相比其他ORM,我还是挺喜欢mybatis的。

闲言碎语不要讲,今天研究了下mybatis的typeHandler:

先看这样一张表(postgresql)

create table user (

  id serial not null

  name character varchar(100),

  age integer,

  emails character varchar[],  -- varchar 数组 表示可以多个email

  address character varchar(2000) -- 因为地址内容为非结构化的数据,我们希望保存json格式描述的地址信息,以增加灵活性

);

这个表有2个字段值得我们注意:

1. emails 为 character varchar[] 数组类型

2. address 我们希望保存为json格式的数据,查询时返回json字符串,mybatis orm 之后可以还原为一个数据对象VO。

完成这2个需求,则需要我们标题中提到的 JsonTypeHandler & ArrayTypeHandler

先看第一个typHandler: ArrayTypeHandler

我们先准备VO的代码:

public class UserVO {

    private long id;
private String name;
private int age;
private String[] emails;
Private Object address; ......
}

其中 emails 对应数据库的 emails,address 对应数据库的 address,为什么用Object类型呢,这是因为以后我们可能会有个 AddressVO 这样的对象,也可能会有 AddressVO2 extends AddressVO 这样的对象,但是数据库的schame不会变。

接下来我们看一下 UserDao.xml 中的片段:

<resultMap type="com.kylin.test.userVO" id="userVO">
<result property="id" column="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<result property="emails" column="emails" typeHandler="com.kylin.test.util.mybatis.handler.ArrayTypeHandler"/>
<result property="address" column="address" typeHandler="com.kylin.test.util.mybatis.handler.JsonTypeHandler"/>
</resultMap>

上面的resultMap中配置了2个typeHandler,关于typeHandler的配置,mybatis有多种方法,这里简单示意一下。

再看UserDao.xml中的2个方法,需要使用到这2个handler

<insert id="addUser" parameterType="com.kylin.test.UserVO">
INSERT INTO user (
name,
age,
emails,
address)
VALUES (
#{name, jdbcType=VARCHAR},
#{age, jdbcType=INTEGER},
#{emails, jdbcType=ARRAY, typeHandler=com.kylin.test.util.mybatis.handler.ArrayTypeHandler},
#{address, jdbcType=VARCHAR, typeHandler=com.kylin.test.util.mybatis.handler.JsonTypeHandler})
</insert> <select id="getUserById" resultMap="userVO">
SELECT *
FROM user
WHERE id = #{0}
</select>

上述的addUser方法,传入了字符串数组,和Object对象,保存到了数据库中,见下面的代码:

    UserVO user = new UserVO();

    user.setName("kylin");
user.setAge(30);
user.setEmails(new String[] { "kylin@163.com", "kylin@263.com" }); Map<String, Object> address = new HashMap<String, Object>();
address.put("country", "china");
address.put("province", "guangdong");
address.put("city", "shenzhen");
user.setAddress(address); // 调用dao.addUser方法
userDao.addUser(user);

上面这个方法,将emails 字符串数组保存入数据库,将Map address,以json字符串的方式保存到数据库

select * from user;

id name     age   emails                                               address
-------------------------------------------------------------------------------
1 kylin 30 ["kylin@163.com","kylin@126.com"] {"contry":"china","province":"guangdong","city":"shenzhen"}

看到输入按期望的存入到了数据库中,稍后我们看从数据库读取出后是什么样子,我们先看看ArrayTypeHandler.java

mybatis 已经实现了 BaseTypeHandler<T> 这个抽象类,并将公共的逻辑实现好了,我们只需要继承BaseTypeHandler就好,只需要处理简单数据即可。

package com.kylin.test.util.mybatis.handler;

import java.sql.Array;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException; import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeException;

// 继承自BaseTypeHandler<Object[]> 使用时传入的参数一定要是Object[],例如 int[]是 Object, 不是Object[],所以传入int[] 会报错的
public class ArrayTypeHandler extends BaseTypeHandler<Object[]> { private static final String TYPE_NAME_VARCHAR = "varchar";
private static final String TYPE_NAME_INTEGER = "integer";
private static final String TYPE_NAME_BOOLEAN = "boolean";
private static final String TYPE_NAME_NUMERIC = "numeric"; @Override
public void setNonNullParameter(PreparedStatement ps, int i, Object[] parameter,
JdbcType jdbcType) throws SQLException { /* 这是ibatis时的做法
StringBuilder arrayString = new StringBuilder("{"); for (int j = 0, l = parameter.length; j < l; j++) {
arrayString.append(parameter[j]);
if (j < l - 1) {
arrayString.append(",");
}
} arrayString.append("}"); ps.setString(i, arrayString.toString());
*/ String typeName = null;
if (parameter instanceof Integer[]) {
typeName = TYPE_NAME_INTEGER;
} else if (parameter instanceof String[]) {
typeName = TYPE_NAME_VARCHAR;
} else if (parameter instanceof Boolean[]) {
typeName = TYPE_NAME_BOOLEAN;
} else if (parameter instanceof Double[]) {
typeName = TYPE_NAME_NUMERIC;
} if (typeName == null) {
throw new TypeException("ArrayTypeHandler parameter typeName error, your type is " + parameter.getClass().getName());
}

// 这3行是关键的代码,创建Array,然后ps.setArray(i, array)就可以了
Connection conn = ps.getConnection();
Array array = conn.createArrayOf(typeName, parameter);
ps.setArray(i, array);
} @Override
public Object[] getNullableResult(ResultSet rs, String columnName)
throws SQLException { return getArray(rs.getArray(columnName));
} @Override
public Object[] getNullableResult(ResultSet rs, int columnIndex) throws SQLException { return getArray(rs.getArray(columnIndex));
} @Override
public Object[] getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException { return getArray(cs.getArray(columnIndex));
} private Object[] getArray(Array array) { if (array == null) {
return null;
} try {
return (Object[]) array.getArray();
} catch (Exception e) {
} return null;
}
}

JsonTypeHandler 我们需要用到处理Json的第三方包:jackson,这个包据说处理json是效率最快的,代价最小的。

先封装一个JsonUtil,并提供JsonUtil.stringify(...) JsonUtil.parse(...) 这样2个方法出来

package com.kylin.test.util.json;

import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.annotate.JsonFilter;
import org.codehaus.jackson.map.ser.impl.SimpleBeanPropertyFilter;
import org.codehaus.jackson.map.ser.impl.SimpleFilterProvider;
import org.springframework.core.annotation.AnnotationUtils; public class JsonUtil { private static Log log = LogFactory.getLog(JsonUtil.class); private static ObjectMapper objectMapper = null; static { objectMapper = new ObjectMapper(); objectMapper.setDateFormat(new SimpleDateFormat(FormatUtil.DATE_FORMAT_LONG));
objectMapper.disable(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES);
objectMapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false);
objectMapper.setFilters(new SimpleFilterProvider().setFailOnUnknownId(false));
} /*
public static JsonUtil getInstance() { if (instance == null) {
synchronized (JsonUtil.class) {
if (instance == null) {
instance = new JsonUtil();
}
}
} return instance;
}
*/ public static String stringify(Object object) { try {
return objectMapper.writeValueAsString(object);
} catch (Exception e) {
log.error(e.getMessage(), e);
} return null;
} public static String stringify(Object object, String... properties) { try {
return objectMapper
.writer(new SimpleFilterProvider().addFilter(
AnnotationUtils.getValue(
AnnotationUtils.findAnnotation(object.getClass(), JsonFilter.class)).toString(),
SimpleBeanPropertyFilter.filterOutAllExcept(properties)))
.writeValueAsString(object);
} catch (Exception e) {
log.error(e.getMessage(), e);
} return null;
} public static void stringify(OutputStream out, Object object) { try {
objectMapper.writeValue(out, object);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
} public static void stringify(OutputStream out, Object object, String... properties) { try {
objectMapper
.writer(new SimpleFilterProvider().addFilter(
AnnotationUtils.getValue(
AnnotationUtils.findAnnotation(object.getClass(), JsonFilter.class)).toString(),
SimpleBeanPropertyFilter.filterOutAllExcept(properties)))
.writeValue(out, object);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
} public static <T> T parse(String json, Class<T> clazz) { if (json == null || json.length() == 0) {
return null;
} try {
return objectMapper.readValue(json, clazz);
} catch (Exception e) {
log.error(e.getMessage(), e);
} return null;
} }

接着再看看JsonTypeHandler

package com.kylin.test.util.mybatis.handler;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException; import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType; import com.kylin.test.util.json.JsonUtil;

// 继承自BaseTypeHandler<Object> 使用Object是为了让JsonUtil可以处理任意类型
public class JsonTypeHandler extends BaseTypeHandler<Object> { @Override
public void setNonNullParameter(PreparedStatement ps, int i, Object parameter,
JdbcType jdbcType) throws SQLException { ps.setString(i, JsonUtil.stringify(parameter));
} @Override
public Object getNullableResult(ResultSet rs, String columnName)
throws SQLException { return JsonUtil.parse(rs.getString(columnName), Object.class);
} @Override
public Object getNullableResult(ResultSet rs, int columnIndex) throws SQLException { return JsonUtil.parse(rs.getString(columnIndex), Object.class);
} @Override
public Object getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException { return JsonUtil.parse(cs.getString(columnIndex), Object.class);
} }

至此,JsonTypeHandler 和 ArrayTypeHandler 就分享介绍完了,

如前面的 resultMap的配置,当调用 getUserById方法时,会返回 String[], 和Map<String, Object>对象回来,

有了这2个基础TypeHandler,接下来设计数据库和数据结构就会方便和灵活很多了。

mybatis 处理数组类型及使用Json格式保存数据 JsonTypeHandler and ArrayTypeHandler的更多相关文章

  1. PHP接收JSON格式的数据

    在API服务中,目前流行采用json形式来交互. 给前端调用的接口输出Json数据,这个比较简单,只需要组织好数据,用json_encode($array) 转化一下,前端就得到json格式的数据. ...

  2. 使用google的GSON解析json格式的数据

    GSON是谷歌提供的开源库,用来解析Json格式的数据,非常好用.如果要使用GSON的话,则要先下载gson-2.2.4.jar这个文件,如果是在Android项目中使用,则在Android项目的li ...

  3. javascript 解析ajax返回的xml和json格式的数据

    写个例子,以备后用 一.JavaScript 解析返回的xml格式的数据: 1.javascript版本的ajax发送请求 (1).创建XMLHttpRequest对象,这个对象就是ajax请求的核心 ...

  4. ASP.NET API(MVC) 对APP接口(Json格式)接收数据与返回数据的统一管理

    话不多说,直接进入主题. 需求:基于Http请求接收Json格式数据,返回Json格式的数据. 整理:对接收的数据与返回数据进行统一的封装整理,方便处理接收与返回数据,并对数据进行验证,通过C#的特性 ...

  5. $.ajax返回的JSON格式的数据后无法执行success的解决方法

    近段时间做项目,在项目使用了ajax技术,遇到了一个奇怪的问题:"$.ajax返回的JSON格式的数据无法执行success",代码是这样写的: 1 $.ajax({ 2 .. 3 ...

  6. Android Volley获取json格式的数据

    为了让Android能够快速地访问网络和解析通用的数据格式Google专门推出了Volley库,用于Android系统的网络传输.volley库可以方便地获取远程服务器的图片.字符串.json对象和j ...

  7. Android读写JSON格式的数据之JsonWriter和JsonReader

    近期的好几个月都没有搞Android编程了,逐渐的都忘却了一些东西.近期打算找一份Android的工作,要继续拾起曾经的东西.公司月初搬家之后就一直没有网络,直到今日公司才有网络接入,各部门才開始办公 ...

  8. 处理JSON格式的数据

    JSON格式的数据是最常用的数据格式,处理方法的选择就显得比较重要了.我常用的一种是用对象来接收,然后保存在数组中,需要时直接从数组中取值.下面列出一个小例子. .h文件中: #import < ...

  9. json格式的数据及遍历:

    代码: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8 ...

随机推荐

  1. Struts2数据校验方法

    方法: 1.在Action类中execute()方法中进行校验. 优点:Action类无需继承框架中的类. 缺点:(1)当有多个校验时,代码重复,违反高内聚,低耦合. 2.重写框架ActionSupp ...

  2. OWA修改密码注意事项

    Exchange搭建参考 http://yuelei.blog.51cto.com/202879/76302 http://543925535.blog.51cto.com/639838/d-37/p ...

  3. 实时监控MySql状态

    大多网站的性能瓶颈都会出在数据库上,所以想把Mysql监控起来,就搜索了下相关资料. 后来和同事讨论了下cacti和nagios有些老套和过时,graphite比较时尚,然后就搜了下相关的资料,最后搞 ...

  4. mssql 用户只能查看授权的数据库

    问题背景:公司的一台数据库服务器上放在多个数据库,每个数据库都使用不同的登录名称,但在将项目文件发布到Ftp时,有些Ftp的信息是在客户那边的 一旦客户那边使用配置文件中的数据库信息连接到数据库他就能 ...

  5. hdu 1171 Big Event in HDU(多重背包+二进制优化)

    题目链接:hdu1171 思路:将多重背包转为成完全背包和01背包问题,转化为01背包是用二进制思想,即件数amount用分解成若干个件数的集合,这里面数字可以组合成任意小于等于amount的件数 比 ...

  6. [Mac]Mac OS 10.11虚拟机搭建ReactNative遇坑记录

    1.命令行安装nvm,一定要加入/.bash_profile,加入以后需要执行source /.bash_profile,使nvm命令行立即生效 2.node一定要安装最新版本,不然执行npm ins ...

  7. 【转】android 自定义ViewPager,修改原动画

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38026503 记 得第一次见到ViewPager这个控件,瞬间爱不释手,做东西的 ...

  8. 对于 Javascript 闭包理解

    一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量 ...

  9. Expression<Func<T,TResult>>和Func<T,TResult> 与AOP与WCF

    1>>Expression<Func<T,TResult>>和Func<T,TResult>http://www.cnblogs.com/xcsn/p/ ...

  10. Android Launcher 怎样去掉主菜单,全部应用摆在桌面,相似小米桌面

    前言          欢迎大家我分享和推荐好用的代码段~~ 声明          欢迎转载,但请保留文章原始出处:          CSDN:http://www.csdn.net        ...