Java MyBatis Framework

Java 常用类库Common Java Class Libraries


MyBatis is an excellent persistence framework that simplifies the interaction between Java applications and relational databases. MyBatis maps SQL statements to Java objects via XML or annotations, avoiding a large amount of boilerplate code in traditional JDBC programming.


Core Features of MyBatis

1. Separation of SQL and Code

MyBatis allows developers to separate SQL statements from Java code, storing them in XML files or annotations, making the code clearer and easier to maintain.

2. Automatic Mapping

MyBatis can automatically map database query results to Java objects, greatly reducing the workload of data conversion.

3. Dynamic SQL

MyBatis provides powerful dynamic SQL capabilities, allowing different SQL statements to be generated based on different conditions.

4. Caching Mechanism

MyBatis has built-in first-level and second-level caches, which can effectively improve application performance.

Basic Architecture of MyBatis

1. Core Components

  • SqlSessionFactoryFactory class for creating SqlSession
  • SqlSessionMain interface for executing SQL commands
  • Mapper interfaceDefines methods for database operations
  • Mapper XMLConfiguration file containing SQL statements

2. Workflow

  1. The application creates SqlSessionFactory through SqlSessionFactoryBuilder.
  2. SqlSessionFactory creates SqlSession.
  3. SqlSession obtains an instance of the Mapper interface.
  4. Call Mapper methods to perform database operations.
  5. Commit the transaction and close SqlSession.

MyBatis Configuration

1. Main Configuration File (mybatis-config.xml)

Example

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment>
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis_db"/>
                <property name="username" value="root"/>
                <property name="password" value="password"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="com/example/mapper/UserMapper.xml"/>
    </mappers>
</configuration>

2. Mapper File Example

Example

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper">
    <select id="getUserById" parameterType="int" resultType="com.example.model.User">
        SELECT * FROM users WHERE id = #{id}
    </select>
   
    <insert id="insertUser" parameterType="com.example.model.User">
        INSERT INTO users(name, email) VALUES(#{name}, #{email})
    </insert>
</mapper>

Basic Usage of MyBatis

1. Obtaining SqlSession

Example

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();

2. Executing a Query

Example

// Method 1: Use SqlSession directly
User user = session.selectOne("com.example.mapper.UserMapper.getUserById", 1);

// Method 2: Through the Mapper interface
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.getUserById(1);

3. Executing an Insert

Example

User newUser = new User();
newUser.setName("Zhang San");
newUser.setEmail("[email protected]");

int rows = mapper.insertUser(newUser);
session.commit(); // Commit transaction

Dynamic SQL in MyBatis

MyBatis provides a variety of dynamic SQL elements, which can dynamically generate SQL statements based on different conditions:

1. The if Element

Example

<select parameterType="map" resultType="User">
    SELECT * FROM users
    WHERE 1=1
    <if test="name != null">
        AND name = #{name}
    </if>
    <if test="email != null">
        AND email = #{email}
    </if>
</select>

2. The choose/when/otherwise Elements

Example

<select parameterType="map" resultType="User">
    SELECT * FROM users
    WHERE status = 'ACTIVE'
    <choose>
        <when test="name != null">
            AND name like #{name}
        </when>
        <when test="email != null">
            AND email = #{email}
        </when>
        <otherwise>
            AND 1=1
        </otherwise>
    </choose>
</select>

3. The foreach Element

Example

<select parameterType="list" resultType="User">
    SELECT * FROM users
    WHERE id IN
    <foreach item="id" collection="list" open="(" separator="," close=")">
        #{id}
    </foreach>
</select>

MyBatis Association Queries

1. One-to-One Association

Example

<resultMap type="User">
    <id property="id" column="user_id"/>
    <result property="name" column="user_name"/>
    <association property="address" javaType="Address">
        <id property="id" column="address_id"/>
        <result property="street" column="street"/>
        <result property="city" column="city"/>
    </association>
</resultMap>

<select resultMap="userWithAddress">
    SELECT
        u.id as user_id, u.name as user_name,
        a.id as address_id, a.street, a.city
    FROM users u
    LEFT JOIN addresses a ON u.address_id = a.id
    WHERE u.id = #{id}
</select>

2. One-to-Many Association

Example

<resultMap type="User">
    <id property="id" column="user_id"/>
    <result property="name" column="user_name"/>
    <collection property="orders" ofType="Order">
        <id property="id" column="order_id"/>
        <result property="orderDate" column="order_date"/>
        <result property="amount" column="amount"/>
    </collection>
</resultMap>

<select resultMap="userWithOrders">
    SELECT
        u.id as user_id, u.name as user_name,
        o.id as order_id, o.order_date, o.amount
    FROM users u
    LEFT JOIN orders o ON u.id = o.user_id
    WHERE u.id = #{id}
</select>

MyBatis Caching Mechanism

1. First-Level Cache

The first-level cache is a SqlSession-level cache, enabled by default. Within the same SqlSession, the same query will only execute SQL once.

Features:

  • Scope:SqlSessionLevel (enabled by default, cannot be disabled)

  • Lifecycle: with theSqlSessioncreation of SqlSession it is created; with theSqlSessionclosing of SqlSession it is destroyed.

  • Scenarios that trigger clearing:

    • ExecuteINSERT/UPDATE/DELETEoperations

    • CallsqlSession.clearCache()

    • Perform transaction rollback

    • Configure a differentStatement ID(even if the SQL is the same)

Notes:

Example

// Example: the same query is executed only once in the same SqlSession
User user1 = sqlSession.selectOne("getUserById", 1);  // Execute SQL
User user2 = sqlSession.selectOne("getUserById", 1);  // Retrieved from cache

2. Second-Level Cache

The second-level cache is a Mapper-level cache, and multiple SqlSessions can share the cache. It needs to be enabled in the configuration file:

<settings>
    <setting name="cacheEnabled" value="true"/>  <!-- 默认true可省略 -->
</settings>

And configure it in the Mapper file:

<cache
  eviction="LRU"                <!-- 淘汰策略(默认LRU) -->
  flushInterval="60000"         <!-- 刷新间隔(毫秒) -->
  size="512"                    <!-- 缓存对象数量 -->
  readOnly="true"/>             <!-- 只读模式(性能更优) -->

Cache Strategy Comparison:

StrategyDescriptionApplicable Scenario
LRULeast Recently UsedRegular use
FIFOFirst In, First OutFixed-order access
SOFTSoft referenceMemory-sensitive scenarios
WEAKWeak referenceExtremely memory-sensitive

Integrating MyBatis with Spring

1. Dependency Configuration (Recommended for Modern Spring Boot)

Example

<!-- pom.xml -->
<dependencies>
    <!-- Spring Boot Starter integration solution (recommended) -->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>3.0.3</version>
    </dependency>

    <!-- Or use in traditional Spring projects -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>3.0.3</version>
    </dependency>
   
    <!-- Select the database driver according to the actual situation -->
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <version>8.0.33</version>
    </dependency>
</dependencies>

2. Configuration Approaches (Three Mainstream Ways)

Approach 1: Spring Boot Auto-configuration (Simplest)

Example

# application.yml
mybatis
:
  mapper-locations
: classpath*:mapper/**/*.xml
  type-aliases-package
: com.example.model
  configuration
:
    map-underscore-to-camel-case
: true  # Automatic camel case conversion

Approach 2: Full Java Config Configuration (Fine-grained Control)

Example

@Configuration
public class MyBatisConfig {
   
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
        factory.setDataSource(dataSource);
        factory.setTypeAliasesPackage("com.example.model");
       
        // Custom configuration (example)
        org.apache.ibatis.session.Configuration config = new Configuration();
        config.setMapUnderscoreToCamelCase(true);
        config.setDefaultFetchSize(100);
        factory.setConfiguration(config);
       
        // Plugin configuration (example)
        factory.setPlugins(
            new MyBatisInterceptor(),
            new PaginationInterceptor()
        );
       
        return factory.getObject();
    }

    @Bean
    public MapperScannerConfigurer mapperScanner() {
        MapperScannerConfigurer scanner = new MapperScannerConfigurer();
        scanner.setBasePackage("com.example.mapper");
        scanner.setAnnotationClass(Repository.class); // Restrict annotation scanning
        return scanner;
    }
}

