From JDBC to MyBatis: Why Java Persistence Evolved and How to Optimize It
This article walks through the step‑by‑step JDBC query process, explains why wrapping JDBC in a framework like MyBatis is beneficial, analyzes MyBatis’s core components and execution flow, and presents five practical optimizations—from connection pooling to dynamic SQL and result caching—to improve Java backend performance.
1 Introduction
This article explains how JDBC evolves into MyBatis, why JDBC should be encapsulated into a persistence framework, and points out areas where MyBatis itself can be improved.
2 JDBC Query Process
The basic JDBC query involves seven steps:
Load JDBC driver;
Obtain a database connection;
Create a Statement object;
Set SQL parameters;
Execute the SQL and get a ResultSet;
Transform the ResultSet into a desired structure;
Release resources (close Connection, Statement, ResultSet).
Example code:
public static List<Map<String,Object>> queryForList(){
Connection connection = null;
ResultSet rs = null;
PreparedStatement stmt = null;
List<Map<String,Object>> resultList = new ArrayList<>();
try{
Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
String url = "jdbc:oracle:thin:@localhost:1521:ORACLEDB";
String user = "trainer";
String password = "trainer";
connection = DriverManager.getConnection(url,user,password);
String sql = "select * from userinfo where user_id = ? ";
stmt = connection.prepareStatement(sql);
stmt.setString(1, "zhangsan");
rs = stmt.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
int num = rsmd.getColumnCount();
while(rs.next()){
Map<String,Object> map = new HashMap<>();
for(int i=0;i<num;i++){
String columnName = rsmd.getColumnName(i+1);
map.put(columnName, rs.getString(columnName));
}
resultList.add(map);
}
}catch(Exception e){
e.printStackTrace();
}finally{
try{ if(rs!=null){ rs.close(); rs=null; } }catch(SQLException e){ e.printStackTrace(); }
try{ if(stmt!=null){ stmt.close(); stmt=null; } }catch(SQLException e){ e.printStackTrace(); }
try{ if(connection!=null){ connection.close(); connection=null; } }catch(SQLException e){ e.printStackTrace(); }
}
return resultList;
}3 From JDBC to MyBatis
Among the seven JDBC steps, many can be encapsulated to reduce boilerplate code.
3.1 Optimize Connection Acquisition and Release
Frequent opening/closing of connections wastes resources and hurts performance. Using a connection pool (e.g., DBCP or container‑provided JNDI) reuses existing connections and shortens latency.
By abstracting the pool behind DataSource , the application can switch implementations without code changes.
3.2 Centralize SQL Storage
Scattering SQL across Java classes leads to poor readability, difficult maintenance, and extra compile‑time work. Storing SQL in configuration files or a database (key‑value) improves manageability and allows direct execution in DB clients.
Loading SQL by key also raises the question of how to cache and retrieve the statements efficiently.
3.3 Parameter Mapping and Dynamic SQL
When the number of parameters varies, static placeholders are insufficient. Introducing a simple templating syntax (e.g., #param#) and an SQL parser enables dynamic statement generation based on a Map of parameters.
3.4 Result Mapping and Caching
After execution, the ResultSet must be transformed into Java objects (beans, maps, lists). MyBatis also supports session‑level caching to avoid repeated queries for identical parameter sets.
3.5 Eliminate Duplicate SQL
When many statements share fragments, extracting common parts into reusable SQL blocks reduces duplication and eases future modifications.
4 MyBatis Shortcomings
MyBatis requires explicit SQL for every operation, which can become overwhelming for large applications.
Potential improvement: generate simple CRUD SQL automatically from Java bean metadata (e.g., map UserInfo to USER_INFO table) using reflection.
5 MyBatis Overall Design
5.1 Interface Layer
MyBatis offers two interaction modes:
Traditional API where the developer passes a statement ID and parameters to SqlSession;
Mapper interface where each method corresponds to a mapped statement, enabling type‑safe, annotation‑driven configuration.
5.1.1 Traditional API
Creates a SqlSession, calls selectList, insert, etc., using statement IDs defined in XML.
5.1.2 Mapper Interface
MyBatis generates a dynamic proxy for the interface; method names map to XML <select|insert|update|delete> nodes, allowing clean, interface‑driven code.
5.2 Data Processing Layer
Core responsibilities:
Build dynamic SQL from parameters;
Execute SQL and wrap the ResultSet into a List.
5.2.1 Parameter Mapping & Dynamic SQL
Uses OGNL expressions to construct SQL at runtime, providing strong flexibility.
5.2.2 Result Set Handling
Converts ResultSet into Java collections, supporting one‑to‑many and many‑to‑one relationships via nested queries or nested result maps.
5.3 Framework Support Layer
Includes transaction management, connection‑pool handling, caching, and configuration loading (XML vs. Java API).
5.4 Bootstrap Layer
Two ways to start MyBatis:
XML configuration file;
Programmatic Java API.
5.5 Core Components and Relationships
Key classes:
SqlSession : top‑level API for database interaction;
Executor : creates BoundSql, manages cache, and delegates to StatementHandler;
StatementHandler : prepares PreparedStatement, sets parameters via ParameterHandler, and executes queries;
ParameterHandler : maps Java values to JDBC parameters;
ResultSetHandler : transforms ResultSet into Java objects;
TypeHandler : converts between Java types and JDBC types;
MappedStatement : encapsulates a single mapped SQL node;
SqlSource : generates BoundSql from parameters;
Configuration : holds all parsed configuration data.
6 SqlSession Workflow
Typical usage:
SqlSession sqlSession = factory.openSession();
List<Employee> result = sqlSession.selectList("com.louis.mybatis.dao.EmployeesMapper.selectByMinSalary", params); SqlSession.selectListlooks up the MappedStatement by ID, creates a BoundSql, obtains a cache key, and delegates to the Executor to perform the query.
7 MyBatis Initialization
7.1 What Initialization Does
It loads all configuration information (environments, mappers, type handlers, plugins, etc.) into a Configuration object.
7.2 Building Configuration from XML
The process: SqlSessionFactoryBuilder.build(InputStream) creates an XMLConfigBuilder; XMLConfigBuilder.parse() reads the XML, creates a Document, and wraps it in an XPathParser;
The parser extracts nodes such as properties, typeAliases, plugins, environments, mappers, etc.;
Each node is parsed and the corresponding objects are stored in the Configuration instance;
The fully populated Configuration is returned and used to construct a DefaultSqlSessionFactory.
7.3 Manual Java API Construction
Developers can instantiate XMLConfigBuilder directly, call parse() to obtain a Configuration, and then build a SqlSessionFactory with new SqlSessionFactoryBuilder().build(configuration).
7.4 Design Patterns Used
7.4.1 Builder Pattern – SqlSessionFactory
Multiple overloaded build methods allow different parameter combinations; the builder isolates construction logic from the final SqlSessionFactory object.
7.4.2 Builder Pattern – Environment
The inner static Environment.Builder assembles the environment’s ID, TransactionFactory, and DataSource before creating an immutable Environment instance.
8 Summary
The article demonstrates the transition from raw JDBC to the MyBatis framework, highlights practical performance optimizations, explains MyBatis’s internal architecture and execution flow, and shows how the framework is initialized using XML or Java API, employing Builder and Factory patterns to keep configuration flexible and extensible.
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.
Java High-Performance Architecture
Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.
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.
