1. 问题描述

解决oracle时间戳转json报错问题。

2022-01-13 15:21:14.657 [XNIO-1 task-28] ERROR com.xxx.modules.system.aspect.DictAspect:93 - json解析失败No serializer found for class java.io.ByteArrayInputStream and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature. FAIL_ON_EMPTY_BEANS) (through reference chain: java.util.HashMap["FDATE"]->oracle.sql.TIMESTAMP["stream"]) com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class java.io.ByteArrayInputStream and no properties discovered to create BeanSerializer  (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: java.util.HashMap["FDATE"]->oracle.sql.TIMESTAMP["stream"])

2. 原因

oracle.sql.TIMESTAMP 类型无法直接转成json对象,需要先转成 oracle.sql.TIMESTAMP

3. 解决方法

解决思路:将oracle.sql.TIMESTAMP类型转成java.sql.TIMESTAMP类型,再转成json格式

解决方法:配置全局mybatis处理器,对oracle时间戳类型进行转换

MybatisTypeHandler

package com.xxx.modules.system.handler;

import com.xxx.common.util.DateUtils;
import com.xxx.common.util.dynamic.db.SqlUtils;
import org.apache.ibatis.type.*;

import java.sql.*;

/**
 * @Description: Mybatis全局类型处理器
 * @author: 
 * @date: 2022/1/13 16:14
 * @Version: V1.0
 */
@MappedTypes(value = { Object.class})
@MappedJdbcTypes(value = JdbcType.TIMESTAMP)
public class MybatisTypeHandler extends BaseTypeHandler<Object> {
    public MybatisTypeHandler() {
    }
    
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {
        ps.setObject(i, parameter);
    }

    @Override
    public Object getNullableResult(ResultSet rs, String columnName) throws SQLException {
        Object result = rs.getObject(columnName);
        return rs.wasNull() ? null : dealResult(result);
    }

    @Override
    public Object getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        Object result = rs.getObject(columnIndex);
        return rs.wasNull() ? null : dealResult(result);
    }

    @Override
    public Object getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        Object result = cs.getObject(columnIndex);
        return cs.wasNull() ? null : dealResult(result);
    }

    /**
     * 解决时间戳转换的错误:
     * 2022-01-13 15:21:14.657 [XNIO-1 task-28] ERROR com.xxx.modules.system.aspect.DictAspect:93 - json解析失败No serializer found for class java.io.ByteArrayInputStream and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.
     * FAIL_ON_EMPTY_BEANS) (through reference chain: java.util.HashMap["FDATE"]->oracle.sql.TIMESTAMP["stream"])
     * com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class java.io.ByteArrayInputStream and no properties discovered to create BeanSerializer
     * (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: java.util.HashMap["FDATE"]->oracle.sql.TIMESTAMP["stream"])
     * @param result
     * @return
     * @throws SQLException
     */
    private Object dealResult(Object result) throws SQLException {
        Object value = SqlUtils.convertOracleTimeToJavaTime(result);
        if (value instanceof Timestamp) {
            return DateUtils.formatDate(new java.sql.Date(((Timestamp) value).getTime()), "yyyy-MM-dd HH:mm:ss");
        }
        return value;
    }
}

SqlUtils

public class SqlUtils {
    public static Object convertOracleTimeToJavaTime(Object value) {
        if (value == null) {
            return null;
        }
        try {
            // 解决时间戳转换问题
            if (value.getClass().getName().equals("oracle.sql.TIMESTAMP")) {
                Class<?> clz = value.getClass();
                Method m = clz.getMethod("timestampValue");
                return m.invoke(value);
            }
        } catch (Exception e) {
            throw new JeecgBootException(e);
        }
        return value;
    }
}

DateUtils

/**
 * 指定日期按指定格式显示
 *
 * @param date    指定的日期
 * @param pattern 指定的格式
 * @return 指定日期按指定格式显示
 */
public static String formatDate(Date date, String pattern) {
    return getSDFormat(pattern).format(date);
}

// 指定模式的时间格式
private static SimpleDateFormat getSDFormat(String pattern) {
    return new SimpleDateFormat(pattern);
}

注意:全局处理器仅针对 TIMESTAMP 类型做处理即可,即使用注解 @MappedJdbcTypes(value = JdbcType.TIMESTAMP),以免 java.util.date 类型执行 set 方法时找不到 sql 对应的 date 类型(仅支持 java.sql.Dateoracle.sql.Date,不支持 java.util.date

修改配置文件

方法一

mybatis/mybatis-config.xml

<typeHandlers>
  <typeHandler handler="com.desay.modules.system.handler.MybatisTimestampHandler"></typeHandler>
</typeHandlers>

Nacos配置

mybatis-plus:
  config-location: classpath:mybatis/mybatis-config.xml

方法二(推荐)

如果方法一没有效果,直接配置nacos扫描handler包即可

mybatis-plus:
	type-handlers-package: com.desay.modules.system.handler

参考:

https://www.cnblogs.com/hellowhy/p/9670635.html

https://blog.csdn.net/weixin_33747129/article/details/88008127

温馨提示

尽量不要使用自定义全局mybatis处理器,以免对其他地方造成影响,可以简单的对查询的字段进行遍历,然后针对时间戳字段进行转换(上面的 SqlUtils.convertOracleTimeToJavaTime 方法),一般查询数据都是分页的,数量不会很大,因此可以忽略遍历的时间复杂度。