MyBatis 是 iBatis 2.x 的升級版,提供了更強大的映射能力、註解支持、OGNL 表達式。本文記錄從零搭建 MyBatis 環境、核心配置文件解析、映射文件編寫、DAO 接口綁定機制的全過程。
| 特性 | iBatis 2.x | MyBatis 3.x |
|---|---|---|
| 關聯映射 | 僅支持嵌套查詢 (N+1 問題) | 新增嵌套結果映射,單次 JOIN 解決關聯 |
| DAO 綁定 | 需手動編寫實現類 | 接口綁定機制,namespace 對應接口全限定名,方法名對應 statement id,無需實現類 |
| 註解支持 | 無 | 支持 @Select、@Insert 等註解,簡化 XML |
| 動態 SQL | 基礎標簽 | 引入OGNL 表達式,<if>/<choose>/<trim>/<foreach> 更強大 |
| 配置規範 | DTD 簡單 | DTD 更嚴格,結構更清晰 |
<dependencies>
<!-- MyBatis 核心 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.15</version>
</dependency>
<!-- MySQL 驅動 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<!-- 測試用 JUnit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
mybatis-config.xml<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 1. 屬性文件外置 -->
<properties resource="db.properties"/>
<!-- 2. 環境配置 (可多環境) -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!-- 3. 映射器註冊 (三種方式) -->
<mappers>
<!-- 方式 A: 相對類路徑 -->
<mapper resource="mapper/UserMapper.xml"/>
<!-- 方式 B: 接口全限定名 (需 mapper 文件同目錄同名) -->
<mapper class="com.xxx.mapper.UserMapper"/>
<!-- 方式 C: 包掃描 (推薦) -->
<package name="com.xxx.mapper"/>
</mappers>
</configuration>
UserMapper.xml 與接口綁定接口定義:
public interface UserMapper {
User selectById(@Param("id") Long id);
List<User> selectByCondition(@Param("user") User user);
int insert(User user);
int update(User user);
int deleteById(Long id);
}
映射文件 (namespace = 接口全限定名):
<mapper namespace="com.xxx.mapper.UserMapper">
<resultMap id="userResult" type="com.xxx.entity.User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="email" column="email"/>
<result property="createTime" column="create_time"/>
</resultMap>
<select id="selectById" resultMap="userResult">
SELECT id, username, email, create_time FROM t_user WHERE id = #{id}
</select>
<select id="selectByCondition" resultMap="userResult">
SELECT * FROM t_user
<where>
<if test="user.username != null"> AND username LIKE CONCAT('%', #{user.username}, '%') </if>
<if test="user.email != null"> AND email = #{user.email} </if>
</where>
</select>
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
INSERT INTO t_user(username, email, create_time) VALUES(#{username}, #{email}, NOW())
</insert>
<update id="update">
UPDATE t_user SET username=#{username}, email=#{email} WHERE id=#{id}
</update>
<delete id="deleteById">
DELETE FROM t_user WHERE id = #{id}
</delete>
</mapper>
namespace 必須精確等於接口全限定名,MyBatis 据此生成接口代理對象。<select|insert|update|delete> 的 id 屬性。#{paramName},多參數需 @Param 指定名稱,或封裝 POJO。resultMap 解決列名與屬性名不一致 (駝峰/下劃線),支援嵌套結果映射 (<association>/<collection>) 解決 N+1。<where> 智能去除首個 AND/OR,<foreach> 批量插入/IN 查詢。public class MyBatisTest {
private SqlSessionFactory sqlSessionFactory;
@Before
public void init() throws IOException {
String resource = "mybatis-config.xml";
InputStream is = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
}
@Test
public void testSelectById() {
try (SqlSession session = sqlSessionFactory.openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.selectById(1L);
System.out.println(user);
}
}
}
<bind>/<script>/<trim> 進階技巧。MapperScannerConfigurer、事務管理、多数據源。MyBatis 以「SQL 為中心」的設計哲學,給予開發者最大靈活性,配合接口綁定機制,實現了 DAO 層零實現類的極致簡潔。