1. 什么是 MyBatis?

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原生类型、接口和 Java POJO(Plain Old Java Objects)为数据库中的记录。

主要特点:

  • SQL 与代码分离:将 SQL 语句放在 XML 文件或注解中,避免硬编码。
  • 灵活的映射机制:支持复杂的关联查询和结果映射。
  • 轻量级:相比 Hibernate 等全功能 ORM 框架,MyBatis 更轻量、灵活。
  • 支持存储过程:可以方便地调用数据库存储过程。

2. 核心概念

2.1 核心组件

  • SqlSessionFactory:创建 SqlSession 的工厂类,是 MyBatis 的核心配置对象。
  • SqlSession:提供执行 SQL 方法的会话对象,类似于 JDBC 中的 Connection。
  • Mapper 接口:定义数据库操作方法的接口,由 MyBatis 自动生成实现类。
  • 映射文件(XML)或注解:定义 SQL 语句和映射规则。

2.2 工作流程

  1. 读取 MyBatis 配置文件(mybatis-config.xml)和映射文件。
  2. 创建 SqlSessionFactory 实例。
  3. 通过 SqlSessionFactory 获取 SqlSession。
  4. 使用 SqlSession 执行 SQL 操作。
  5. 关闭 SqlSession。

3. 环境搭建

3.1 添加依赖(Maven)

xml

<!-- MyBatis 核心 -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.7</version>
</dependency>

<!-- 数据库驱动(以 MySQL 为例) -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.26</version>
</dependency>

<!-- 日志(可选,方便调试) -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.32</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
</dependency>

3.2 创建 MyBatis 配置文件 mybatis-config.xml

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>
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/test?useSSL=false"/>
        <property name="username" value="root"/>
        <property name="password" value="password"/>
      </dataSource>
    </environment>
  </environments>
  
  <!-- 映射文件位置 -->
  <mappers>
    <mapper resource="mappers/UserMapper.xml"/>
  </mappers>
</configuration>

3.3 创建实体类

java

public class User {
    private Long id;
    private String username;
    private String email;
    private Integer age;
    
    // 构造方法、Getter 和 Setter 略
}

4. 使用 XML 映射文件

4.1 创建 Mapper 接口

java

public interface UserMapper {
    User selectById(Long id);
    List<User> selectAll();
    int insert(User user);
    int update(User user);
    int deleteById(Long id);
}

