Handling Character Encoding and JSON Mapping in a Java Backend with Servlet Filters and MyBatis

This article explains how to configure UTF-8 encoding in Gradle, capture request/response bodies in a servlet filter for logging, transform database fields into JSON using a custom MyBatis type handler, and define the corresponding resultMap and SQL query for backend services.

FunTester
FunTester
FunTester
Handling Character Encoding and JSON Mapping in a Java Backend with Servlet Filters and MyBatis

Character Encoding

Due to time pressure the author relied on log analysis and basic Java knowledge to solve a character‑encoding issue, ensuring that both the build configuration and the servlet filter use UTF‑8.

In build.gradle the encoding is set as follows:

tasks.withType(JavaCompile) {

    options.encoding = "UTF-8"

}

The servlet filter reads the request and response streams, logs them, and then writes the response back using the same UTF‑8 charset to avoid garbled output:

@Override
public void doFilter(ServletRequest request, ServletResponse response,
                     FilterChain chain) throws IOException, ServletException {
    HttpServletRequest req = (HttpServletRequest) request;
    HttpServletResponse resp = (HttpServletResponse) response;
    ResponseWrapper responseWrapper = new ResponseWrapper(resp);
    RequestWrapper requestWrapper = new RequestWrapper(req);
    String url = requestWrapper.getRequestURI();
    String queryArgs = requestWrapper.getQueryString();
    queryArgs = queryArgs == null ? requestWrapper.getBody() : queryArgs;
    long start = Time.getTimeStamp();
    chain.doFilter(requestWrapper == null ? request : requestWrapper, responseWrapper);
    long end = Time.getTimeStamp();
    byte[] bytes = responseWrapper.getContent();
    String respContent = new String(bytes, Constant.UTF_8);
    logger.info("请求:{},耗时:{}ms,参数:{},响应:{}", url, end - start, queryArgs, respContent);
    response.getOutputStream().write(respContent.getBytes(Constant.UTF_8));
}

Database Fields Combined into JSON

The backend needs to return four status‑count fields from the database as a JSON array. Instead of post‑processing, a custom MyBatis typeHandler is used to convert the semicolon‑separated string into a JSON array.

typehandler

package com.okay.family.common.typehandler;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.okay.family.fun.frame.SourceCode;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;

/**
 * Convert the map result stored in the database into JSON format
 */
@MappedTypes(JSONObject.class)
@MappedJdbcTypes(JdbcType.VARCHAR)
public class ResultArrayHandler extends BaseTypeHandler<JSONArray> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, JSONArray parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, String.valueOf(parameter.toJSONString()));
    }

    @Override
    public JSONArray getNullableResult(ResultSet rs, String columnName) throws SQLException {
        String sqlJson = rs.getString(columnName);
        if (null != sqlJson) {
            JSONArray result = new JSONArray();
            List<String> asList = Arrays.asList(sqlJson.split(";"));
            asList.forEach(x -> {
                String[] split = x.split("=", 2);
                result.add(SourceCode.getJson("text=" + split[0], "amount=" + split[1]));
            });
        }
        return null;
    }

    @Override
    public JSONArray getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        String sqlJson = rs.getString(columnIndex);
        if (null != sqlJson) {
            JSONArray result = new JSONArray();
            List<String> asList = Arrays.asList(sqlJson.split(";"));
            asList.forEach(x -> {
                String[] split = x.split("=", 2);
                result.add(SourceCode.getJson("text=" + split[0], "amount=" + split[1]));
            });
        }
        return null;
    }

    @Override
    public JSONArray getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        String sqlJson = cs.getString(columnIndex);
        if (null != sqlJson) {
            JSONArray result = new JSONArray();
            List<String> asList = Arrays.asList(sqlJson.split(";"));
            asList.forEach(x -> {
                String[] split = x.split("=", 2);
                result.add(SourceCode.getJson("text=" + split[0], "amount=" + split[1]));
            });
        }
        return null;
    }
}

Mapper Configuration

The MyBatis resultMap references the custom type handler to map the concatenated status string to the JSON list field.

<resultMap type="com.okay.family.common.bean.casecollect.response.CollectionRunResultDetailBean" id="CollectionRunResultDetailBean">
    <result property="id" column="id"/>
    <result property="name" column="name"/>
    <result property="envName" column="envName"/>
    <result property="runId" column="runId"/>
    <result property="caseNum" column="caseNum"/>
    <result property="result" column="result"/>
    <result property="startTime" column="start"/>
    <result property="endTime" column="end"/>
    <result property="list" column="list" typeHandler="com.okay.family.common.typehandler.ResultArrayHandler"/>
</resultMap>

The corresponding SQL statement concatenates the four status counts into a single string that the type handler later splits:

<select id="getCollectionRunDetail" parameterType="java.lang.Integer" resultMap="CollectionRunResultDetailBean">
    select r.collectionId id, t.name, s.name envName, r.runId, r.caseNum, c.name result, start, end,
    concat("成功=", r.success, ";失败=", r.fail, ";无法运行=", r.unrun, ";用户错误=", r.userError) list
    from ...
    where runId = #{0}
</select>

The article concludes with a reference to the original WeChat public account "FunTester" where more original technical articles are published.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaencodingMyBatisServlet
FunTester
Written by

FunTester

10k followers, 1k articles | completely useless

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.