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.
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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
