Java MyBatis Framework
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
- The application creates SqlSessionFactory through SqlSessionFactoryBuilder.
- SqlSessionFactory creates SqlSession.
- SqlSession obtains an instance of the Mapper interface.
- Call Mapper methods to perform database operations.
- Commit the transaction and close SqlSession.
MyBatis Configuration
1. Main Configuration File (mybatis-config.xml)
Example
<!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
<!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
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
2. Executing a Query
Example
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
newUser.setName("Zhang San");
newUser.setEmail("zhangsan@example.com");
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 * 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 * 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 * 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
<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
<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 the
SqlSessioncreation of SqlSession it is created; with theSqlSessionclosing of SqlSession it is destroyed.Scenarios that trigger clearing:
Execute
INSERT/UPDATE/DELETEoperationsCall
sqlSession.clearCache()Perform transaction rollback
Configure a different
Statement ID(even if the SQL is the same)
Notes:
Example
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:
| Strategy | Description | Applicable Scenario |
|---|---|---|
| LRU | Least Recently Used | Regular use |
| FIFO | First In, First Out | Fixed-order access |
| SOFT | Soft reference | Memory-sensitive scenarios |
| WEAK | Weak reference | Extremely memory-sensitive |
Integrating MyBatis with Spring
1. Dependency Configuration (Recommended for Modern Spring Boot)
Example
<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
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
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
<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
@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
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
@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
@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
public PlatformTransactionManager transactionManager() {
return new ChainedTransactionManager(
new JpaTransactionManager(entityManagerFactory()),
new DataSourceTransactionManager(dataSource())
);
}
Best Practices for MyBatis
- Use Mapper interfaces instead of directly using SqlSession: This makes the code more type-safe and easier to maintain.
- Use dynamic SQL appropriately: Avoid SQL injection risks and maintain SQL readability.
- Batch Operations: For large batch data operations, use batch processing to improve performance.
- Use caching appropriately: Configure appropriate caching strategies based on business requirements.
- 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.
Other Extensions
Common Java Class Libraries