Implementing Streaming Reads with MyBatis for Large-Scale Java Report Export
This article explains how to overcome export failures caused by large data volumes in a legacy Java system by switching from default full-result JDBC reads to a forward‑only streaming approach using MyBatis, detailing environment setup, configuration changes, and complete code examples for controller, service, DAO, and mapper layers.
When the company's legacy reporting module failed to export datasets larger than ten thousand rows, analysis revealed that the default JDBC fetch retrieved all rows at once, causing excessive memory usage and GC pressure. The solution was to adopt a forward‑only streaming read, which fetches rows incrementally.
JDBC three read modes:
1. Full fetch (default) – retrieves the entire result set in one go.
2. Streaming – fetches one row at a time.
3. Cursor – fetches multiple rows per round.
MyBatis uses the first mode by default.
Development environment: JDK 1.8, IntelliJ IDEA 2018, MyBatis 3, Spring MVC, Spring 4.
Implementation steps: The project adopts a layered architecture (controller → service → DAO) and modifies the service method to accept a ResultHandler callback, while the DAO method returns void. The mapper XML is configured with resultSetType="FORWARD_ONLY" and fetchSize="-2147483648" to enable streaming.
Controller layer:
@RequestMapping("/export")
public void export(Vo vo, String props, HttpServletResponse response) {
// ...
list = ossVipCustomService.selectForwardOnly(vo, Order.build());
// ...
}Service layer (core logic):
public List<Bo> selectForwardOnly(Vo vo, Order order) {
final List<Bo> list = new ArrayList<>();
mapper.selectForwardOnly(vo, order, new ResultHandler<Bo>() {
@Override
public void handleResult(ResultContext<? extends Bo> resultContext) {
// callback processing
list.add(resultContext.getResultObject());
}
});
return list;
}DAO interface:
/**
* Streaming read of data
*/
void selectForwardOnly(@Param("record") Vo vo,
@Param("order") Order order,
ResultHandler<Bo> handler);Mapper XML configuration:
<select id="selectForwardOnly"
parameterType="com.*.Vo" resultMap="GetListBo"
resultSetType="FORWARD_ONLY" fetchSize="-2147483648">
SELECT * FROM customer
</select>The author notes that returning void from the DAO is intentional because the result is processed in the callback, not passed back to the service layer.
Personal reflections mention extensive Googling, adjustments to MySQL connection parameters and MyBatis settings, and the realization that the DAO should not expose a return value.
Additional resources and promotional links are included but are not part of the technical solution.
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.
Architect's Tech Stack
Java backend, microservices, distributed systems, containerized programming, and more.
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.
