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.

Su San Talks Tech
Su San Talks Tech
Su San Talks Tech
Master MyBatis: Core Concepts, Interview Questions, and Practical Tips

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?

MyBatis logo
MyBatis logo

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 diagram
ORM diagram

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?
JDBC shortcomings
JDBC shortcomings

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.

Hibernate vs MyBatis
Hibernate vs MyBatis

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?
MyBatis vs Hibernate
MyBatis vs 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:

MyBatis usage steps
MyBatis usage steps

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.

MyBatis component lifecycle
MyBatis component lifecycle

Integration with Spring can manage thread‑safe, transaction‑aware sessions and mappers automatically.

4. How to pass multiple parameters in a mapper?

Multiple parameters in mapper
Multiple parameters in 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 ${}

#{} vs ${}
#{} vs ${}

#{} 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?

LIKE query examples
LIKE query examples

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.

MyBatis association
MyBatis association

One‑to‑one ( &lt;association&gt; ) 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 ( &lt;collection&gt; ) 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?

MyBatis batch
MyBatis batch

Method 1: &lt;foreach&gt; 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.

First‑level cache
First‑level cache
Second‑level cache
Second‑level cache

Working Principle

MyBatis works in two major steps: building a SqlSessionFactory and running a session.

MyBatis workflow
MyBatis workflow
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 factory construction
Session factory construction
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.

MyBatis core components
MyBatis core components

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.

Mapper proxy
Mapper proxy

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.

Executor types
Executor types

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.

Pagination interceptor
Pagination interceptor

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从入门到精通》

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.

JavaSQLBackend DevelopmentPersistenceMyBatisORM
Su San Talks Tech
Written by

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.

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.