MyBatis入门教程 - 从零开始学习MyBatis框架
MyBatis入门教程 - 从零开始学习MyBatis框架
目录
- MyBatis简介
- 环境搭建
- 第一个MyBatis程序
- MyBatis核心概念
- CRUD操作详解
- 动态SQL
- 关联映射
- MyBatis配置详解
- MyBatis与Spring集成
- MyBatis Generator代码生成器
- MyBatis Plus快速开发
- 最佳实践与常见问题
- 总结与进阶
1. MyBatis简介
MyBatis是一个优秀的持久层框架,它支持自定义SQL、存储过程以及高级映射。MyBatis消除了几乎所有的JDBC代码和参数的手动设置以及结果集的检索。MyBatis可以使用简单的XML或注解来配置和映射原生信息,将接口和Java的POJOs(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录。
核心特点:
- ✅ 半自动ORM框架:相比Hibernate的全自动,MyBatis需要手动编写SQL,但提供了灵活的映射机制
- ✅ SQL与代码分离:SQL写在XML配置文件中,便于维护和优化
- ✅ 动态SQL支持:强大的动态SQL功能,可以根据条件动态生成SQL语句
- ✅ 结果映射灵活:支持复杂的结果映射,包括一对一、一对多、多对多关系
- ✅ 轻量级框架:框架本身很小,学习成本低
- ✅ 性能优秀:直接使用SQL,可以精确控制SQL执行,性能优化空间大
| 特性 | JDBC | MyBatis | Hibernate |
|---|---|---|---|
| SQL控制 | 完全手动 | 手动编写 | 自动生成 |
| 学习曲线 | 简单 | 中等 | 陡峭 |
| 灵活性 | 最高 | 高 | 较低 |
| 性能优化 | 完全可控 | 可控 | 受限 |
| 适用场景 | 简单项目 | 中大型项目 | 中小型项目 |
- 需要精确控制SQL语句的项目
- 对性能要求较高的项目
- 数据库表结构复杂,关联关系多的项目
- 需要频繁修改SQL的项目
- 互联网项目,需求变化频繁
2. 环境搭建
- JDK 8或更高版本
- Maven 3.6+ 或 Gradle
- IDE(IntelliJ IDEA、Eclipse等)
- MySQL数据库(或其他数据库)
方式一:使用IDE创建
- 打开IntelliJ IDEA
- File → New → Project
- 选择Maven,点击Next
- 填写GroupId和ArtifactId
- 点击Finish
方式二:使用命令行创建
mvn archetype:generate -DgroupId=com.example -DartifactId=mybatis-demo -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false在pom.xml文件中添加MyBatis和数据库驱动依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>mybatis-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<mybatis.version>3.5.13</mybatis.version>
<mysql.version>8.0.33</mysql.version>
<junit.version>4.13.2</junit.version>
</properties>
<dependencies>
<!-- MyBatis核心依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- 日志框架(可选,推荐使用) -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.12</version>
</dependency>
<!-- JUnit测试框架 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>创建测试数据库和表:
-- 创建数据库
CREATE DATABASE IF NOT EXISTS mybatis_demo DEFAULT CHARACTER SET utf8mb4;
USE mybatis_demo;
-- 创建用户表
CREATE TABLE IF NOT EXISTS user (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL COMMENT '用户名',
password VARCHAR(100) NOT NULL COMMENT '密码',
email VARCHAR(100) COMMENT '邮箱',
age INT COMMENT '年龄',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
-- 插入测试数据
INSERT INTO user (username, password, email, age) VALUES
('zhangsan', '123456', 'zhangsan@example.com', 25),
('lisi', '123456', 'lisi@example.com', 30),
('wangwu', '123456', 'wangwu@example.com', 28);2.5 项目目录结构
创建标准的Maven项目结构:
mybatis-demo/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ ├── entity/
│ │ │ │ └── User.java
│ │ │ ├── mapper/
│ │ │ │ └── UserMapper.java
│ │ │ └── util/
│ │ │ └── MyBatisUtil.java
│ │ └── resources/
│ │ ├── mybatis-config.xml
│ │ └── mapper/
│ │ └── UserMapper.xml
│ └── test/
│ └── java/
│ └── com/
│ └── example/
│ └── UserMapperTest.java
└── pom.xml3. 第一个MyBatis程序
创建User.java实体类:
package com.example.entity;
import java.time.LocalDateTime;
/**
* 用户实体类
*/
public class User {
private Integer id;
private String username;
private String password;
private String email;
private Integer age;
private LocalDateTime createTime;
private LocalDateTime updateTime;
// 无参构造方法
public User() {
}
// 有参构造方法
public User(String username, String password, String email, Integer age) {
this.username = username;
this.password = password;
this.email = email;
this.age = age;
}
// Getter和Setter方法
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public LocalDateTime getCreateTime() {
return createTime;
}
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
public LocalDateTime getUpdateTime() {
return updateTime;
}
public void setUpdateTime(LocalDateTime updateTime) {
this.updateTime = updateTime;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", email='" + email + '\'' +
", age=" + age +
", createTime=" + createTime +
", updateTime=" + updateTime +
'}';
}
}创建UserMapper.java接口:
package com.example.mapper;
import com.example.entity.User;
import java.util.List;
/**
* 用户Mapper接口
*/
public interface UserMapper {
/**
* 根据ID查询用户
*/
User selectById(Integer id);
/**
* 查询所有用户
*/
List<User> selectAll();
/**
* 插入用户
*/
int insert(User user);
/**
* 更新用户
*/
int update(User user);
/**
* 删除用户
*/
int deleteById(Integer id);
}创建UserMapper.xml映射文件:
<?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">
<!-- 结果映射 -->
<resultMap id="userResultMap" type="com.example.entity.User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="password" column="password"/>
<result property="email" column="email"/>
<result property="age" column="age"/>
<result property="createTime" column="create_time"/>
<result property="updateTime" column="update_time"/>
</resultMap>
<!-- 根据ID查询 -->
<select id="selectById" parameterType="int" resultMap="userResultMap">
SELECT id, username, password, email, age, create_time, update_time
FROM user
WHERE id = #{id}
</select>
<!-- 查询所有 -->
<select id="selectAll" resultMap="userResultMap">
SELECT id, username, password, email, age, create_time, update_time
FROM user
</select>
<!-- 插入 -->
<insert id="insert" parameterType="com.example.entity.User" useGeneratedKeys="true" keyProperty="id">
INSERT INTO user (username, password, email, age)
VALUES (#{username}, #{password}, #{email}, #{age})
</insert>
<!-- 更新 -->
<update id="update" parameterType="com.example.entity.User">
UPDATE user
SET username = #{username},
password = #{password},
email = #{email},
age = #{age}
WHERE id = #{id}
</update>
<!-- 删除 -->
<delete id="deleteById" parameterType="int">
DELETE FROM user WHERE id = #{id}
</delete>
</mapper>创建mybatis-config.xml核心配置文件:
<?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>
<!-- 属性配置 -->
<properties>
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_demo?useSSL=false&serverTimezone=UTC&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</properties>
<!-- 设置 -->
<settings>
<!-- 开启驼峰命名转换 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 开启二级缓存 -->
<setting name="cacheEnabled" value="true"/>
<!-- 开启延迟加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 日志实现 -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!-- 类型别名 -->
<typeAliases>
<package name="com.example.entity"/>
</typeAliases>
<!-- 环境配置 -->
<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>
<!-- Mapper映射文件 -->
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>创建MyBatisUtil.java工具类:
package com.example.util;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
/**
* MyBatis工具类
*/
public class MyBatisUtil {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
// 读取配置文件
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 构建SqlSessionFactory
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
throw new RuntimeException("初始化MyBatis失败", e);
}
}
/**
* 获取SqlSession
*/
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession();
}
/**
* 获取SqlSessionFactory
*/
public static SqlSessionFactory getSqlSessionFactory() {
return sqlSessionFactory;
}
}创建UserMapperTest.java测试类:
package com.example;
import com.example.entity.User;
import com.example.mapper.UserMapper;
import com.example.util.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class UserMapperTest {
@Test
public void testSelectById() {
SqlSession sqlSession = MyBatisUtil.getSqlSession();
try {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.selectById(1);
System.out.println(user);
} finally {
sqlSession.close();
}
}
@Test
public void testSelectAll() {
SqlSession sqlSession = MyBatisUtil.getSqlSession();
try {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.selectAll();
users.forEach(System.out::println);
} finally {
sqlSession.close();
}
}
@Test
public void testInsert() {
SqlSession sqlSession = MyBatisUtil.getSqlSession();
try {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User("zhaoliu", "123456", "zhaoliu@example.com", 32);
int result = mapper.insert(user);
sqlSession.commit(); // 提交事务
System.out.println("插入结果:" + result);
System.out.println("插入的用户ID:" + user.getId());
} finally {
sqlSession.close();
}
}
@Test
public void testUpdate() {
SqlSession sqlSession = MyBatisUtil.getSqlSession();
try {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.selectById(1);
user.setEmail("newemail@example.com");
int result = mapper.update(user);
sqlSession.commit(); // 提交事务
System.out.println("更新结果:" + result);
} finally {
sqlSession.close();
}
}
@Test
public void testDelete() {
SqlSession sqlSession = MyBatisUtil.getSqlSession();
try {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int result = mapper.deleteById(4);
sqlSession.commit(); // 提交事务
System.out.println("删除结果:" + result);
} finally {
sqlSession.close();
}
}
}运行测试方法,如果一切正常,你应该能看到:
- 查询结果正确输出
- 插入、更新、删除操作成功执行
- SQL语句在控制台输出
4. MyBatis核心概念
SqlSessionFactory是MyBatis的核心接口,用于创建SqlSession实例。它是线程安全的,通常在整个应用生命周期中只需要一个实例。
创建方式:
// 方式1:从XML配置文件构建
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 方式2:从Java代码构建
DataSource dataSource = BlogDataSourceFactory.getBlogDataSource();
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(BlogMapper.class);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);SqlSession是MyBatis的主要接口,用于执行SQL命令、获取Mapper、管理事务等。
主要方法:
// 获取Mapper
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 执行SQL(不推荐,直接使用Mapper)
User user = sqlSession.selectOne("com.example.mapper.UserMapper.selectById", 1);
// 提交事务
sqlSession.commit();
// 回滚事务
sqlSession.rollback();
// 关闭会话
sqlSession.close();重要提示:
SqlSession不是线程安全的,每个线程都应该有自己独立的SqlSession实例- 使用完毕后必须关闭
SqlSession - 推荐使用try-with-resources自动关闭
Mapper接口是MyBatis的核心概念,它定义了数据访问的方法。
特点:
- 接口方法名对应XML中的SQL语句ID
- 方法参数对应SQL的参数
- 方法返回值对应SQL的查询结果
示例:
public interface UserMapper {
// 方法名selectById对应XML中的<select id="selectById">
User selectById(Integer id);
// 多个参数使用@Param注解
List<User> selectByCondition(@Param("username") String username,
@Param("age") Integer age);
}Mapper XML文件定义了SQL语句和结果映射。
基本结构:
<mapper namespace="com.example.mapper.UserMapper">
<!-- SQL语句定义 -->
<select id="selectById" resultType="User">
SELECT * FROM user WHERE id = #{id}
</select>
</mapper>关键元素:
<select>:查询语句<insert>:插入语句<update>:更新语句<delete>:删除语句<resultMap>:结果映射<sql>:可重用的SQL片段
MyBatis支持多种参数绑定方式:
单个参数
<!-- 基本类型 -->
<select id="selectById" resultType="User">
SELECT * FROM user WHERE id = #{id}
</select>
<!-- 对象类型 -->
<insert id="insert" parameterType="User">
INSERT INTO user (username, password) VALUES (#{username}, #{password})
</insert>多个参数
// 接口方法
List<User> selectByCondition(String username, Integer age);<!-- 方式1:使用arg0, arg1或param1, param2 -->
<select id="selectByCondition" resultType="User">
SELECT * FROM user
WHERE username = #{arg0} AND age = #{arg1}
</select>
<!-- 方式2:使用@Param注解(推荐) -->// 接口方法(推荐方式)
List<User> selectByCondition(@Param("username") String username,
@Param("age") Integer age);<select id="selectByCondition" resultType="User">
SELECT * FROM user
WHERE username = #{username} AND age = #{age}
</select>Map参数
// 接口方法
List<User> selectByMap(Map<String, Object> params);<select id="selectByMap" resultType="User">
SELECT * FROM user
WHERE username = #{username} AND age = #{age}
</select>自动映射
当数据库字段名和Java属性名一致时,MyBatis会自动映射:
<select id="selectById" resultType="User">
SELECT id, username, password FROM user WHERE id = #{id}
</select>开启驼峰命名转换
在mybatis-config.xml中配置:
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>这样create_time会自动映射到createTime。
手动映射(resultMap)
<resultMap id="userResultMap" type="User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="password" column="password"/>
<result property="createTime" column="create_time"/>
</resultMap>
<select id="selectById" resultMap="userResultMap">
SELECT * FROM user WHERE id = #{id}
</select>5. CRUD操作详解
简单查询
<!-- 根据ID查询 -->
<select id="selectById" parameterType="int" resultType="User">
SELECT * FROM user WHERE id = #{id}
</select>
<!-- 查询所有 -->
<select id="selectAll" resultType="User">
SELECT * FROM user
</select>
<!-- 条件查询 -->
<select id="selectByUsername" parameterType="string" resultType="User">
SELECT * FROM user WHERE username = #{username}
</select>模糊查询
<select id="selectByUsernameLike" parameterType="string" resultType="User">
SELECT * FROM user WHERE username LIKE CONCAT('%', #{username}, '%')
</select>分页查询
<select id="selectByPage" resultType="User">
SELECT * FROM user LIMIT #{offset}, #{limit}
</select>// 接口方法
List<User> selectByPage(@Param("offset") int offset, @Param("limit") int limit);简单插入
<insert id="insert" parameterType="User">
INSERT INTO user (username, password, email, age)
VALUES (#{username}, #{password}, #{email}, #{age})
</insert>获取自增主键
<!-- MySQL -->
<insert id="insert" parameterType="User" useGeneratedKeys="true" keyProperty="id">
INSERT INTO user (username, password, email, age)
VALUES (#{username}, #{password}, #{email}, #{age})
</insert>
<!-- 其他数据库 -->
<insert id="insert" parameterType="User">
<selectKey keyProperty="id" resultType="int" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
INSERT INTO user (username, password, email, age)
VALUES (#{username}, #{password}, #{email}, #{age})
</insert>批量插入
<insert id="insertBatch" parameterType="list">
INSERT INTO user (username, password, email, age) VALUES
<foreach collection="list" item="user" separator=",">
(#{user.username}, #{user.password}, #{user.email}, #{user.age})
</foreach>
</insert>// 接口方法
int insertBatch(List<User> users);简单更新
<update id="update" parameterType="User">
UPDATE user
SET username = #{username},
password = #{password},
email = #{email},
age = #{age}
WHERE id = #{id}
</update>选择性更新
<update id="updateSelective" parameterType="User">
UPDATE user
<set>
<if test="username != null">username = #{username},</if>
<if test="password != null">password = #{password},</if>
<if test="email != null">email = #{email},</if>
<if test="age != null">age = #{age},</if>
</set>
WHERE id = #{id}
</update>简单删除
<delete id="deleteById" parameterType="int">
DELETE FROM user WHERE id = #{id}
</delete>批量删除
<delete id="deleteByIds">
DELETE FROM user WHERE id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>// 接口方法
int deleteByIds(@Param("ids") List<Integer> ids);6. 动态SQL
动态SQL是MyBatis的强大特性之一,可以根据条件动态生成SQL语句。
<select id="selectByCondition" resultType="User">
SELECT * FROM user WHERE 1=1
<if test="username != null and username != ''">
AND username = #{username}
</if>
<if test="age != null">
AND age = #{age}
</if>
</select>类似Java的switch-case:
<select id="selectByCondition" resultType="User">
SELECT * FROM user WHERE 1=1
<choose>
<when test="username != null">
AND username = #{username}
</when>
<when test="email != null">
AND email = #{email}
</when>
<otherwise>
AND age > 18
</otherwise>
</choose>
</select>自动处理WHERE子句,去除多余的AND或OR:
<select id="selectByCondition" resultType="User">
SELECT * FROM user
<where>
<if test="username != null">
AND username = #{username}
</if>
<if test="age != null">
AND age = #{age}
</if>
</where>
</select>自动处理SET子句,去除多余的逗号:
<update id="updateSelective" parameterType="User">
UPDATE user
<set>
<if test="username != null">username = #{username},</if>
<if test="password != null">password = #{password},</if>
<if test="email != null">email = #{email},</if>
</set>
WHERE id = #{id}
</update>自定义前缀、后缀和去除的字符串:
<select id="selectByCondition" resultType="User">
SELECT * FROM user
<trim prefix="WHERE" prefixOverrides="AND |OR ">
<if test="username != null">
AND username = #{username}
</if>
<if test="age != null">
AND age = #{age}
</if>
</trim>
</select>遍历集合,常用于IN查询和批量操作:
<!-- IN查询 -->
<select id="selectByIds" resultType="User">
SELECT * FROM user WHERE id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
<!-- 批量插入 -->
<insert id="insertBatch">
INSERT INTO user (username, password) VALUES
<foreach collection="users" item="user" separator=",">
(#{user.username}, #{user.password})
</foreach>
</insert>创建变量并绑定到上下文:
<select id="selectByUsernameLike" resultType="User">
<bind name="pattern" value="'%' + username + '%'"/>
SELECT * FROM user WHERE username LIKE #{pattern}
</select>定义可重用的SQL片段:
<!-- 定义SQL片段 -->
<sql id="userColumns">
id, username, password, email, age
</sql>
<!-- 使用SQL片段 -->
<select id="selectAll" resultType="User">
SELECT <include refid="userColumns"/> FROM user
</select>7. 关联映射
假设有用户表和用户详情表:
-- 用户详情表
CREATE TABLE user_detail (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT UNIQUE,
real_name VARCHAR(50),
address VARCHAR(200),
phone VARCHAR(20),
FOREIGN KEY (user_id) REFERENCES user(id)
);实体类:
public class UserDetail {
private Integer id;
private Integer userId;
private String realName;
private String address;
private String phone;
// getter/setter...
}
public class User {
private Integer id;
private String username;
private UserDetail userDetail; // 一对一关系
// getter/setter...
}映射配置:
<resultMap id="userWithDetailMap" type="User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<association property="userDetail" javaType="UserDetail">
<id property="id" column="detail_id"/>
<result property="realName" column="real_name"/>
<result property="address" column="address"/>
<result property="phone" column="phone"/>
</association>
</resultMap>
<select id="selectUserWithDetail" resultMap="userWithDetailMap">
SELECT u.id, u.username,
d.id as detail_id, d.real_name, d.address, d.phone
FROM user u
LEFT JOIN user_detail d ON u.id = d.user_id
WHERE u.id = #{id}
</select>假设有订单表和订单项表:
CREATE TABLE orders (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT,
order_no VARCHAR(50),
total_amount DECIMAL(10,2),
FOREIGN KEY (user_id) REFERENCES user(id)
);
CREATE TABLE order_item (
id INT PRIMARY KEY AUTO_INCREMENT,
order_id INT,
product_name VARCHAR(100),
quantity INT,
price DECIMAL(10,2),
FOREIGN KEY (order_id) REFERENCES orders(id)
);实体类:
public class Order {
private Integer id;
private Integer userId;
private String orderNo;
private BigDecimal totalAmount;
private List<OrderItem> items; // 一对多关系
// getter/setter...
}
public class OrderItem {
private Integer id;
private Integer orderId;
private String productName;
private Integer quantity;
private BigDecimal price;
// getter/setter...
}映射配置:
<resultMap id="orderWithItemsMap" type="Order">
<id property="id" column="id"/>
<result property="orderNo" column="order_no"/>
<result property="totalAmount" column="total_amount"/>
<collection property="items" ofType="OrderItem">
<id property="id" column="item_id"/>
<result property="productName" column="product_name"/>
<result property="quantity" column="quantity"/>
<result property="price" column="price"/>
</collection>
</resultMap>
<select id="selectOrderWithItems" resultMap="orderWithItemsMap">
SELECT o.id, o.order_no, o.total_amount,
i.id as item_id, i.product_name, i.quantity, i.price
FROM orders o
LEFT JOIN order_item i ON o.id = i.order_id
WHERE o.id = #{id}
</select>假设有用户表和角色表,通过中间表关联:
CREATE TABLE role (
id INT PRIMARY KEY AUTO_INCREMENT,
role_name VARCHAR(50)
);
CREATE TABLE user_role (
user_id INT,
role_id INT,
PRIMARY KEY (user_id, role_id),
FOREIGN KEY (user_id) REFERENCES user(id),
FOREIGN KEY (role_id) REFERENCES role(id)
);映射配置:
<resultMap id="userWithRolesMap" type="User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<collection property="roles" ofType="Role">
<id property="id" column="role_id"/>
<result property="roleName" column="role_name"/>
</collection>
</resultMap>
<select id="selectUserWithRoles" resultMap="userWithRolesMap">
SELECT u.id, u.username,
r.id as role_id, r.role_name
FROM user u
LEFT JOIN user_role ur ON u.id = ur.user_id
LEFT JOIN role r ON ur.role_id = r.id
WHERE u.id = #{id}
</select>8. MyBatis配置详解
<?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>
<!-- 属性 -->
<properties/>
<!-- 设置 -->
<settings/>
<!-- 类型别名 -->
<typeAliases/>
<!-- 类型处理器 -->
<typeHandlers/>
<!-- 对象工厂 -->
<objectFactory/>
<!-- 插件 -->
<plugins/>
<!-- 环境配置 -->
<environments/>
<!-- 数据库厂商标识 -->
<databaseIdProvider/>
<!-- 映射器 -->
<mappers/>
</configuration><!-- 方式1:直接配置 -->
<properties>
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_demo"/>
</properties>
<!-- 方式2:引用外部文件 -->
<properties resource="db.properties"/>
<!-- 方式3:引用URL -->
<properties url="file:///path/to/db.properties"/>常用设置:
<settings>
<!-- 开启驼峰命名转换 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 开启二级缓存 -->
<setting name="cacheEnabled" value="true"/>
<!-- 延迟加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
<!-- 日志实现 -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!-- 默认执行器类型 -->
<setting name="defaultExecutorType" value="REUSE"/>
<!-- 超时时间 -->
<setting name="defaultStatementTimeout" value="25"/>
</settings><!-- 方式1:单个类型别名 -->
<typeAliases>
<typeAlias type="com.example.entity.User" alias="User"/>
</typeAliases>
<!-- 方式2:包扫描(推荐) -->
<typeAliases>
<package name="com.example.entity"/>
</typeAliases>MyBatis内置别名:
int→Integerstring→Stringlist→Listmap→Map- 等等
<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>
<environment id="production">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!-- 生产环境配置 -->
</dataSource>
</environment>
</environments>数据源类型:
UNPOOLED:不使用连接池POOLED:使用连接池(推荐)JNDI:JNDI数据源
<!-- 方式1:单个Mapper文件 -->
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
<!-- 方式2:使用class -->
<mappers>
<mapper class="com.example.mapper.UserMapper"/>
</mappers>
<!-- 方式3:包扫描(推荐) -->
<mappers>
<package name="com.example.mapper"/>
</mappers>9. MyBatis与Spring集成
<dependencies>
<!-- Spring核心 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.23</version>
</dependency>
<!-- Spring JDBC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.23</version>
</dependency>
<!-- MyBatis-Spring整合 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.1.0</version>
</dependency>
<!-- 数据源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.16</version>
</dependency>
</dependencies>创建applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描组件 -->
<context:component-scan base-package="com.example"/>
<!-- 数据源 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_demo"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!-- SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>
<!-- Mapper扫描 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.example.mapper"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
</beans>@Repository
public interface UserMapper {
User selectById(Integer id);
// ...
}@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public User getUserById(Integer id) {
return userMapper.selectById(id);
}
}10. MyBatis Generator代码生成器
MyBatis Generator可以自动生成实体类、Mapper接口和XML文件。
<build>
<plugins>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.4.2</version>
<configuration>
<configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
<overwrite>true</overwrite>
</configuration>
</plugin>
</plugins>
</build><?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration PUBLIC
"-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="MySQL" targetRuntime="MyBatis3">
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis_demo"
userId="root"
password="root"/>
<javaModelGenerator targetPackage="com.example.entity"
targetProject="src/main/java"/>
<sqlMapGenerator targetPackage="mapper"
targetProject="src/main/resources"/>
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.example.mapper"
targetProject="src/main/java"/>
<table tableName="user" domainObjectName="User"/>
</context>
</generatorConfiguration>mvn mybatis-generator:generate11. MyBatis Plus快速开发
MyBatis Plus是MyBatis的增强工具,提供了更多便捷功能。
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3</version>
</dependency>public interface UserMapper extends BaseMapper<User> {
// 继承了基本的CRUD方法
// selectById, selectList, insert, update, delete等
}@Service
public class UserService extends ServiceImpl<UserMapper, User> {
// 继承了更多便捷方法
}12. 最佳实践与常见问题
使用Mapper接口而不是直接使用SqlSession
// 推荐 UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.selectById(1); // 不推荐 User user = sqlSession.selectOne("selectById", 1);使用参数化查询防止SQL注入
<!-- 正确 --> WHERE username = #{username} <!-- 错误 --> WHERE username = '${username}'合理使用resultMap
- 复杂映射使用resultMap
- 简单映射可以使用resultType
SQL语句写在XML中
- 便于维护和优化
- 支持动态SQL
使用连接池
- 提高性能
- 管理数据库连接
Q1: #{ }和${ }的区别?
#{ }:预编译,防止SQL注入,会自动添加引号${ }:字符串替换,有SQL注入风险,不会添加引号
<!-- #{ }示例 -->
WHERE username = #{username} -- 生成: WHERE username = ?
<!-- ${ }示例 -->
ORDER BY ${columnName} -- 生成: ORDER BY idQ2: 如何解决字段名和属性名不一致?
- 方式1:开启驼峰命名转换
- 方式2:使用resultMap手动映射
- 方式3:SQL中使用别名
Q3: 如何实现分页?
- 方式1:使用LIMIT子句
- 方式2:使用PageHelper插件
- 方式3:使用MyBatis Plus的分页插件
Q4: 如何开启二级缓存?
<!-- 在mybatis-config.xml中 -->
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
<!-- 在Mapper.xml中 -->
<mapper namespace="com.example.mapper.UserMapper">
<cache/>
<!-- SQL语句 -->
</mapper>Q5: 如何处理枚举类型?
实现TypeHandler接口或使用MyBatis内置的枚举处理器。
13. 总结与进阶
通过本教程,你已经掌握了:
- ✅ MyBatis的基本概念和配置
- ✅ CRUD操作的实现
- ✅ 动态SQL的使用
- ✅ 关联映射的处理
- ✅ 与Spring的集成
- ✅ 代码生成器的使用
MyBatis源码学习
- SqlSessionFactory的创建过程
- SQL语句的执行流程
- 结果映射的实现原理
性能优化
- SQL优化
- 缓存机制
- 批量操作
插件开发
- 拦截器原理
- 自定义插件
Spring Boot集成
- 使用MyBatis Starter
- 多数据源配置
分布式事务
- 事务管理
- 多数据源事务
- 官方文档:https://mybatis.org/mybatis-3/zh/index.html
- GitHub:https://github.com/mybatis/mybatis-3
- MyBatis Plus:https://baomidou.com/
- 完成一个完整的CRUD项目
- 实现复杂的关联查询
- 优化SQL性能
- 集成到Spring Boot项目
结语
MyBatis是一个功能强大、灵活易用的持久层框架。通过本教程的学习,相信你已经掌握了MyBatis的核心功能和使用方法。
记住:
- 多实践:理论结合实践,多写代码
- 多思考:理解原理,不要死记硬背
- 多优化:关注性能,优化SQL
- 多学习:关注社区,持续学习
祝你学习愉快,编程顺利! 🚀
本教程由Java突击队学习社区编写,如有问题欢迎反馈。