mybatis 处理数组类型及使用Json格式保存数据 JsonTypeHandler and ArrayTypeHandler

来源:互联网 发布:计算机处理数据的方式 编辑:程序博客网 时间:2024/05/16 14:48

mybatis 处理数组类型及使用Json格式保存数据 JsonTypeHandler and ArrayTypeHandler

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,接下来设计数据库和数据结构就会方便和灵活很多了。

0 0