Bootstrap

Mybatis——数据库json字段映射实体类

Mybatis——数据库json字段映射实体类

场景:数据库varchar字段存放json格式字符串数据,Mybatis查询解析json数据映射到实体类属性变量。

相关依赖:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.74</version>
</dependency>
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>4.6.10</version>
</dependency>

Json解析工具类:

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;

import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.TypeUtil;

/**
 * @Title: JsonUtil
 * @Description: Json工具类,暂时内部使用fastjson实现
 * @version V1.0
 */
public class JsonUtil {
    private JsonUtil() {}

    public static String obj2json(Object obj) {
        return obj2json(obj, DateUtil.DATE_TIME_PATTERN);
    }

    /**
     * 
     * @Description: 
     * @param obj
     * @param beIgnoreNull 是否忽略null值
     * @param beFormat  是否格式化
     * @param dataFormat  指定日期格式化 
     * @return
     */
    public static String obj2json2(Object obj, boolean beIgnoreNull, boolean beFormat, String dataFormat) {
        List<SerializerFeature> features = new ArrayList<>();
        if (!beIgnoreNull) {
            features.add(SerializerFeature.WriteMapNullValue);
            features.add(SerializerFeature.DisableCircularReferenceDetect);
            features.add(SerializerFeature.WriteNullStringAsEmpty);
            features.add(SerializerFeature.WriteNullListAsEmpty);
            features.add(SerializerFeature.WriteDateUseDateFormat);
        }
        if (beFormat) {
            features.add(SerializerFeature.PrettyFormat);
        }
        features.add(SerializerFeature.WriteDateUseDateFormat);
        return obj2json2(obj, dataFormat, features.toArray(new SerializerFeature[] {}));
    }

    public static String obj2json2(Object obj, boolean beIgnoreNull, boolean beFormat) {
        return obj2json2(obj, beIgnoreNull, beFormat, DateUtil.DATE_TIME_PATTERN);
    }

    public static String obj2json2(Object obj, boolean beIgnoreNull) {
        return obj2json2(obj, beIgnoreNull, false);
    }

    public static String obj2json2(Object obj) {
        return obj2json2(obj, false);
    }

    /**
     * 
     * @Description: 对象转json
     * @param obj
     *            对象
     * @param beFormat
     *            是否格式化
     * @return json字符串
     */
    public static String obj2json(Object obj, boolean beFormat) {
        return obj2json2(obj, false, beFormat);
    }

    /**
    * 
    * @Description: 对象转json
    * @param obj
    * @param dataFormat 日期格式
    * @return
    * 
    * 如果java bean的Date类型属性需要特殊处理,使用注解
    * @com.alibaba.fastjson.annotation.JSONField
    */
    public static String obj2json(Object obj, String dataFormat) {
        return obj2json2(obj, false, false, dataFormat);
    }

    private static String obj2json2(Object obj, String dataFormat, SerializerFeature... feature) {
        String defaultFormat = JSONObject.DEFFAULT_DATE_FORMAT;
        JSONObject.DEFFAULT_DATE_FORMAT = dataFormat;
        String json = JSON.toJSONString(obj, feature);
        JSONObject.DEFFAULT_DATE_FORMAT = defaultFormat;
        return json;
    }

    /**
     * 
     * @Description: xml转json
     * @param xml
     * @return json字符串
     */
    public static String xml2json(String xml) {
        return cn.hutool.json.JSONUtil.xmlToJson(xml).toJSONString(0);
    }

    /**
     * 
     * @Description: json转对象
     * @param jsonString
     * @param beanClass
     * @return 实体类对象
     */
    public static <T> T json2Obj(String jsonString, Class<T> beanClass) {
        return JSON.parseObject(jsonString, beanClass);
    }

    /**
     * 
     * @Description JSON字符串转为实体类对象,转换异常将被抛出
     *
     * @param <T>
     *            Bean类型
     * @param jsonString
     *            JSON字符串
     * @param typeReference
     *            {@link TypeReference}类型参考子类,可以获取其泛型参数中的Type类型
     * @param ignoreError
     *            是否忽略错误
     * @return 实体类对象
     */
    public static <T> T json2Obj(String jsonString, com.sf.nwms.core.util.lang.TypeReference<T> typeReference,
        boolean ignoreError) {
        /* return cn.hutool.json.JSONUtil.toBean(jsonString, TypeUtil.getTypeArgument(typeReference.getClass()),
            ignoreError);*/
        return json2Obj(jsonString, typeReference);
    }

