mybatis处理数组类型及使⽤Json格式保存数据
JsonTypeHandlerandA。。。
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不会变。
接下来我们看⼀下 l 中的⽚段:
<resultMap type="st.userVO" id="userVO">
<result property="id" column="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<result property="emails" column="emails" typeHandler="batis.handler.ArrayTypeHandler"/>
<result property="address" column="address" typeHandler="batis.handler.JsonTypeHandler"/>
</resultMap>
上⾯的resultMap中配置了2个typeHandler,关于typeHandler的配置,mybatis有多种⽅法,这⾥简单⽰意⼀下。
再看l中的2个⽅法,需要使⽤到这2个handler
<insert id="addUser" parameterType="st.UserVO">
INSERT INTO user (
name,
age,
emails,
address)
VALUES (
#{name, jdbcType=VARCHAR},
#{age, jdbcType=INTEGER},
#{emails, jdbcType=ARRAY, typeHandler=batis.handler.ArrayTypeHandler},
#{address, jdbcType=VARCHAR, typeHandler=batis.handler.JsonTypeHandler})
</insert>
<select id="getUserById" resultMap="userVO">
SELECT *
FROM user
WHERE id = #{0}
</select>
上述的addUser⽅法,传⼊了字符串数组,和Object对象,保存到了数据库中,见下⾯的代码:
error parse newUserVO user = new UserVO();
user.setName("kylin");
user.setAge(30);
user.setEmails(new String[] { "kylin@163", "kylin@263" });
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","kylin@126"] {"contry":"china","province":"guangdong","city":"shenzhen"}
看到输⼊按期望的存⼊到了数据库中,稍后我们看从数据库读取出后是什么样⼦,我们先看看ArrayTypeHandler.java
mybatis 已经实现了 BaseTypeHandler<T> 这个抽象类,并将公共的逻辑实现好了,我们只需要继承BaseTypeHandler就好,只需要处理简单数据即可。
package batis.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.pe.BaseTypeHandler;
import org.pe.JdbcType;
import org.pe.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, String());
*/
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 " + Class().getName());
}
// 这3⾏是关键的代码,创建Array,然后ps.setArray(i, array)就可以了
Connection conn = ps.getConnection();
Array array = ateArrayOf(typeName, parameter);
ps.setArray(i, array);
}
@Override
public Object[] getNullableResult(ResultSet rs, String columnName)
throws SQLException {
return Array(columnName));
}
@Override
public Object[] getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return Array(columnIndex));
}
@Override
public Object[] getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
return Array(columnIndex));
}
private Object[] getArray(Array array) {
if (array == null) {
return null;
}
try {
return (Object[]) Array();
} catch (Exception e) {
}
return null;
}
}
JsonTypeHandler 我们需要⽤到处理Json的第三⽅包:jackson,这个包据说处理json是效率最快的,代价最⼩的。先封装⼀个JsonUtil,并提供JsonUtil.stringify(...) JsonUtil.parse(...) 这样2个⽅法出来
package st.util.json;
import java.io.OutputStream;
SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.apachemons.logging.Log;
import org.apachemons.logging.LogFactory;
dehaus.jackson.map.DeserializationConfig;
dehaus.jackson.map.ObjectMapper;
dehaus.jackson.map.SerializationConfig;
dehaus.jackson.map.annotate.JsonFilter;
dehaus.jackson.map.ser.impl.SimpleBeanPropertyFilter;
dehaus.jackson.map.ser.impl.SimpleFilterProvider;
import annotation.AnnotationUtils;
public class JsonUtil {
private static Log log = Log(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); figure(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) {
<(e.getMessage(), e);
}
return null;
}
public static String stringify(Object object, properties) {
try {
return objectMapper
.writer(new SimpleFilterProvider().addFilter(
AnnotationUtils.Class(), JsonFilter.class)).toString(), SimpleBeanPropertyFilter.filterOutAllExcept(properties)))
.writeValueAsString(object);
} catch (Exception e) {
<(e.getMessage(), e);
}
return null;
}
public static void stringify(OutputStream out, Object object) {
try {
objectMapper.writeValue(out, object);
} catch (Exception e) {
<(e.getMessage(), e);
}
}
public static void stringify(OutputStream out, Object object, properties) {
try {
objectMapper
.
writer(new SimpleFilterProvider().addFilter(
AnnotationUtils.Class(), JsonFilter.class)).toString(), SimpleBeanPropertyFilter.filterOutAllExcept(properties)))
.writeValue(out, object);
} catch (Exception e) {
<(e.getMessage(), e);
}
}
public static <T> T parse(String json, Class<T> clazz) {
if (json == null || json.length() == 0) {
return null;
}
try {
adValue(json, clazz);
} catch (Exception e) {
<(e.getMessage(), e);
}
return null;
}
}
接着再看看JsonTypeHandler
package batis.handler;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.pe.BaseTypeHandler;
import org.pe.JdbcType;
import st.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.String(columnName), Object.class);
}
@Override
public Object getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return JsonUtil.String(columnIndex), Object.class);
}
@Override
public Object getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
return JsonUtil.String(columnIndex), Object.class);
}
}
⾄此,JsonTypeHandler 和 ArrayTypeHandler 就分享介绍完了,
如前⾯的 resultMap的配置,当调⽤ getUserById⽅法时,会返回 String[], 和Map<String, Object>对象回来,有了这2个基础TypeHandler,接下来设计数据库和数据结构就会⽅便和灵活很多了。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论