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[] { "[email protected]", "[email protected]" }); Mapaddress = new HashMap (); 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 ["[email protected]","[email protected]"] {"contry":"china","province":"guangdong","city":"shenzhen"}
看到输入按期望的存入到了数据库中,稍后我们看从数据库读取出后是什么样子,我们先看看ArrayTypeHandler.java
mybatis 已经实现了 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;
// 继承自BaseTypeHandlerpublic class ArrayTypeHandler extends BaseTypeHandler
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 staticT parse(String json, Class 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;
// 继承自BaseTypeHandlerpublic class JsonTypeHandler extends BaseTypeHandler
至此,JsonTypeHandler 和 ArrayTypeHandler 就分享介绍完了,
如前面的 resultMap的配置,当调用 getUserById方法时,会返回 String[], 和Map
有了这2个基础TypeHandler,接下来设计数据库和数据结构就会方便和灵活很多了。