    /**
     * 
     * @Description: 
     * @param <T>
     * @param jsonString
     * @param typeReference
     * @return
     * 
     * eg: JsonUtil.json2Obj(jsonString, new com.sf.nwms.core.util.lang.TypeReference<List<JavaBean>())
     */
    public static <T> T json2Obj(String jsonString, com.sf.nwms.core.util.lang.TypeReference<T> typeReference) {
        return JSON.parseObject(jsonString, TypeUtil.getTypeArgument(typeReference.getClass()));
    }

    /**
     * 
     * @Description: json转对象
     * @param jsonString
     *            json字符串
     * @param beanType
     *            实体类对象类型
     * @param ignoreError
     *            是否忽略错误
     * @return 实体类对象
     */
    public static <T> T json2Obj(String jsonString, Type beanType, boolean ignoreError) {
        return JSON.parseObject(jsonString, beanType);
    }

}

使用到日期工具类:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.StringUtils;

/**     
 * @Title: DateUtils   
 * @Description: 日期工具类       
 */
public class DateUtil {

    /** 分 */
    public static final long MINUTE_TTL = 60 * 1000L;
    /** 时 */
    public static final long HOURS_TTL = 60 * 60 * 1000L;
    /** 半天 */
    public static final long HALF_DAY_TTL = 12 * 60 * 60 * 1000L;
    /** 天 */
    public static final long DAY_TTL = 24 * 60 * 60 * 1000L;
    /** 月 */
    public static final long MONTH_TTL = 30 * 24 * 60 * 60 * 1000L;
    /** 时间格式(yyyy-MM-dd) */
    public final static String DATE_PATTERN = "yyyy-MM-dd";
    /** 时间格式(yyyy-MM-dd'T'HH:mm:ss.SSSZ) */
    public final static String DATE_TIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ssZ";

    private static ThreadLocal<SimpleDateFormat> simpleDateFormatThreadLocal = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat(DATE_TIME_PATTERN);
        }
    };

    public static Long getSystemTimeSeconds() {
        return System.currentTimeMillis() / 1000L;
    }

    /**
     * @Description:   将时间戳转换为时间
     * 输入形如:148851067484755 输出形如:2017-03-03T11:11:14.000+0800
     * 异常格式返回-1
     * @param s
     * @return
     */
    public static String getTimestampDateTimeLong(Long s) {
        String res;
        Date date = new Date(s * 1000);
        res = simpleDateFormatThreadLocal.get().format(date);
        return res;
    }

    /**
     * 
     * @Description: 将时间戳转换为时间
     * @param s 毫秒数
     * @return
     */
    public static String getTimestampDateLong(Long s) {
        return simpleDateFormatThreadLocal.get().format(new Date(s));
    }

    /**
     * 
     * @Description: 日期格式化 日期格式为:yyyy-MM-dd
     * @param date  日期
     * @return 返回yyyy-MM-dd格式日期
     */
    public static String formatShort(Date date) {
        return format(date, DATE_PATTERN);
    }

    /**
     * 
     * @Description: 日期格式化 日期格式为:yyyy-MM-dd'T'HH:mm:ss.SSSZ
     * @param date 日期
     * @return 返回
     */
    public static String formatLong(Date date) {
        return format(date, DATE_TIME_PATTERN);
    }

    /**
    * 
    * @Description: 格式化时间
    * @param date
    * @param pattern
    * @return
    */
    public static String format(Date date, String pattern) {
        if (date != null) {
            SimpleDateFormat df = null;
            if (DATE_TIME_PATTERN.equals(pattern)) {
                df = simpleDateFormatThreadLocal.get();
            } else {
                df = new SimpleDateFormat(pattern);
            }
            return df.format(date);
        }
        return null;
    }

    /**
     * 
     * @Description: 字符串转时间
     * @param strDate
     * @return
     * @throws ParseException
     */
    public static Date stringLongToDate(String strDate) throws ParseException {
        return stringToDate(strDate, DATE_TIME_PATTERN);
    }

    public static Date stringShortToDate(String strDate) throws ParseException {
        return stringToDate(strDate, DATE_PATTERN);
    }

    /**
     * 字符串转换成日期
     * @param strDate 日期字符串
     * @param pattern 日期的格式
     * @throws ParseException 
     */
    public static Date stringToDate(String strDate, String pattern) throws ParseException {
        if (StringUtils.isBlank(strDate)) {
            return null;
        }
        SimpleDateFormat df = null;
        if (DATE_TIME_PATTERN.equals(pattern)) {
            df = simpleDateFormatThreadLocal.get();
        } else {
            df = new SimpleDateFormat(pattern);
        }
        return df.parse(strDate);
    }

    /**
     * @Description: 日期计算
     * @param oldDate
     * @param addValue
     * @param unit
     * @return
     */
    public static Date add(Date oldDate, Long addValue, TimeUnit unit) {
        long milliseconds = oldDate.getTime();
        milliseconds += unit.toMillis(addValue);
        return new Date(milliseconds);
    }

}

