我們知道,Java和MySQL中的數據類型是不同的,Java中除了基本數據類型,還有對象。
有時候使用MySQL存儲數據,或者從MySQL中讀取數據時,會有一些特殊需求 😩 ,比如:
- 將Integer數組直接存入MySQL,保存為BLOB形式,讀取出來時又是正常的Integer數組
- 將Integer數組轉換為String,然后存入MySQL,使用varchar類型,讀取出來時又是正常的Integer數組
這也太難了叭!
解決辦法有兩種:
- Basic Method:Java在存入數據之前,或讀取數據之后,做手動類型轉換
- Clever Method:定義TypeHandler,並在Mybatis對應位置指明
關於第一種方法這里不予贅述,不夠Smart。這里主要講述如何自定義Handler,來解決Java數據->MySQL數據的特殊類型轉換問題😀
這種Handler不僅方便了我們的數據庫操作,還有利於代碼的復用。
這里以Integer[]數組的存儲為形如,1,2,3,
的varchar字符串為例。
問題示例
我們定義一個role類,與數據庫的role表對應:
public class Role {
private Integer id;
private String name;
private Integer[] accessIds;
private Date createTime;
// ... ignore get and set methods
}
注意到里面有一個accessIds字段,它的類型是Integer[]
數據庫設計:
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`access_ids` varchar(255) DEFAULT NULL,
`create_time` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES ('1', '測試角色', ',1,2,', '2019-11-14 13:43:14');
自定義Handler類
通過繼承BaseTypeHandler類,重寫其方法,定義一個Integer[]與數據庫varchar類型自動轉換的Handler類:
/**
* Java Int數組與MySQL String轉換器
* 比如[1,2,3] --> ",1,2,3,"
*/
public class StringToIntArrayHandler extends BaseTypeHandler<Integer[]> {
private static final String splitCharset = ",";
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Integer[] objects, JdbcType jdbcType) throws SQLException {
String str = arrayToString(objects);
ps.setString(i, str);
}
@Override
public Integer[] getNullableResult(ResultSet rs, String columnName) throws SQLException {
String str = rs.getString(columnName);
return stringToArray(str);
}
@Override
public Integer[] getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
String str = rs.getString(columnIndex);
return stringToArray(str);
}
@Override
public Integer[] getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
String str = cs.getString(columnIndex);
return stringToArray(str);
}
// --- private methods ---
/**
* Integer數組轉String
* 注:使用提前設定好的分隔符分割數組的每一項
*/
private static String arrayToString(Integer[] array) {
StringBuilder res = new StringBuilder();
if (array != null && array.length > 0) {
for (Object o : array) {
res.append(splitCharset).append(o.toString());
}
res.append(splitCharset);
}
return res.length() > 0 ? res.toString() : null;
}
/**
* 從String轉Integer數組
* 注:String是用分隔符分割的,使用String.split方法可以分解為數組
*/
private static Integer[] stringToArray(String str) {
List<Integer> list = new ArrayList<>();
if (str != null) {
String[] array = str.split(splitCharset);
if (array.length > 0) {
for (String o : array) {
if (o != null && o.length() > 0) {
list.add(Integer.parseInt(o));
}
}
}
}
return list.toArray(new Integer[0]);
}
}
這個類的具體作用是什么呢?
- 當Java中類型是Integer[]時,使用這個Handler類,將Integer[]轉換為以
,
號分割的字符串,然后存入數據庫 - 當從數據庫讀取以
,
分割值的字符串時,可以通過這個Handler,自動將字符串轉換為Integer[]數組
下面我們演示一下具體的使用😄
在Mybatis中應用自定義的Handler
Mybatis存放SQL語句的XML文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.example.model.dao.RoleDAO">
<resultMap id="roleMap" type="com.example.model.bean.Role">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="accessIds" column="access_ids"
typeHandler="ccom.example.model.dao.handler.StringToIntArrayHandler"/>
<result property="createTime" column="create_time"/>
</resultMap>
<select id="findById" parameterType="map" resultMap="roleMap">
SELECT id, name, access_ids, create_time
FROM role
WHERE id = #{id}
</select>
<insert id="insert" parameterType="com.example.model.bean.Role">
<selectKey resultType="java.lang.Integer" order="AFTER" keyProperty="id">
SELECT LAST_INSERT_ID()
</selectKey>
INSERT INTO role
(name, create_time, access_ids)
VALUES
(#{name}, #{createTime}
, #{accessIds, jdbcType=VARCHAR, typeHandler=com.example.model.dao.handler.StringToIntArrayHandler})
</insert>
</mapper>
以上XML中演示了select和insert兩種情況時,如何應用typeHandler。