Mybatis

Li

Li Wei

April 26, 202611 min read

MyBatis

Overview

ORM (Object Relational Mapping) refers to the pattern that maps persistent data to entity objects, solving the mismatch between object‑oriented programming and relational databases.

MyBatis

  • MyBatis is an excellent Java‑based persistence framework that wraps JDBC, allowing developers to focus on the SQL statements themselves without dealing with driver loading, connection creation, or Statement handling.
  • It configures the statements to be executed via XML or annotations and maps Java objects to the dynamic parameters in those statements, generating the final SQL to run.
  • The framework executes the SQL, maps the results to Java objects, and returns them. By adopting ORM concepts, it solves the entity‑to‑database mapping problem, encapsulates JDBC, and hides the low‑level API details, so we can perform persistence operations without directly using the JDBC API.

Official MyBatis website:

Basic Operations

Relevant APIs

Resources – utility class for loading resources

  • InputStream getResourceAsStream(String fileName): Returns an input stream for the specified resource using the class loader.
  • The fileName parameter is the core configuration file placed in src: MyBatisConfig.xml

SqlSessionFactoryBuilder – builder used to obtain a SqlSessionFactory

  • SqlSessionFactory build(InputStream is): Obtains a SqlSessionFactory from a given resource’s input stream.

SqlSessionFactory – factory interface for obtaining SqlSession builders

  • SqlSession openSession(): Gets a SqlSession builder and opens a session with manual commit.
  • SqlSession openSession(boolean): Gets a SqlSession builder with true to enable auto‑commit.

SqlSession – the session object used to execute SQL, manage transactions, and create mapper proxies

  • A SqlSession represents a single interaction with the database and must be closed after use.
  • Like Connection, SqlSession is not thread‑safe; obtain a new instance for each use.

Note: Updates must be committed, either manually or by enabling auto‑commit.

Common SqlSession API

Method Description
List<T> selectList(String statement, Object parameter) Executes a query and returns a List of results
T selectOne(String statement, Object parameter) Executes a query and returns a single result object
int insert(String statement, Object parameter) Executes an insert and returns the number of rows affected
int update(String statement, Object parameter) Executes an update and returns the number of rows affected
int delete(String statement, Object parameter) Executes a delete and returns the number of rows affected
void commit() Commits the transaction
void rollback() Rolls back the transaction
T getMapper(Class<T> cls) Retrieves the proxy implementation of the specified mapper interface
void close() Releases resources

Mapper Configuration

Mapper XML files define the mapping between data and objects as well as the SQL statements to execute. They reside under the src directory.

File name: StudentMapper.xml

  • Header:

    • Root tag: <mapper> – the core root element
    • namespace attribute: the namespace (usually the fully‑qualified mapper interface name)
  • CRUD tags:

    • <select> – query operation
    • <insert> – insert operation
    • <update> – update operation
    • <delete> – delete operation
  • Common attributes:

    • id: unique identifier, used together with the namespace

    • resultType: fully‑qualified class name of the result object (must match the method’s return type; if the return type is a List, it must match the generic type)

    • parameterType: fully‑qualified class name of the parameter object (must match the method’s parameter type)

    • statementType (optional): STATEMENT, PREPARED, or CALLABLE. Default is PREPARED.

      • STATEMENT: Direct SQL execution using Statement (no pre‑compilation). Parameter placeholder: $
      • PREPARED: Pre‑processed parameters using PreparedStatement. Placeholder: #
      • CALLABLE: Executes stored procedures via CallableStatement
  • Parameter binding syntax:

    • SQL parameter placeholder: #{属性名}

Recommended official documentation: https://mybatis.org/mybatis-3/zh/sqlmap-xml.html

Core Configuration

The core configuration file holds the most important MyBatis settings, such as database connection, transaction handling, and connection‑pool information.