Approach 3: Traditional XML Configuration (Compatible with Legacy Projects)

Example

<!-- applicationContext.xml -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.example.mapper"/>
</bean>

3. Transaction Management Configuration

Example

@Configuration
@EnableTransactionManagement
public class TransactionConfig {
   
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
   
    // Can define transaction template
    @Bean
    public TransactionTemplate transactionTemplate(PlatformTransactionManager manager) {
        return new TransactionTemplate(manager);
    }
}

4. Usage Guidelines and Best Practices

Mapper interface definition:

Example

@Repository // Specify DAO layer annotations
public interface UserMapper {
    @Select("SELECT * FROM users WHERE id = #{id}")
    User selectById(@Param("id") int id);
   
    @Options(useGeneratedKeys = true, keyProperty = "id")
    @Insert("INSERT INTO users(name) VALUES(#{name})")
    int insert(User user);
}

Service layer example:

Example

@Service
@Transactional(readOnly = true) // Read-only by default
public class UserService {
   
    private final UserMapper userMapper;
   
    @Autowired // Constructor injection is recommended
    public UserService(UserMapper userMapper) {
        this.userMapper = userMapper;
    }
   
    @Transactional // Open transactions separately for write operations
    public User createUser(String name) {
        User user = new User();
        user.setName(name);
        userMapper.insert(user);
        return userMapper.selectById(user.getId());
    }
}

5. Advanced Feature Integration

Dynamic Data Source Configuration:

Example

@Bean
@Primary
public DataSource dynamicDataSource() {
    DynamicDataSource ds = new DynamicDataSource();
    ds.setDefaultTargetDataSource(primaryDataSource());
    ds.setTargetDataSources(Map.of(
        "master", primaryDataSource(),
        "slave", secondaryDataSource()
    ));
    return ds;
}

Multi-DataSource Transaction Management:

Example

@Bean
public PlatformTransactionManager transactionManager() {
    return new ChainedTransactionManager(
        new JpaTransactionManager(entityManagerFactory()),
        new DataSourceTransactionManager(dataSource())
    );
}

Best Practices for MyBatis

  1. Use Mapper interfaces instead of directly using SqlSession: This makes the code more type-safe and easier to maintain.
  2. Use dynamic SQL appropriately: Avoid SQL injection risks and maintain SQL readability.
  3. Batch Operations: For large batch data operations, use batch processing to improve performance.
  4. Use caching appropriately: Configure appropriate caching strategies based on business requirements.
  5. SQL Optimization: Write efficient SQL statements in the Mapper XML.

MyBatis is a flexible and powerful ORM framework. Used properly, it can greatly improve development efficiency and application performance. I hope this article helps you understand and use the MyBatis framework.

Java 常用类库Common Java Class Libraries

Other Extensions