4.2 创建映射文件 UserMapper.xml

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="BaseResultMap" type="com.example.entity.User">
    <id column="id" property="id" jdbcType="BIGINT"/>
    <result column="username" property="username" jdbcType="VARCHAR"/>
    <result column="email" property="email" jdbcType="VARCHAR"/>
    <result column="age" property="age" jdbcType="INTEGER"/>
  </resultMap>

  <!-- 查询单个用户 -->
  <select id="selectById" parameterType="java.lang.Long" resultMap="BaseResultMap">
    SELECT * FROM user WHERE id = #{id}
  </select>

  <!-- 查询所有用户 -->
  <select id="selectAll" resultMap="BaseResultMap">
    SELECT * FROM user
  </select>

  <!-- 插入用户 -->
  <insert id="insert" parameterType="com.example.entity.User">
    INSERT INTO user (username, email, age)
    VALUES (#{username}, #{email}, #{age})
  </insert>

  <!-- 更新用户 -->
  <update id="update" parameterType="com.example.entity.User">
    UPDATE user
    SET username = #{username},
        email = #{email},
        age = #{age}
    WHERE id = #{id}
  </update>

  <!-- 删除用户 -->
  <delete id="deleteById" parameterType="java.lang.Long">
    DELETE FROM user WHERE id = #{id}
  </delete>
</mapper>

5. 使用注解方式

5.1 简单 SQL 注解

java

public interface UserMapper {
    @Select("SELECT * FROM user WHERE id = #{id}")
    User selectById(Long id);

    @Select("SELECT * FROM user")
    List<User> selectAll();

    @Insert("INSERT INTO user (username, email, age) VALUES (#{username}, #{email}, #{age})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    int insert(User user);

    @Update("UPDATE user SET username=#{username}, email=#{email}, age=#{age} WHERE id=#{id}")
    int update(User user);

    @Delete("DELETE FROM user WHERE id = #{id}")
    int deleteById(Long id);
}

5.2 复杂映射注解

java

public interface UserMapper {
    @Results({
        @Result(property = "id", column = "id", id = true),
        @Result(property = "username", column = "username"),
        @Result(property = "email", column = "email"),
        @Result(property = "age", column = "age")
    })
    @Select("SELECT * FROM user WHERE id = #{id}")
    User selectById(Long id);
}

6. 使用 MyBatis

6.1 创建 SqlSessionFactory

java

public class MyBatisUtil {
    private static SqlSessionFactory sqlSessionFactory;
    
    static {
        try {
            InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    public static SqlSessionFactory getSqlSessionFactory() {
        return sqlSessionFactory;
    }
}

6.2 执行数据库操作

java

public class UserService {
    public User getUserById(Long id) {
        try (SqlSession session = MyBatisUtil.getSqlSessionFactory().openSession()) {
            UserMapper mapper = session.getMapper(UserMapper.class);
            return mapper.selectById(id);
        }
    }
    
    public List<User> getAllUsers() {
        try (SqlSession session = MyBatisUtil.getSqlSessionFactory().openSession()) {
            UserMapper mapper = session.getMapper(UserMapper.class);
            return mapper.selectAll();
        }
    }
    
    public void addUser(User user) {
        try (SqlSession session = MyBatisUtil.getSqlSessionFactory().openSession()) {
            UserMapper mapper = session.getMapper(UserMapper.class);
            mapper.insert(user);
            session.commit(); // 提交事务
        }
    }
    
    public void updateUser(User user) {
        try (SqlSession session = MyBatisUtil.getSqlSessionFactory().openSession()) {
            UserMapper mapper = session.getMapper(UserMapper.class);
            mapper.update(user);
            session.commit();
        }
    }
    
    public void deleteUser(Long id) {
        try (SqlSession session = MyBatisUtil.getSqlSessionFactory().openSession()) {
            UserMapper mapper = session.getMapper(UserMapper.class);
            mapper.deleteById(id);
            session.commit();
        }
    }
}

7. 高级特性

7.1 动态 SQL

MyBatis 提供了强大的动态 SQL 功能,可以根据条件动态生成 SQL 语句。

 

xml

<select id="findUsers" parameterType="map" resultType="User">
  SELECT * FROM user
  <where>
    <if test="username != null and username != ''">
      AND username LIKE CONCAT('%', #{username}, '%')
    </if>
    <if test="age != null">
      AND age &gt;= #{age}
    </if>
  </where>
</select>

7.2 关联查询

处理一对多、多对一关系。

 

xml

<resultMap id="UserWithOrdersMap" type="User">
  <id column="user_id" property="id"/>
  <result column="username" property="username"/>
  <collection property="orders" ofType="Order">
    <id column="order_id" property="id"/>
    <result column="order_number" property="orderNumber"/>
  </collection>
</resultMap>

<select id="getUserWithOrders" resultMap="UserWithOrdersMap">
  SELECT 
    u.id AS user_id, 
    u.username,
    o.id AS order_id,
    o.order_number
  FROM user u
  LEFT JOIN orders o ON u.id = o.user_id
  WHERE u.id = #{id}
</select>

7.3 分页查询

java

// 使用 RowBounds 进行分页(简单但性能较低)
List<User> selectAll(RowBounds rowBounds);

// 调用时
RowBounds rowBounds = new RowBounds(10, 20); // 从第10条开始,取20条
List<User> users = mapper.selectAll(rowBounds);

7.4 插件机制

自定义插件实现分页、性能监控等功能。

 

java

@Intercepts({
    @Signature(type = Executor.class, method = "query", 
               args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class PageInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 分页逻辑
        return invocation.proceed();
    }
}

8. 事务管理

8.1 自动提交事务

java

try (SqlSession session = sqlSessionFactory.openSession(true)) {
    // 自动提交事务
    UserMapper mapper = session.getMapper(UserMapper.class);
    // 执行数据库操作
}

8.2 手动提交事务

java

SqlSession session = sqlSessionFactory.openSession();
try {
    UserMapper mapper = session.getMapper(UserMapper.class);
    // 执行多个数据库操作
    session.commit(); // 提交事务
} catch (Exception e) {
    session.rollback(); // 回滚事务
    throw e;
} finally {
    session.close();
}

9. 与 Spring 集成

9.1 添加依赖

xml

<dependency>
    <groupId>org.mybatis.spring</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>2.0.6</version>
</dependency>

9.2 配置 Spring 和 MyBatis

java

@Configuration
@MapperScan("com.example.mapper") // 扫描 Mapper 接口
public class MyBatisConfig {

    @Bean
    public DataSource dataSource() {
        // 配置数据源
        return new DriverManagerDataSource("jdbc:mysql://localhost:3306/test", "root", "password");
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath:mappers/*.xml"));
        return factoryBean.getObject();
    }

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

10. 什么是 ResultMap?

ResultMap 是 MyBatis 中最强大的特性之一,用于解决数据库表结构与 Java 对象结构之间的不匹配问题。例如:

  • 数据库列名与 Java 属性名不一致(如 user_name → username)。
  • 复杂的关联查询(如一对一、一对多关系)。
  • 自定义类型转换(如将数据库的 INT 转为 Java 的 Boolean)。

1. 代码逐行解析

xml

<resultMap id="BaseResultMap" type="com.example.entity.User">
  <id column="id" property="id" jdbcType="BIGINT"/>
  <result column="username" property="username" jdbcType="VARCHAR"/>
  <result column="email" property="email" jdbcType="VARCHAR"/>
  <result column="age" property="age" jdbcType="INTEGER"/>
</resultMap>

1.1 根标签 <resultMap>

  • id:结果映射的唯一标识符,用于在 SQL 语句中引用(如 <select resultMap="BaseResultMap">)。
  • type:映射的目标 Java 类的全限定名(这里是 User 类)。

1.2 子标签 <id> 和 <result>

  • 共同点

    • column:数据库表的列名。
    • property:Java 对象的属性名(通过 getter/setter 访问)。
    • jdbcType:列的 JDBC 类型(如 BIGINTVARCHAR),用于处理空值等情况。
  • 区别

    • <id>:表示主键列,有助于提高 MyBatis 的缓存效率。
    • <result>:表示普通列。

2. 映射原理示例

假设执行 SQL:

sql

SELECT id, username, email, age FROM user WHERE id = 1;

MyBatis 会根据 BaseResultMap 将结果映射为 User 对象:

 

数据库列 Java 属性 映射后效果
id id user.setId(rs.getLong("id"))
username username user.setUsername(rs.getString("username"))
email email user.setEmail(rs.getString("email"))
age age user.setAge(rs.getInt("age"))

3. 为什么需要 ResultMap?

场景 1:列名与属性名不一致

若数据库列名为 user_name,而 Java 属性为 username,需显式映射:

xml

<result column="user_name" property="username"/>

场景 2:复杂关联查询

例如,查询 User 及其关联的 Order 列表:

xml

<resultMap id="UserWithOrders" type="User">
  <id column="user_id" property="id"/>
  <result column="username" property="username"/>
  <!-- 一对多关联:一个用户对应多个订单 -->
  <collection property="orders" ofType="Order">
    <id column="order_id" property="id"/>
    <result column="order_number" property="orderNumber"/>
  </collection>
</resultMap>

4. 简化写法:自动映射

如果数据库列名与 Java 属性名完全一致(如 id → id),MyBatis 支持自动映射,无需编写 resultMap

xml

<select id="selectById" resultType="com.example.entity.User">
  SELECT * FROM user WHERE id = #{id}
</select>

 

但建议复杂场景下仍使用 resultMap 以提高可读性和灵活性。

11.数据库操作映射

MyBatis 中使用 XML 配置文件定义的 数据库操作映射,包含了增删改查(CRUD)的基本操作。下面我会逐行解释每个 SQL 映射的含义和工作原理。

1. 查询单个用户

xml

<select id="selectById" parameterType="java.lang.Long" resultMap="BaseResultMap">
    SELECT * FROM user WHERE id = #{id}
</select>

关键属性

  • id:映射的唯一标识符,对应 Mapper 接口中的方法名(如 User selectById(Long id))。
  • parameterType:输入参数的类型(这里是 Long)。
  • resultMap:指定结果集的映射规则(引用之前定义的 BaseResultMap)。

执行流程

  1. Mapper 接口调用 selectById(1L)
  2. MyBatis 解析 SQL:SELECT * FROM user WHERE id = ?,并将参数 1L 绑定到占位符 ?
  3. 执行 SQL,返回结果集。
  4. 根据 BaseResultMap 将结果集映射为 User 对象。

2. 查询所有用户

xml

<select id="selectAll" resultMap="BaseResultMap">
    SELECT * FROM user
</select>

特点

  • 无 parameterType:因为不需要输入参数。
  • 返回列表:对应 Mapper 接口中的 List<User> selectAll()

执行流程

  1. 执行 SELECT * FROM user
  2. 将每行结果映射为 User 对象。
  3. 返回 User 列表。

3. 插入用户

xml

<insert id="insert" parameterType="com.example.entity.User">
    INSERT INTO user (username, email, age)
    VALUES (#{username}, #{email}, #{age})
</insert>

参数处理

  • #{username}:从 User 对象中获取 username 属性(通过 user.getUsername())。
  • 自动类型转换:MyBatis 会根据属性类型(如 StringInteger)自动设置 JDBC 参数。

执行流程

  1. 传入 User 对象(如 new User("Alice", "alice@example.com", 25))。
  2. 解析 SQL:INSERT INTO user (username, email, age) VALUES (?, ?, ?)
  3. 绑定参数:? = "Alice", "alice@example.com", 25
  4. 执行插入,返回受影响的行数(int)。

4. 更新用户

xml

<update id="update" parameterType="com.example.entity.User">
    UPDATE user
    SET username = #{username},
        email = #{email},
        age = #{age}
    WHERE id = #{id}
</update>

关键逻辑

  • 动态参数:从 User 对象中提取属性值。
  • 主键条件:通过 id 定位要更新的记录。

执行流程

  1. 传入已修改的 User 对象(如 id=1username="Bob")。
  2. 生成 SQL:UPDATE user SET username=?, email=?, age=? WHERE id=?
  3. 执行更新,返回成功更新的行数。

5. 删除用户

xml

<delete id="deleteById" parameterType="java.lang.Long">
    DELETE FROM user WHERE id = #{id}
</delete>

执行流程

  1. 传入 id(如 1L)。
  2. 生成 SQL:DELETE FROM user WHERE id = ?
  3. 执行删除,返回受影响的行数。

6. 关键语法解析

#{} 与 ${} 的区别

  • #{}(推荐):
    • 预编译处理:SQL 会被编译为 SELECT * FROM user WHERE id = ?,防止 SQL 注入。
    • 类型安全:自动处理类型转换(如 Long → BIGINT)。
  • ${}(慎用):
    • 直接替换:SQL 会被替换为 SELECT * FROM user WHERE id = 1,存在 SQL 注入风险。
    • 适用场景:动态表名、列名(如 ${tableName})。

参数传递方式

  • 单个参数:直接使用 #{paramName},如 #{id}
  • 多个参数
    • 使用 @Param 注解(接口方法中):

      java

      User selectUser(@Param("id") Long id, @Param("name") String name);
      

       

       

      xml

      WHERE id = #{id} AND username = #{name}
      

       

    • 使用对象(如 User):通过属性名访问(如 #{username})。

mybatis-spring-boot-starter

mybatis-spring-boot-starter 是 MyBatis 和 Spring Boot 的集成模块,它提供了自动配置功能,使得在 Spring Boot 项目中使用 MyBatis 更加方便。这个依赖会自动配置 MyBatis 的相关组件,包括 SqlSessionFactorySqlSessionTemplate 等,并且会自动扫描指定包下的 Mapper 接口。

特点

  • 自动配置:提供了自动配置功能,减少了手动配置的工作量。

  • 集成 Spring Boot:与 Spring Boot 的自动配置机制无缝集成。

  • 简化开发:提供了 @MapperScan 注解,方便扫描 Mapper 接口。

  • 依赖管理:管理了 MyBatis 和 Spring 相关的依赖,确保版本兼容。

 

Logo

一座年轻的奋斗人之城,一个温馨的开发者之家。在这里,代码改变人生,开发创造未来!

更多推荐