Mybatis自定义TypeHandler实现JavaBean-
2019-12-26 本文已影响0人
小孩孜
首先是JsonUtils - String-Object互转工具类
JsonUtils.java
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 获取ObjectMapper对象
*
* @author xihc
* @version 1.0
* @date 2018年04月21日
*/
public class JsonUtils {
private static ObjectMapper mapper = new ObjectMapper();
private static Logger logger = LoggerFactory.getLogger(JsonUtils.class);
public static ObjectMapper getObjectMapper() {
// mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"));
// 允许没有双引号
mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
// 设置忽略没有的字段
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return mapper;
}
/**
* 每次获取一个新的ObjectMapper,可以自定义解析配置
*
* @return
*/
public static ObjectMapper newObjectMapper() {
return mapper.copy();
}
/**
* 将JsonString转为对象
*
* @param content
* @param valueType
* @return
*/
public static <T> T readValue(String content, Class<T> valueType) {
if (null == content || "".equals(content)) {
return null;
}
try {
return getObjectMapper().readValue(content, valueType);
} catch (Exception e) {
logger.error("JsonUtil.readValue()", e);
return null;
}
}
/**
* 将JsonString转为对象
*
* @param content
* @param valueTypeRef
* @return
*/
public static <T> T readValue(String content, TypeReference<T> valueTypeRef) {
if (null == content || "".equals(content)) {
return null;
}
try {
return getObjectMapper().readValue(content, valueTypeRef);
} catch (Exception e) {
logger.error("JsonUtil.readValue()", e);
return null;
}
}
/**
* 将map转为指定对象
*
* @param obj
* @param valueTypeRef
* @return
*/
public static <T> T convertValue(Object obj, TypeReference<T> valueTypeRef) {
if (null == obj) {
return null;
}
try {
return getObjectMapper().convertValue(obj, valueTypeRef);
} catch (Exception e) {
logger.error("JsonUtil.convertValue()", e);
return null;
}
}
/**
* 将map转为指定对象
*
* @param obj
* @param clazz
* @return
*/
public static <T> T convertValue(Object obj, Class<T> clazz) {
if (null == obj) {
return null;
}
try {
return getObjectMapper().convertValue(obj, clazz);
} catch (Exception e) {
logger.error("JsonUtil.convertValue()", e);
return null;
}
}
/**
* 将对象转为JsonString
*
* @param obj
* @return
*/
public static String toJsonString(Object obj) {
if (null == obj) {
return null;
}
try {
return getObjectMapper().writeValueAsString(obj);
} catch (Exception e) {
logger.error("JsonUtil.toJsonString()", e);
return null;
}
}
/**
* 将对象转为JsonString,不包含为null的字段
*
* @param obj
* @return
* @since 1.18
*/
public static String toNoNullJsonStr(Object obj) {
if (null == obj) {
return null;
}
try {
ObjectMapper objectMapper = newObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
String jsonStr = objectMapper.writeValueAsString(obj);
return jsonStr;
} catch (Exception e) {
logger.error("JsonUtil.toNoNullJsonStr()", e);
return null;
}
}
}
Mybatis自定义转换器 - JsonTypeHandler
JsonTypeHandler.java
import com.dreawer.sic.util.JsonUtils;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 数据库字段string - Java对象互转handler
*
* @author Leo.Xi
* @date 2019/12/18
* @since 1.0
**/
@MappedJdbcTypes({JdbcType.VARCHAR, JdbcType.CHAR, JdbcType.NCHAR})// 数据库字符串类型
public class JsonTypeHandler<T extends Object> extends BaseTypeHandler<T> {
private Class<T> clazz;
public JsonTypeHandler(Class<T> clazz) {
if (clazz == null) {
throw new IllegalArgumentException("Type argument cannot be null");
}
this.clazz = clazz;
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, JsonUtils.toNoNullJsonStr(parameter));
}
@Override
public T getNullableResult(ResultSet rs, String columnName) throws SQLException {
return JsonUtils.readValue(rs.getString(columnName), clazz);
}
@Override
public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return JsonUtils.readValue(rs.getString(columnIndex), clazz);
}
@Override
public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return JsonUtils.readValue(cs.getString(columnIndex), clazz);
}
}
JsonListTypeHandler.java
import com.dreawer.sic.util.JsonUtils;
import com.fasterxml.jackson.core.type.TypeReference;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List;
/**
* 数据库字段string - Java对象互转handler
*
* @author Leo.Xi
* @date 2019/12/18
* @since 1.0
**/
@MappedJdbcTypes({JdbcType.VARCHAR, JdbcType.CHAR, JdbcType.NCHAR, JdbcType.LONGVARCHAR})
public class JsonListTypeHandler<T extends Object> extends BaseTypeHandler<List<T>> {
private Class<? extends T> clazz;
public JsonListTypeHandler(Class<? extends T> clazz) {
if (clazz == null) {
throw new IllegalArgumentException("Type argument cannot be null");
}
this.clazz = clazz;
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, List<T> parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, JsonUtils.toNoNullJsonStr(parameter));
}
@Override
public List<T> getNullableResult(ResultSet rs, String columnName) throws SQLException {
List<T> ts = JsonUtils.readValue(rs.getString(columnName), new TypeReference<List<T>>() {
});
List<T> result = new LinkedList<>();
if (ts != null) {
ts.forEach(x -> {
T t = JsonUtils.convertValue(x, clazz);
result.add(t);
});
return result;
}
return null;
}
@Override
public List<T> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
List<T> ts = JsonUtils.readValue(rs.getString(columnIndex), new TypeReference<List<T>>() {
});
List<T> result = new LinkedList<>();
if (ts != null) {
ts.forEach(x -> {
T t = JsonUtils.convertValue(x, clazz);
result.add(t);
});
return result;
}
return null;
}
@Override
public List<T> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
List<T> ts = JsonUtils.readValue(cs.getString(columnIndex), new TypeReference<List<T>>() {
});
List<T> result = new LinkedList<>();
if (ts != null) {
ts.forEach(x -> {
T t = JsonUtils.convertValue(x, clazz);
result.add(t);
});
return result;
}
return null;
}
}
最后就是SpringBoot的使用了 - MybatisConfig
直接上代码:
MybatisConfig.java
import com.dreawer.sic.dao.JsonListTypeHandler;
import com.dreawer.sic.dao.JsonTypeHandler;
import com.dreawer.sic.dao.domain.Demo;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.boot.autoconfigure.MybatisProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.sql.DataSource;
/**
* MybatisConfig - 配置类
*
* @author Leo.Xi
* @date 2019/12/19
* @since 1.0
**/
@Configuration
public class MybatisConfig {
/** 需要使用JsonTypeHandle转换的 单个 - 需要自行添加 */
private static Class[] JSON_TYPE_HANDLER_ONE_CLASSES = new Class[]{Demo.class};
/** 需要使用JsonListTypeHandle转换的 List - 需要自行添加 */
private static Class[] JSON_TYPE_HANDLER_LIST_CLASSES = new Class[]{Demo.class};
@Autowired
private MybatisProperties mybatisProperties;
/**
* 自定义sqlSession 配置
*
* @param dataSource 数据源
* @return SqlSessionFactory
* @author Leo.Xi
* @date 2019/12/19
* @since 0.0.1
*/
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
for (Class clazz : JSON_TYPE_HANDLER_ONE_CLASSES) {
configuration.getTypeHandlerRegistry().register(clazz, JsonTypeHandler.class);
}
for (Class clazz : JSON_TYPE_HANDLER_LIST_CLASSES) {
configuration.getTypeHandlerRegistry().register(new JsonListTypeHandler<>(clazz));
}
// 控制台打印sql
configuration.setLogImpl(org.apache.ibatis.logging.stdout.StdOutImpl.class);
sessionFactory.setConfiguration(configuration);
sessionFactory.setTypeAliasesPackage(mybatisProperties.getTypeAliasesPackage());
String[] mapperLocations = mybatisProperties.getMapperLocations();
for (String mapperLocation : mapperLocations) {
sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocation));
}
return sessionFactory.getObject();
}
}
此处重点说一下:
/** 需要使用JsonTypeHandle转换的 - 需要自行添加 */
private static Class[] JSON_TYPE_HANDLER_CLASSES = new Class[]{List.class, ArrayList.class, Demo.class
};
其中Demo.class是我们需要转成数据库varchar类型的对象类型。
至于前面的List.class、ArrayList.class如果你的对象中需要转换的是数组、列表的都需要加一下的。
剩余源码:
User.java
import com.dreawer.domain.BaseDomain;
import lombok.Getter;
import lombok.Setter;
/**
* <CODE>User</CODE> 用户实体类。
* @author xihc
* @version 0.0.1
*/
@Getter
@Setter
public class User {
private String id = null; // ID
private String storeId = null; // 店铺ID
private String name = null; // 名称
private String phone = null; // 手机号
private Demo demo;// 测试demo
}
Demo.java
import lombok.Data;
/**
* demo示例
*
* @author Leo.Xi
* @date 2019/12/18
* @since 1.0
**/
@Data
public class Demo {
private String name;
}
UserDao.java
import java.util.List;
import org.springframework.stereotype.Repository;
import com.dreawer.persistence.mybatis.MyBatisBaseDao;
import com.dreawer.sic.dao.domain.User;
/**
* <CODE>UserDao</CODE> 用户信息 DAO 类,负责对用户数据进行访问和操作。
* @author xihc
* @version 0.0.1
*/
@Repository
public class UserDao extends MyBatisBaseDao<User> {
/**
* 添加用户信息。
* @param user 待添加的用户信息。
* @return 添加成功记录数:结果为1则添加成功,为0则添加失败。
* @author kael
* @since 0.0.1
*/
public int add(User user) {
//返回添加结果
return insert("add", user);
}
/**
* 删除用户信息。
* @param id 待删除的用户ID。
* @return 删除成功记录数:结果为1则删除成功,为0则删除失败。
* @author kael
* @since 0.0.1
*/
public int delete(String id) {
//返回删除结果
return delete("delete", id);
}
/**
* 更新用户信息。
* @param user 待更新的用户信息。
* @return 更新成功记录数:结果为1则删除成功,为0则删除失败。
* @author kael
* @since 0.0.1
*/
public int update(User user) {
//返回更新结果
return update("update", user);
}
/**
* 查询用户信息。
* @param id 用户ID。
* @return 查询结果:查询到结果返回用户信息,未查询到结果则返回NULL。
* @author kael
* @since 0.0.1
*/
public User findUser(String id) {
//返回查询结果
return selectOne("findUser", id);
}
/**
* 查询用户信息列表。
* @param storeId 店铺ID。
* @return 查询结果:查询到结果返回用户信息列表,未查询到结果则返回NULL。
* @author kael
* @since 0.0.1
*/
public List<User> findUsers(String storeId) {
//返回查询结果
return selectList("findUsers", storeId);
}
/**
* 获取用户总数。
* @param storeId 店铺ID。
* @return 查询结果:查询到结果用户总数,未查询到结果则返回0。
* @author kael
* @since 1.0
*/
public int getUserCount(String storeId) {
//返回查询结果
return count("getUserCount", storeId);
}
}
MyBatisBaseDao<T>
不用在意,没有的改成原生的接口也是可行的。
UserDao.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.xxx.xxx.User">
<!-- ================================================================================== -->
<!-- SQL新增 -->
<!-- ================================================================================== -->
<!-- 新增用户 -->
<insert id="add" parameterType="User">
INSERT INTO user(id, sto_id, name, phone,demo)
VALUES(#{id}, #{storeId}, #{name}, #{phone}, #{demo})
</insert>
<!-- ================================================================================== -->
<!-- SQL删除 -->
<!-- ================================================================================== -->
<!-- 删除用户 -->
<insert id="delete" parameterType="String">
DELETE FROM user WHERE id = #{id}
</insert>
<!-- ================================================================================== -->
<!-- SQL更新 -->
<!-- ================================================================================== -->
<!-- 删除用户 -->
<update id="update" parameterType="User">
UPDATE user
SET name = #{name}, phone = #{phone}
WHERE id = #{id}
</update>
<!-- ================================================================================== -->
<!-- SQL查询 -->
<!-- ================================================================================== -->
<!-- 根据用户ID查询用户信息 -->
<select id="findUser" parameterType="String" resultMap="userResultMap">
SELECT <include refid="basicFields"/>
FROM user
WHERE id = #{id}
</select>
<!-- 根据店铺ID查询用户列表 -->
<select id="findUsers" parameterType="String" resultMap="userResultMap">
SELECT <include refid="basicFields"/>
FROM user
WHERE sto_id = #{storeId}
</select>
<!-- 根据店铺ID获取用户总数 -->
<select id="getUserCount" parameterType="String" resultType="Integer">
SELECT COUNT(*)
FROM user
WHERE sto_id = #{storeId}
</select>
<!-- ================================================================================== -->
<!-- 公用SQL定义 -->
<!-- ================================================================================== -->
<!-- 基本信息 -->
<sql id="basicFields">
id, sto_id, name, phone,demo
</sql>
<!-- ================================================================================== -->
<!-- 结果集映射 -->
<!-- ================================================================================== -->
<resultMap id="userResultMap" type="User">
<result property="id" column="id" />
<result property="storeId" column="sto_id" />
<result property="name" column="name" />
<result property="phone" column="phone" />
<result property="demo" column="demo" />
</resultMap>
</mapper>
SQL
-- ==================================================
-- 用户信息表 - 测试用
-- ==================================================
DROP TABLE IF EXISTS user;
CREATE TABLE user (
id CHAR(32) NOT NULL COMMENT '用户ID',
sto_id CHAR(32) NOT NULL COMMENT '店铺ID',
name VARCHAR(20) NOT NULL COMMENT '姓名',
phone VARCHAR(11) NOT NULL COMMENT '手机号',
PRIMARY KEY (id)
) COMMENT='用户信息表' ENGINE=InnoDB;
ALTER TABLE user
ADD COLUMN demo VARCHAR(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'demo对象' AFTER phone;
测试结果截图:
测试结果