File name: MyBatisConfig.xml

  • Header:

    • Root tag: <configuration> – the core root element
  • Include external files:

    • <properties> – includes external property files (e.g., database connection settings)
    • resource attribute: specifies the file name
  • Settings:

    • <settings> – can modify runtime behavior (e.g., enable cache, lazy loading)
  • Type aliases:

    • <typeAliases> – parent tag for defining aliases for fully‑qualified class names

    • <typeAlias> – child tag that maps an alias to a class

      • type: fully‑qualified class name
      • alias: desired alias
    • <package> – automatically creates aliases for all classes in a package; the alias is the class name with the first letter lower‑cased

  • Built‑in aliases:

    Alias Java type
    string java.lang.String
    long java.lang.Long
    int java.lang.Integer
    double java.lang.Double
    boolean java.lang.Boolean
  • Environments: You can define multiple <environment> tags.

    • <environments default="dev"> – selects which environment is active

    • <environment id="dev"> – unique identifier matching the default attribute

    • <transactionManager type="JDBC"> – transaction manager (default is JDBC)

    • <dataSource type="POOLED"> – data source; POOLED uses MyBatis’s built‑in connection pool, UNPOOLED disables pooling

      • Child tags (<property>) specify driver, url, username, password and their values.
  • Mapper registration:

    • <mappers> – parent tag for mapper inclusion

    • <mapper resource="StudentMapper.xml"/> – include by resource name

    • <mapper url="..."/> – include via URL (network or file system)

    • <mapper class="com.example.mapper.StudentMapper"/> – include by fully‑qualified class name

    • <mapper> can also be used for batch registration.

#{} vs ${}

  • #{} – placeholder. The supplied value is treated as a string and automatically quoted. It is passed to the database via a prepared statement (?), which prevents SQL injection and improves security.
  • ${} – literal substitution. The value is inserted directly into the SQL without quoting, which can lead to injection vulnerabilities.

Use #{} wherever possible; avoid or minimize ${}.

When ${} is required:

  • When a table name is a parameter, e.g., SELECT * FROM ${tableName}
  • In ORDER BY clauses, e.g., SELECT * FROM t_user ORDER BY ${columnName}

Note: Use #{} for SQL parameters and ${} for values read from properties files.

Logging

During development, you often need to see the exact SQL MyBatis executes, along with parameters and results. This can be achieved with Log4j.

  • Add the Log4j dependency in pom.xml.
  • Configure Log4j inside the root <configuration> tag of the core config file.
  • Create a log4j.properties file under src.

Code Example

  • Entity class
  • StudentMapper interface
  • config.properties
  • MyBatisConfig.xml
  • StudentMapper.xml
  • Controller test code: query by ID
  • Controller test code: insert operation

Batch Operations

Three ways to perform batch operations:

  • Global batch (via <settings> attribute):

    • defaultExecutorType sets the default executor type.

      • SIMPLE – ordinary executor (default; parameters are set each time)
      • REUSE – reuses prepared statements (parameters set once, executed multiple times)
      • BATCH – reuses statements and performs batch updates (only for modification operations)
  • Session‑level batch using SqlSession.

  • Spring configuration (applicationContext.xml) for batch execution.

Mapper‑Based Development

Proxy Rules

Layered architecture: controller → service → DAO.

Traditional DAO requires an interface and an implementation class. With MyBatis’s mapper proxy approach, you only write the mapper interface (equivalent to a DAO interface); MyBatis generates a dynamic proxy for it at runtime.

Interface‑first development:

  • Define the mapper interface.
  • MyBatis creates a proxy that handles all database interactions.

Mapper interface conventions:

  • The namespace in Mapper.xml must match the fully‑qualified name of the mapper interface.
  • The id attribute of CRUD tags in Mapper.xml must match the method names in the mapper interface.
  • The parameterType attribute must match the method’s parameter type.
  • The resultType attribute must match the method’s return type.

How It Works

MyBatis uses JDK dynamic proxies. When you call sqlSession.getMapper(YourMapper.class), you receive a MapperProxy instance. Method invocations on this proxy are delegated to MapperMethod.execute(), which determines the operation type (insert, update, delete, select) via a switch statement and finally delegates to the underlying SqlSession to run the SQL.

Result Mapping

Relevant Tags

  • <resultMap> – defines the result object type; must align with the method’s return type (or generic type if returning a List).

  • <result> – maps a single column to a property; used to configure field‑to‑property mapping (alternative to resultType).

    • id attribute: unique identifier
    • type attribute: the Java class of the result object
    • autoMapping attribute: enables automatic mapping

Core configuration tags for result mapping:

  • <id> – maps primary‑key columns

  • <result> – maps non‑primary‑key columns

    • column: column name in the table
    • property: corresponding Java field name
  • <association> – maps a single nested object (one‑to‑one or many‑to‑one).

    • property: name of the nested object field in the parent class
    • javaType: Java type of the nested object
    • select: ID of a separate select statement used to load the nested object; the column specified by column is passed as a parameter
  • <collection> – maps a collection of nested objects (one‑to‑many or many‑to‑many).

    • property: name of the collection field in the parent class
    • ofType: Java type of the elements in the collection
  • <discriminator> – determines which mapping to use based on the value of a particular column, allowing custom object construction based on that value.

