Master MyBatis: Core Concepts, Interview Questions, and Practical Tips
This comprehensive guide explains MyBatis fundamentals, compares it with Hibernate, details its lifecycle, configuration, dynamic SQL, caching, lazy loading, batch operations, pagination, and plugin development, providing code examples and best‑practice recommendations for Java backend developers.
Basics
Hello everyone, I am Su San. The star of this session is MyBatis, the most popular ORM framework in China and a handy tool for CRUD developers. Let’s see what interview questions are commonly asked.
1. What is MyBatis?
Advantages :
MyBatis is a semi‑ORM framework that encapsulates JDBC, allowing developers to focus on raw SQL without handling driver loading, connection creation, or statement management, thus offering high performance and flexibility.
It supports XML or annotation configuration to map POJOs to database records, eliminating most JDBC boiler‑plate code and manual parameter handling.
Disadvantages :
Writing SQL can be labor‑intensive, especially with many fields or tables, requiring solid SQL skills.
SQL is tied to a specific database, reducing portability.
What is ORM?
ORM (Object‑Relational Mapping) is a technique that maps Java objects (POJOs) to relational database tables, automatically persisting objects to the database using metadata that describes the mapping.
Why is MyBatis a semi‑automatic ORM tool? How does it differ from fully automatic tools?
Hibernate is a fully automatic ORM; it can retrieve associated objects or collections directly from the object model.
MyBatis requires manual SQL for associations, making it a semi‑automatic ORM.
What shortcomings does plain JDBC have, and how does MyBatis solve them?
Frequent connection creation/release wastes resources – MyBatis uses a connection pool configured in mybatis-config.xml.
SQL embedded in code hurts maintainability – SQL is placed in mapper.xml files, separating it from Java code.
Parameter binding is cumbersome – MyBatis automatically maps Java objects to SQL parameters.
Result‑set parsing is tedious – MyBatis automatically maps query results to Java objects.
2. What are the differences between Hibernate and MyBatis?
Both are JDBC wrappers for the persistence layer.
Mapping relationship :
MyBatis is a semi‑automatic mapper; configuring Java‑SQL mappings and multi‑table joins is simple.
Hibernate is a fully automatic mapper; configuring Java‑table mappings for multi‑table joins is complex.
SQL optimization and portability :
Hibernate abstracts SQL, provides logging, caching, cascading, and HQL, offering good DB independence but less control over SQL performance.
MyBatis requires manual SQL, supports dynamic SQL, stored procedures, and is less portable but easier to optimize.
Suitable scenarios for MyBatis and Hibernate?
Hibernate: standard ORM, less SQL writing, suitable for stable, medium‑size projects such as office automation systems.
MyBatis: semi‑ORM, more SQL writing, suitable for rapidly changing requirements and fast‑iteration projects like e‑commerce sites.
3. MyBatis usage process and lifecycle
The basic usage steps are:
Create SqlSessionFactory from configuration or programmatically.
Open a SqlSession from the factory.
Execute database operations via the session.
Commit transaction for updates/deletes.
Close the session.
MyBatis lifecycle?
The lifecycle concerns the core components: SqlSessionFactoryBuilder: short‑lived, used only to build the factory. SqlSessionFactory: application‑wide singleton, creates sessions. SqlSession: analogous to a JDBC connection, not thread‑safe, lives per request or method. Mapper: obtained from a session, lives within the session’s transaction scope.
Integration with Spring can manage thread‑safe, transaction‑aware sessions and mappers automatically.
4. How to pass multiple parameters in a mapper?
Method 1: Positional parameters
public User selectUser(String name, int deptId);
<select id="selectUser" resultMap="UserResultMap">
select * from user where user_name = #{0} and dept_id = #{1}
</select>Not recommended because the order is not intuitive.
Method 2: @Param annotation
public User selectUser(@Param("userName") String name, @Param("deptId") int deptId);
<select id="selectUser" resultMap="UserResultMap">
select * from user where user_name = #{userName} and dept_id = #{deptId}
</select>Recommended for a small number of parameters.
Method 3: Map parameter
public User selectUser(Map<String, Object> params);
<select id="selectUser" parameterType="java.util.Map" resultMap="UserResultMap">
select * from user where user_name = #{userName} and dept_id = #{deptId}
</select>Suitable when many parameters are needed and they may change.
Method 4: Java Bean parameter
public User selectUser(User user);
<select id="selectUser" parameterType="com.jourwon.pojo.User" resultMap="UserResultMap">
select * from user where user_name = #{userName} and dept_id = #{deptId}
</select>Directly maps bean properties to SQL placeholders; recommended for readability.
5. Entity field names differ from table column names – what to do?
Solution 1: Use column aliases in the SQL to match entity property names.
<select id="getOrder" parameterType="int" resultType="com.jourwon.pojo.Order">
select order_id id, order_no orderno, order_price price from orders where order_id=#{id};
</select>Solution 2: Define a <resultMap> with explicit <result> mappings.
<select id="getOrder" parameterType="int" resultMap="orderResultMap"/>
<resultMap type="com.jourwon.pojo.Order" id="orderResultMap">
<id property="id" column="order_id"/>
<result property="orderno" column="order_no"/>
<result property="price" column="order_price"/>
</resultMap>6. Can MyBatis map Enum types?
Yes. MyBatis can map any object to a column by implementing a custom TypeHandler that defines setParameter() and getResult() for conversion between Java and JDBC types.
7. Difference between #{} and ${}
#{} is a placeholder that is pre‑compiled; ${} performs string concatenation without pre‑compilation.
#{} prevents SQL injection; ${} does not.
#{} replacement occurs inside the DBMS; ${} replacement occurs before the SQL reaches the DBMS.
8. How to write fuzzy LIKE queries?
Using '%${question}%' is vulnerable to injection – not recommended.
Using "%"#{question}"%" adds quotes correctly.
Recommended: CONCAT('%', #{question}, '%').
Using <bind> tags – not recommended.
9. Can MyBatis perform one‑to‑one and one‑to‑many associations?
Yes, it supports one‑to‑one, one‑to‑many, many‑to‑many, and many‑to‑one associations.
One‑to‑one ( <association> ) example with Order and Pay entities.
public class Order {
private Integer orderId;
private String orderDesc;
private Pay pay;
} <resultMap id="peopleResultMap" type="cn.fighter3.entity.Order">
<id property="orderId" column="order_id"/>
<result property="orderDesc" column="order_desc"/>
<association property="pay" javaType="cn.fighter3.entity.Pay">
<id column="payId" property="pay_id"/>
<result column="account" property="account"/>
</association>
</resultMap>One‑to‑many ( <collection> ) example with Category and Product.
public class Category {
private int categoryId;
private String categoryName;
private List<Product> products;
} <resultMap type="Category" id="categoryBean">
<id column="categoryId" property="category_id"/>
<result column="categoryName" property="category_name"/>
<collection property="products" ofType="Product">
<id column="product_id" property="productId"/>
<result column="productName" property="productName"/>
<result column="price" property="price"/>
</collection>
</resultMap>10. Does MyBatis support lazy loading? How does it work?
Lazy loading is enabled for <association> (one‑to‑one) and <collection> (one‑to‑many) via the lazyLoadingEnabled setting.
Implementation uses CGLIB to create a proxy; when a lazy property is accessed, the proxy issues a separate query to load the data and injects it into the original object.
Other frameworks like Hibernate use the same principle.
11. How to retrieve generated primary keys?
<insert id="insert" useGeneratedKeys="true" keyProperty="userId">
insert into user (user_name, user_password, create_time)
values (#{userName}, #{userPassword}, #{createTime, jdbcType=TIMESTAMP})
</insert>After insertion, the generated key is populated in the Java object:
mapper.insert(user);
Long id = user.getId();12. Does MyBatis support dynamic SQL?
Yes. MyBatis provides tags such as <if>, <choose>, <when>, <otherwise>, <trim>, <where>, <set>, and <foreach> to build SQL dynamically based on parameter values.
<select id="findActiveBlogWithTitleLike" resultType="Blog">
SELECT * FROM BLOG WHERE state='ACTIVE'
<if test="title != null">
AND title like #{title}
</if>
</select>13. How does MyBatis perform batch operations?
Method 1: <foreach> in an INSERT statement
<insert id="addEmpsBatch">
INSERT INTO emp (ename, gender, email, did)
VALUES
<foreach collection="emps" item="emp" separator=",">
(#{emp.eName}, #{emp.gender}, #{emp.email}, #{emp.dept.id})
</foreach>
</insert>Method 2: ExecutorType.BATCH
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
for (int i = 0; i < 1000; i++) {
mapper.addEmp(new Employee(UUID.randomUUID().toString().substring(0,5), "b", "1"));
}
session.commit();14. Explain MyBatis first‑level and second‑level cache
First‑level cache : Per‑session PerpetualCache stored in a HashMap. Scope is a single SqlSession. Cleared on session flush or close. Enabled by default.
Second‑level cache : Per‑mapper (namespace) cache, also based on PerpetualCache. Can be shared across sessions. Disabled by default; must be enabled and the cached objects must be Serializable. Custom cache implementations (e.g., Ehcache) can be configured.
Working Principle
MyBatis works in two major steps: building a SqlSessionFactory and running a session.
Building the session factory
Read configuration files (global config and mapper XML).
Initialize core settings (type aliases, plugins, mappers, object factories, etc.).
Create a singleton Configuration object.
Initialize environment variables such as the data source.
Instantiate DefaultSqlSessionFactory using the configuration.
Session execution
The session relies on four core components: Executor: Executes statements, handles caching, and manages transactions. StatementHandler: Prepares and executes JDBC statements. ParameterHandler: Sets parameters on prepared statements. ResultSetHandler: Maps result sets to Java objects.
Functional Architecture
MyBatis consists of three layers:
API layer : Public interfaces used by developers to interact with the database.
Data processing layer : Handles SQL lookup, parsing, execution, and result mapping.
Infrastructure layer : Provides connection management, transaction handling, configuration loading, and caching.
Why Mapper interfaces need no implementation
MyBatis creates mapper proxies via dynamic proxy (CGLIB). When session.getMapper(Mapper.class) is called, a MapperProxyFactory generates a proxy that delegates method calls to a MapperMethod, which ultimately invokes the appropriate SqlSession operation.
Executor Types
SimpleExecutor : Creates a new Statement for each operation and closes it immediately.
ReuseExecutor : Reuses prepared statements based on the SQL key, storing them in a map for later reuse.
BatchExecutor : Accumulates statements for batch execution; suitable for bulk inserts/updates but not for selects.
Plugins
19. How do MyBatis plugins work and how to write one?
MyBatis intercepts the four core objects ( Executor, StatementHandler, ParameterHandler, ResultSetHandler) using JDK dynamic proxies. A plugin implements Interceptor and defines @Intercepts with @Signature to specify the target type, method, and argument types.
@Intercepts({
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class MyInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("before…");
Object result = invocation.proceed();
System.out.println("after…");
return result;
}
}Configure the plugin in mybatis-config.xml:
<plugins>
<plugin interceptor="com.example.MyInterceptor">
<property name="dbType" value="mysql"/>
</plugin>
</plugins>20. How does MyBatis handle pagination? Plugin principle?
MyBatis provides RowBounds for in‑memory pagination, but physical pagination is usually achieved with a pagination plugin that intercepts the Executor.query method, rewrites the original SQL according to the database dialect (e.g., adding LIMIT for MySQL), and appends pagination parameters.
Typical flow:
Intercept the query method.
Extract the original SQL and pagination parameters.
Rewrite the SQL with dialect‑specific pagination clauses (e.g., SELECT * FROM (SELECT ...) t LIMIT offset, limit).
Execute the modified SQL and return the paged result set.
References
[1] MyBatis interview questions (2020) – https://blog.csdn.net/ThinkWon/article/details/101292950
[2] MyBatis official site – https://mybatis.org/mybatis-3/zh/index.html
[3] 《深入浅出MyBatis基础原理与实战》
[4] MyBatis cache mechanism – https://tech.meituan.com/2018/01/19/mybatis-cache.html
[5] 《MyBatis从入门到精通》
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.
Su San Talks Tech
Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.
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.