mysql存储json的字段类型转换抽象类:

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import com.alibaba.fastjson.JSON;

/**     
 * @Title: AbstractObjectTypeHandler   
 * @Description: mysql存储json的字段类型转换抽象类 
 */
// @Slf4j
// @MappedJdbcTypes(JdbcType.VARCHAR)
public abstract class AbstractObjectTypeHandler<T> extends BaseTypeHandler<T> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
        if (null != parameter) {
            ps.setString(i, JsonUtil.obj2json(parameter));
        }
    }

    @Override
    public T getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return deserialize(rs.getString(columnName));
    }

    @Override
    public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return deserialize(rs.getString(columnIndex));
    }

    @Override
    public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return deserialize(cs.getString(columnIndex));
    }

    /**    
     * @Description: json反序列化
     * @param data
     * @return          
     */
    private T deserialize(String data) {
        return StringUtils.isBlank(data) ? null : string2Obj(data);
    }

    // 重点,解析方法,子类可重写json解析
    @SuppressWarnings("unchecked")
    protected T string2Obj(String jsonString) {
        Class<T> clazz = (Class<T>)getRawType();
        return JSON.parseObject(jsonString, clazz);
    }
}

解析实体

数据库表test:

字段名类别参考值
idbigint1
json_valuevarchar{name:“张三”,age:14}

正常Mybatis映射实体类:

@Data
public class Test{
    private Long id;
    private String jsonValue;
}

Json内容实体类:

@Data
public class Person{
    private String name;
    private int age;
}

Mybatis映射实体类时,解析json字段数据到变量,改造Test实体类:

@Data
public class Test{
    private Long id;
    
    @ColumnType(jdbcType = JdbcType.VARCHAR, typeHandler = PersonTypeHandler.class)
    private Person jsonValue;
}

添加Json解析转换器:

import com.alibaba.fastjson.JSON;

/**     
 * @Description: Person类型转换器       
 */
public class PersonTypeHandler extends AbstractObjectTypeHandler<Person> {}

Mybatis xml映射

<resultMap type="com.test.model.Test" id="TestMap">
    <result property="id" column="id" jdbcType="INTEGER"/>
    <result typeHandler="com.test.typehandler.PersonTypeHandler" property="jsonValue" column="json_value" jdbcType="VARCHAR"/>
</resultMap>

查询test数据时,使用以上resultMap接收,在映射实体类时自动解析json字段。

解析List

数据库表test:

字段名类别参考值
idbigint1
json_list_valuevarchar[{name:“张三”,age:14},{name:“李四”,age:15}]

正常Mybatis映射实体类:

@Data
public class Test{
    private Long id;
    private String jsonListValue;
}

Json内容实体类:

@Data
public class Person{
    private String name;
    private int age;
}

Mybatis映射实体类时,解析json字段数据到变量,改造Test实体类:

@Data
public class Test{
    private Long id;
    
    // 映射成List<Person>
    @ColumnType(jdbcType = JdbcType.VARCHAR, typeHandler = ListPersonTypeHandler.class)
    private List<Person> jsonValue;
}

添加Json解析转换器:

import com.alibaba.fastjson.JSON;

/**     
 * @Description: List<Person>类型转换器       
 */
public class ListPersonTypeHandler extends AbstractObjectTypeHandler<List<Person>> {
    @Override
    protected List<Person> string2Obj(String jsonString) {
        return JSON.parseArray(jsonString, Person.class);
    }
}

Mybatis xml映射

<resultMap type="com.test.model.Test" id="TestMap">
    <result property="id" column="id" jdbcType="INTEGER"/>
    <result typeHandler="com.test.typehandler.ListPersonTypeHandler" property="jsonListValue" column="json_list_value" jdbcType="VARCHAR"/>
</resultMap>

查询test数据时,使用以上resultMap接收,在映射实体类时自动解析json集合字段。

;