Nested Queries

A nested query can be used for complex object graphs. Circular references are resolved through caching.

Multi‑Table Queries

One‑to‑One

Steps:

  1. Prepare bean classes.
  2. Create OneToOneMapper.xml and include it in MyBatisConfig.xml (you can alias the bean package).
  3. Configure MyBatisConfig.xml.
  4. Write a test class.
One‑to‑Many

Steps:

  1. Prepare bean classes.
  2. Create the mapper XML.
  3. Implement the necessary code snippets.
Many‑to‑Many

Example: student‑course relationship where the join table does not need a separate bean class.

  1. Prepare bean classes.
  2. Create the mapper XML.

Discriminator Example

Requirement: If the query result indicates a female, fetch department information; otherwise, do not. If male, populate the last_name column.

Loading Strategies

Eager vs. Lazy Loading
  • Eager loading: The query runs immediately when the method is called.

  • Lazy loading: Data is fetched only when accessed. MyBatis supports lazy loading for one‑to‑one and one‑to‑many associations.

    • Pros: Reduces unnecessary joins; improves performance for queries that often need only the main table.
    • Cons: May increase total query time when many lazy‑loaded objects are eventually accessed, potentially hurting user experience.

Core configuration options:

Tag/Attribute Description Default
lazyLoadingEnabled Global switch for lazy loading. When true, all associations are lazy unless overridden with fetchType. false
aggressiveLazyLoading When true, any method call loads all lazy properties of the object; otherwise, each lazy property loads on demand. false

Lazy loading is implemented using CGLIB dynamic proxies:

  1. CGLIB creates a proxy for the target mapper that has lazy loading enabled.
  2. When a lazy property’s getter is invoked, the proxy’s interceptor detects a null value, triggers the SQL query, sets the property, and returns the value.
association

Step‑by‑step query: first retrieve the owner’s ID, then use that ID to fetch the full owner information.

  • In OneToOneMapper.xml (one‑to‑one mapping):

    • column attribute supplies the parameter for the referenced select tag.
    • select attribute names the other select statement to invoke.
    • fetchType="lazy" enables lazy loading for this association only.
  • Example files: PersonMapper.xml, PersonMapper.java, and a test class.

collection

In a one‑to‑many mapping, the collection node also supports lazy loading with select and column attributes.

  • In OneToManyMapper.xml (one‑to‑many mapping):

    • column specifies which column’s value is used as the condition for the nested query.
    • select points to the mapper method that retrieves the collection items (full qualified DAO class name + method name).
  • Example file: StudentMapper.xml.

Annotation‑Based Development

Single‑Table Operations

Annotations can replace XML mapper files, simplifying development.

Common annotations:

  • @Select("SQL query") – query operation
  • @Insert("SQL insert") – insert operation
  • @Update("SQL update") – update operation
  • @Delete("SQL delete") – delete operation

Parameter annotation:

  • @Param – used when a SQL statement requires multiple (>1) parameters to map method arguments to named placeholders.

Core configuration: Map the mapper interfaces to MyBatis via <mapper class="..."/> in MyBatisConfig.xml.

Typical steps:

  1. Create the mapper interface with annotated methods.
  2. Adjust the core configuration file to scan the mapper package.
  3. Define the bean class.
  4. Write test code.

Multi‑Table Operations

Relevant Annotations

When moving from XML to annotations for complex relationships, you can use:

  • @Results (replaces <resultMap>)
  • @Result (replaces <result> and <id>)
  • @One (for one‑to‑one associations)
  • @Many (for one‑to‑many collections)

@Results example:

@Results({
    @Result(column = "id", property = "id", id = true),
    @Result(column = "name", property = "name"),
    @Result(column = "dept_id", property = "department",
            one = @One(select = "com.example.mapper.DepartmentMapper.selectById"))
})

These annotations allow you to configure complex object graphs directly on the mapper interface without separate XML files.


Originally written by Li Wei (李唯_) and published in Chinese on 后端技术栈全书 (Full-Stack Backend Engineering). Translated and adapted for DriftSeas with permission.

Keep reading

More related articles from DriftSeas.