MyBatis 完全教程:从零开始到高级优化

一、MyBatis 概述

(一)MyBatis 简介

MyBatis 是一个优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。通过简单的 XML 或注解用于配置和原始映射,将接口和 Java 的 POJOs(Plain Old Java Objects,普通老式 Java 对象)映射成数据库中的记录。

MyBatis 的前身是 Apache 的开源项目 iBatis,2010 年这个项目由 Apache Software Foundation 迁移到了 Google Code,并且改名为 MyBatis。2013 年 11 月,MyBatis 正式迁移到 GitHub。

MyBatis 的主要特点包括:

  • 灵活性高:MyBatis 允许开发者编写 SQL 语句,因此可以灵活地处理各种复杂的查询和更新操作。
  • 与数据库交互紧密:MyBatis 直接操作 SQL,避免了 ORM(Object-Relational Mapping,对象关系映射)框架可能带来的性能损耗。
  • 支持动态 SQL:MyBatis 提供了强大的动态 SQL 功能,可以根据不同的条件动态拼接 SQL 语句。

与其他持久层框架相比,MyBatis 有其独特的优势和劣势。例如,与 Hibernate 相比,MyBatis 的灵活性更高,但 Hibernate 的代码更简洁,因为 Hibernate 是一个全 ORM 框架,它会自动将 Java 对象映射到数据库表中,而 MyBatis 需要手动编写 SQL 语句。对于一些对 SQL 有定制化需求的项目,MyBatis 是一个更好的选择。

(二)适用场景

MyBatis 适用于以下场景:

  • 中小型项目:对于中小型项目,MyBatis 的灵活性和高性能可以满足需求,同时避免了 ORM 框架可能带来的复杂性和性能问题。
  • 需要高性能数据库交互的项目:MyBatis 直接操作 SQL,因此可以实现高性能的数据库交互。
  • 对 SQL 有定制化需求的项目:如果项目中需要编写复杂的 SQL 语句,或者需要对 SQL 进行精细的优化,MyBatis 是一个很好的选择。

(三)优势与劣势

MyBatis 的优势包括:

  • 灵活性高:可以编写自定义的 SQL 语句,灵活处理各种复杂的数据库操作。
  • 性能优越:直接操作 SQL,避免了 ORM 框架可能带来的性能损耗。
  • 易于理解和使用:MyBatis 的学习曲线相对平缓,容易上手。

MyBatis 的劣势包括:

  • 配置较为复杂:需要编写大量的 XML 文件来配置 MyBatis 的行为。
  • SQL 编写繁琐:需要手动编写 SQL 语句,对于一些简单的 CRUD 操作,可能会显得比较繁琐。
  • 对于大型项目,维护成本较高:由于需要编写大量的 SQL 语句和配置文件,对于大型项目,维护成本可能会比较高。

二、开发环境搭建

(一)数据库准备

在开始使用 MyBatis 之前,需要先准备数据库。可以选择 MySQL、PostgreSQL、Oracle 等关系型数据库。以下以 MySQL 为例,介绍如何创建数据库和表结构。

  1. 安装 MySQL:可以从 MySQL 官方网站下载并安装 MySQL 数据库。
  2. 创建数据库:使用 MySQL 客户端工具(如 Navicat、DBeaver 或 MySQL Workbench)连接到 MySQL 数据库,创建一个新的数据库。例如,创建一个名为 mybatis_demo 的数据库。
  3. 创建表结构:在 mybatis_demo 数据库中创建表。例如,创建一个用户表 users 和一个订单表 orders
CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    password VARCHAR(50) NOT NULL,
    email VARCHAR(50)
);

CREATE TABLE orders (
    id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL,
    order_number VARCHAR(50) NOT NULL,
    amount DECIMAL(10, 2) NOT NULL,
    FOREIGN KEY (user_id) REFERENCES users(id)
);

(二)项目创建

使用 Maven 或 Gradle 创建一个 Java 项目,并添加 MyBatis 和数据库驱动的依赖。

  1. 使用 Maven 创建项目:在命令行中运行以下命令,创建一个 Maven 项目。
mvn archetype:generate -DgroupId=com.example -DartifactId=mybatis-demo -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
  1. 添加 MyBatis 和 MySQL 依赖:在项目的 pom.xml 文件中添加以下依赖。
<dependencies>
    <!-- MyBatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.13</version>
    </dependency>
    <!-- MySQL 驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.33</version>
    </dependency>
    <!-- 单元测试 -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>

(三)配置文件

MyBatis 的配置文件包括核心配置文件 mybatis-config.xml 和 Mapper 文件。以下是配置文件的详细说明。

1. MyBatis 核心配置文件

mybatis-config.xml 文件是 MyBatis 的核心配置文件,它包含了 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>
    <!-- 配置环境 -->
    <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/mybatis_demo"/>
                <property name="username" value="root"/>
                <property name="password" value="password"/>
            </dataSource>
        </environment>
    </environments>
    <!-- 配置 Mapper 文件 -->
    <mappers>
        <mapper resource="com/example/mapper/UserMapper.xml"/>
        <mapper resource="com/example/mapper/OrderMapper.xml"/>
    </mappers>
</configuration>

2. Mapper 文件

Mapper 文件定义了 SQL 映射语句,它将 Java 方法调用映射到数据库操作。以下是 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">
    <!-- 查询用户 -->
    <select id="selectUserById" 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 (username, password, email) VALUES (#{username}, #{password}, #{email})
    </insert>
    <!-- 更新用户 -->
    <update id="updateUser" parameterType="com.example.model.User">
        UPDATE users SET username = #{username}, password = #{password}, email = #{email} WHERE id = #{id}
    </update>
    <!-- 删除用户 -->
    <delete id="deleteUser" parameterType="int">
        DELETE FROM users WHERE id = #{id}
    </delete>
</mapper>

(四)工具准备

为了提高开发效率,可以使用一些工具来辅助开发。

1. MyBatis Generator

MyBatis Generator 是一个代码生成工具,它可以自动生成 Mapper 接口、Model 类和 Mapper XML 文件。以下是使用 MyBatis Generator 的步骤。

  1. 添加 MyBatis Generator 依赖:在 pom.xml 文件中添加以下依赖。
<dependency>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-core</artifactId>
    <version>1.4.1</version>
</dependency>
  1. 配置 MyBatis Generator:创建一个 generatorConfig.xml 文件,配置代码生成策略。
<?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="default" targetRuntime="MyBatis3">
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/mybatis_demo"
                        userId="root"
                        password="password"/>
        <javaModelGenerator targetPackage="com.example.model" targetProject="src/main/java"/>
        <sqlMapGenerator targetPackage="com.example.mapper" targetProject="src/main/resources"/>
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.example.mapper" targetProject="src/main/java"/>
        <table tableName="users" domainObjectName="User"/>
        <table tableName="orders" domainObjectName="Order"/>
    </context>
</generatorConfiguration>
  1. 运行 MyBatis Generator:在命令行中运行以下命令,生成代码。
mvn mybatis-generator:generate

2. 数据库管理工具

可以使用 Navicat、DBeaver 或 MySQL Workbench 等工具来管理数据库。这些工具提供了图形化界面,方便进行数据库操作。

3. IDE 配置

在 IntelliJ IDEA 或 Eclipse 中配置 MyBatis 插件,可以方便地编写和调试 SQL。例如,在 IntelliJ IDEA 中,可以安装 MyBatis Plugin 插件,它提供了 SQL 语法高亮、代码补全等功能。

三、MyBatis 基础操作

(一)CRUD 操作

MyBatis 的核心功能是实现数据库的 CRUD 操作。以下是 CRUD 操作的详细说明。

1. 创建 Mapper 接口

Mapper 接口定义了数据库操作的方法。例如,UserMapper 接口定义了用户表的 CRUD 操作。

package com.example.mapper;

import com.example.model.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper {
    User selectUserById(int id);
    int insertUser(User user);
    int updateUser(User user);
    int deleteUser(int id);
}

2. 编写 XML 文件

Mapper XML 文件定义了 SQL 映射语句。例如,UserMapper.xml 文件定义了用户表的 SQL 映射。

<?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="selectUserById" 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 (username, password, email) VALUES (#{username}, #{password}, #{email})
    </insert>
    <update id="updateUser" parameterType="com.example.model.User">
        UPDATE users SET username = #{username}, password = #{password}, email = #{email} WHERE id = #{id}
    </update>
    <delete id="deleteUser" parameterType="int">
        DELETE FROM users WHERE id = #{id}
    </delete>
</mapper>

3. 测试 CRUD 功能

编写测试类,通过 SqlSession 调用 Mapper 方法,测试 CRUD 功能。

package com.example.test;

import com.example.mapper.UserMapper;
import com.example.model.User;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.io.Resources;

import java.io.IOException;
import java.io.Reader;

public class MyBatisTest {
    public static void main(String[] args) throws IOException {
        // 加载 MyBatis 配置文件
        String resource = "mybatis-config.xml";
        Reader reader = Resources.getResourceAsReader(resource);
        // 构建 SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        // 打开 SqlSession
        try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
            // 获取 Mapper 接口
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            // 测试查询用户
            User user = userMapper.selectUserById(1);
            System.out.println(user.getUsername());
            // 测试插入用户
            User newUser = new User();
            newUser.setUsername("newuser");
            newUser.setPassword("newpassword");
            newUser.setEmail("newuser@example.com");
            userMapper.insertUser(newUser);
            // 测试更新用户
            user.setUsername("updateduser");
            userMapper.updateUser(user);
            // 测试删除用户
            userMapper.deleteUser(1);
        }
    }
}

(二)参数处理

MyBatis 支持多种参数类型,包括基本类型、POJO 和 Map。以下是参数处理的详细说明。

1. 使用 #{} 占位符绑定参数

在 SQL 映射语句中,可以使用 #{} 占位符绑定参数。例如:

<select id="selectUserById" parameterType="int" resultType="com.example.model.User">
    SELECT * FROM users WHERE id = #{id}
</select>

2. 处理复杂参数

如果参数是一个复杂的对象,可以通过点语法访问对象的属性。例如:

<insert id="insertUser" parameterType="com.example.model.User">
    INSERT INTO users (username, password, email) VALUES (#{username}, #{password}, #{email})
</insert>

3. @Param 注解

如果方法有多个参数,可以使用 @Param 注解来区分参数。例如:

int insertUser(@Param("username") String username, @Param("password") String password, @Param("email") String email);

(三)结果映射

结果映射是将数据库查询结果映射到 Java 对象的过程。MyBatis 提供了两种结果映射方式:resultTyperesultMap

1. resultType

resultType 是简单结果映射的方式,适用于查询结果直接映射到 Java 对象的情况。例如:

<select id="selectUserById" parameterType="int" resultType="com.example.model.User">
    SELECT * FROM users WHERE id = #{id}
</select>

2. resultMap

resultMap 是复杂结果映射的方式,适用于查询结果需要进行复杂映射的情况。例如:

<resultMap id="UserResultMap" type="com.example.model.User">
    <id property="id" column="id"/>
    <result property="username" column="username"/>
    <result property="password" column="password"/>
    <result property="email" column="email"/>
</resultMap>
<select id="selectUserById" parameterType="int" resultMap="UserResultMap">
    SELECT * FROM users WHERE id = #{id}
</select>

(四)分页查询

分页查询是数据库操作中常见的需求。MyBatis 提供了两种分页查询的方式:手动实现分页逻辑和使用分页插件。

1. 手动实现分页逻辑

可以通过计算分页参数(offsetlimit)来实现分页查询。例如:

<select id="selectUsersByPage" parameterType="map" resultType="com.example.model.User">
    SELECT * FROM users LIMIT #{offset}, #{limit}
</select>

在 Java 代码中,可以计算分页参数并调用分页查询方法。例如:

Map<String, Object> params = new HashMap<>();
params.put("offset", (page - 1) * pageSize);
params.put("limit", pageSize);
List<User> users = userMapper.selectUsersByPage(params);

2. 使用分页插件

MyBatis 提供了分页插件,如 PageHelper。使用分页插件可以简化分页查询的实现。以下是使用 PageHelper 的步骤。

  1. 添加 PageHelper 依赖:在 pom.xml 文件中添加以下依赖。
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>7.1.5</version>
</dependency>
  1. 配置 PageHelper 插件:在 mybatis-config.xml 文件中配置 PageHelper 插件。
<plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
        <property name="helperDialect" value="mysql"/>
    </plugin>
</plugins>
  1. 使用 PageHelper 实现分页查询:在 Java 代码中使用 PageHelper 实现分页查询。例如:
PageHelper.startPage(page, pageSize);
List<User> users = userMapper.selectAllUsers();

四、高级特性

(一)动态 SQL

动态 SQL 是 MyBatis 的一个重要特性,它可以根据不同的条件动态拼接 SQL 语句。以下是动态 SQL 的详细说明。

1. <if> 标签

<if> 标签可以根据条件拼接 SQL。例如:

<select id="selectUsersByUsernameAndPassword" parameterType="map" resultType="com.example.model.User">
    SELECT * FROM users
    <where>
        <if test="username != null">
            AND username = #{username}
        </if>
        <if test="password != null">
            AND password = #{password}
        </if>
    </where>
</select>

2. <where> 标签

<where> 标签可以自动处理 WHERE 关键字。例如:

<select id="selectUsersByUsernameAndPassword" parameterType="map" resultType="com.example.model.User">
    SELECT * FROM users
    <where>
        <if test="username != null">
            AND username = #{username}
        </if>
        <if test="password != null">
            AND password = #{password}
        </if>
    </where>
</select>

3. <foreach> 标签

<foreach> 标签可以遍历集合参数。例如:

<select id="selectUsersByIds" parameterType="list" resultType="com.example.model.User">
    SELECT * FROM users
    <where>
        <foreach collection="list" item="id" separator="OR">
            id = #{id}
        </foreach>
    </where>
</select>

4. <choose><when><otherwise> 标签

<choose><when><otherwise> 标签类似于 Java 中的 switch 语句。例如:

<select id="selectUsersByUsernameOrEmail" parameterType="map" resultType="com.example.model.User">
    SELECT * FROM users
    <where>
        <choose>
            <when test="username != null">
                AND username = #{username}
            </when>
            <when test="email != null">
                AND email = #{email}
            </when>
            <otherwise>
                AND 1 = 1
            </otherwise>
        </choose>
    </where>
</select>

(二)事务管理

事务管理是数据库操作中的一个重要概念。MyBatis 提供了两种事务管理方式:JDBC 事务和管理事务。

1. 事务类型

  • JDBC 事务:使用 JDBC 的事务管理。例如:
<transactionManager type="JDBC"/>
  • 管理事务:由容器管理事务(如 Spring)。例如:
<transactionManager type="MANAGED"/>

2. 事务控制

可以通过 SqlSession 提交或回滚事务。例如:

SqlSession sqlSession = sqlSessionFactory.openSession();
try {
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    userMapper.insertUser(newUser);
    sqlSession.commit();
} catch (Exception e) {
    sqlSession.rollback();
    throw e;
} finally {
    sqlSession.close();
}

3. 事务注解

在使用 Spring 时,可以使用 @Transactional 注解管理事务。例如:

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;

    @Transactional(rollbackFor = Exception.class)
    public void insertUser(User user) {
        userMapper.insertUser(user);
    }
}

(三)连接池

连接池可以提高数据库连接的复用性,提高系统的性能。MyBatis 提供了内置连接池和第三方连接池。

1. 内置连接池

MyBatis 提供了内置连接池,可以通过配置文件配置连接池参数。例如:

<dataSource type="POOLED">
    <property name="driver" 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="password"/>
    <property name="poolMaximumActiveConnections" value="10"/>
    <property name="poolMaximumIdleConnections" value="5"/>
    <property name="poolMaximumCheckoutTime" value="20000"/>
</dataSource>

2. 第三方连接池

可以使用第三方连接池,如 HikariCP。以下是使用 HikariCP 的步骤。

  1. 添加 HikariCP 依赖:在 pom.xml 文件中添加以下依赖。
<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>5.0.0</version>
</dependency>
  1. 配置 HikariCP 数据源:在 mybatis-config.xml 文件中配置 HikariCP 数据源。
<dataSource type="com.zaxxer.hikari.HikariDataSource">
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
    <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mybatis_demo"/>
    <property name="username" value="root"/>
    <property name="password" value="password"/>
    <property name="maximumPoolSize" value="10"/>
    <property name="minimumIdle" value="5"/>
    <property name="connectionTimeout" value="30000"/>
</dataSource>

(四)缓存机制

缓存机制可以提高系统的性能。MyBatis 提供了一级缓存和二级缓存。

1. 一级缓存

一级缓存是基于 SqlSession 的缓存。在同一个 SqlSession 中,查询相同的数据会直接从缓存中获取。

2. 二级缓存

二级缓存是基于 Mapper 的缓存。可以通过配置文件配置二级缓存策略。例如:

<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>

3. 使用第三方缓存框架

可以使用第三方缓存框架,如 EhCache。以下是使用 EhCache 的步骤。

  1. 添加 EhCache 依赖:在 pom.xml 文件中添加以下依赖。
<dependency>
    <groupId>org.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>3.10.8</version>
</dependency>
  1. 配置 EhCache:在 ehcache.xml 文件中配置缓存策略。
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://www.ehcache.org/v3"
        xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.0.xsd">
    <cache alias="userCache">
        <key-type>java.lang.Integer</key-type>
        <value-type>com.example.model.User</value-type>
        <expiry>
            <ttl unit="seconds">60</ttl>
        </expiry>
        <resources>
            <heap unit="entries">1000</heap>
        </resources>
    </cache>
</config>
  1. 配置 MyBatis 使用 EhCache:在 mybatis-config.xml 文件中配置二级缓存。
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>

(五)延迟加载

延迟加载可以提高系统的性能。可以通过配置文件配置延迟加载策略。例如:

<settings>
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>

五、注解开发

(一)注解使用

MyBatis 提供了注解方式来替代 XML 文件。以下是注解的详细说明。

1. CRUD 注解

  • @Select:用于查询操作。例如:
@Select("SELECT * FROM users WHERE id = #{id}")
User selectUserById(int id);
  • @Insert:用于插入操作。例如:
@Insert("INSERT INTO users (username, password, email) VALUES (#{username}, #{password}, #{email})")
int insertUser(User user);
  • @Update:用于更新操作。例如:
@Update("UPDATE users SET username = #{username}, password = #{password}, email = #{email} WHERE id = #{id}")
int updateUser(User user);
  • @Delete:用于删除操作。例如:
@Delete("DELETE FROM users WHERE id = #{id}")
int deleteUser(int id);

2. 参数注解

  • @Param:用于处理多个参数。例如:
int insertUser(@Param("username") String username, @Param("password") String password, @Param("email") String email);

3. 结果映射注解

  • @Results@Result:用于定义结果映射。例如:
@Results({
    @Result(property = "id", column = "id"),
    @Result(property = "username", column = "username"),
    @Result(property = "password", column = "password"),
    @Result(property = "email", column = "email")
})
@Select("SELECT * FROM users WHERE id = #{id}")
User selectUserById(int id);

(二)注解与 XML 的对比

1. 适用场景

  • 注解:适用于简单的 SQL 映射。例如,CRUD 操作。
  • XML:适用于复杂的 SQL 映射。例如,动态 SQL、多表查询。

2. 优缺点

  • 注解
    • 优点:代码更简洁,易于维护。
    • 缺点:不支持动态 SQL,功能相对有限。
  • XML
    • 优点:功能强大,支持复杂 SQL 映射。
    • 缺点:配置繁琐,维护成本高。

六、多表查询与关系映射

(一)一对一关系

一对一关系是指一个实体对应一个实体。例如,用户与用户详情(UserUserDetail)。

1. 实现方式

可以通过 association 标签实现一对一关系。例如:

<resultMap id="UserResultMap" type="com.example.model.User">
    <id property="id" column="id"/>
    <result property="username" column="username"/>
    <result property="password" column="password"/>
    <association property="userDetail" column="user_detail_id" javaType="com.example.model.UserDetail">
        <id property="id" column="id"/>
        <result property="phone" column="phone"/>
        <result property="address" column="address"/>
    </association>
</resultMap>
<select id="selectUserById" parameterType="int" resultMap="UserResultMap">
    SELECT u.*, ud.* FROM users u LEFT JOIN user_details ud ON u.id = ud.user_id WHERE u.id = #{id}
</select>

2. 注解实现

可以通过注解实现一对一关系。例如:

@One(select = "selectUserDetailById")
UserDetail userDetail;

(二)一对多关系

一对多关系是指一个实体对应多个实体。例如,用户与订单(UserOrder)。

1. 实现方式

可以通过 collection 标签实现一对多关系。例如:

<resultMap id="UserResultMap" type="com.example.model.User">
    <id property="id" column="id"/>
    <result property="username" column="username"/>
    <result property="password" column="password"/>
    <collection property="orders" ofType="com.example.model.Order">
        <id property="id" column="id"/>
        <result property="orderNumber" column="order_number"/>
        <result property="amount" column="amount"/>
    </collection>
</resultMap>
<select id="selectUserById" parameterType="int" resultMap="UserResultMap">
    SELECT u.*, o.* FROM users u LEFT JOIN orders o ON u.id = o.user_id WHERE u.id = #{id}
</select>

2. 注解实现

可以通过注解实现一对多关系。例如:

@Results({
    @Result(property = "id", column = "id"),
    @Result(property = "username", column = "username"),
    @Result(property = "password", column = "password"),
    @Result(property = "orders", column = "id", ofType = Order.class)
})
@Select("SELECT u.*, o.* FROM users u LEFT JOIN orders o ON u.id = o.user_id WHERE u.id = #{id}")
User selectUserById(int id);

(三)多对多关系

多对多关系是指多个实体对应多个实体。例如,用户与角色(UserRole)。

1. 实现方式

可以通过中间表(如 user_role)实现多对多关系。例如:

<resultMap id="UserResultMap" type="com.example.model.User">
    <id property="id" column="id"/>
    <result property="username" column="username"/>
    <result property="password" column="password"/>
    <collection property="roles" ofType="com.example.model.Role">
        <id property="id" column="id"/>
        <result property="roleName" column="role_name"/>
    </collection>
</resultMap>
<select id="selectUserById" parameterType="int" resultMap="UserResultMap">
    SELECT u.*, r.* FROM users u
    LEFT JOIN user_role ur ON u.id = ur.user_id
    LEFT JOIN roles r ON ur.role_id = r.id
    WHERE u.id = #{id}
</select>

2. 注解实现

可以通过注解实现多对多关系。例如:

@Many(select = "selectRolesByUserId")
List<Role> roles;

(四)联合查询与分步查询

1. 联合查询

联合查询是通过 SQL JOIN 语句实现的。例如:

<select id="selectUserById" parameterType="int" resultMap="UserResultMap">
    SELECT u.*, o.* FROM users u LEFT JOIN orders o ON u.id = o.user_id WHERE u.id = #{id}
</select>

2. 分步查询

分步查询是先查询主表,再根据主表结果查询关联表。例如:

<select id="selectUserById" parameterType="int" resultType="com.example.model.User">
    SELECT * FROM users WHERE id = #{id}
</select>
<select id="selectOrdersByUserId" parameterType="int" resultType="com.example.model.Order">
    SELECT * FROM orders WHERE user_id = #{userId}
</select>

在 Java 代码中,可以先查询用户,再查询订单。例如:

User user = userMapper.selectUserById(1);
List<Order> orders = orderMapper.selectOrdersByUserId(user.getId());

(五)复杂查询优化

1. 视图(View)

可以通过创建视图简化复杂查询。例如:

CREATE VIEW user_orders AS
SELECT u.*, o.* FROM users u LEFT JOIN orders o ON u.id = o.user_id;

然后可以通过视图查询数据。例如:

<select id="selectUserOrdersById" parameterType="int" resultType="com.example.model.UserOrder">
    SELECT * FROM user_orders WHERE id = #{id}
</select>

2. 存储过程

可以通过存储过程优化复杂查询。例如:

DELIMITER //
CREATE PROCEDURE GetUserOrders(IN userId INT)
BEGIN
    SELECT u.*, o.* FROM users u LEFT JOIN orders o ON u.id = o.user_id WHERE u.id = userId;
END //
DELIMITER ;

然后可以通过存储过程查询数据。例如:

<select id="selectUserOrdersById" parameterType="int" resultType="com.example.model.UserOrder">
    CALL GetUserOrders(#{userId})
</select>

七、工具与扩展

(一)MyBatis Generator

1. 功能

MyBatis Generator 可以自动生成 Mapper 接口、Model 类和 Mapper XML 文件。它支持自定义代码生成策略,例如生成注释、生成接口方法等。

2. 配置

可以通过 generatorConfig.xml 文件配置代码生成策略。例如:

<?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="default" targetRuntime="MyBatis3">
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/mybatis_demo"
                        userId="root"
                        password="password"/>
        <javaModelGenerator targetPackage="com.example.model" targetProject="src/main/java"/>
        <sqlMapGenerator targetPackage="com.example.mapper" targetProject="src/main/resources"/>
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.example.mapper" targetProject="src/main/java"/>
        <table tableName="users" domainObjectName="User"/>
        <table tableName="orders" domainObjectName="Order"/>
    </context>
</generatorConfiguration>

3. 使用

可以通过命令行运行 MyBatis Generator 生成代码。例如:

mvn mybatis-generator:generate

(二)插件开发

1. 自定义插件

可以通过实现 Interceptor 接口开发自定义插件。例如,开发一个日志插件记录 SQL 执行时间。

@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class LogInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = invocation.proceed();
        long endTime = System.currentTimeMillis();
        System.out.println("SQL 执行时间:" + (endTime - startTime) + "ms");
        return result;
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        // 设置插件属性
    }
}

2. 内置插件

MyBatis 提供了一些内置插件,例如分页插件(如 PageHelper)和日志插件(如 Log4j)。

(三)与其他框架集成

1. MyBatis 与 Spring 集成

可以通过配置文件将 MyBatis 集成到 Spring 中。例如:

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <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="password"/>
</bean>

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath*:com/example/mapper/*.xml"/>
</bean>

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

2. MyBatis 与 Spring Boot 集成

可以通过添加 MyBatis-Starter 依赖将 MyBatis 集成到 Spring Boot 中。例如:

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.0</version>
</dependency>

然后可以在 application.properties 文件中配置数据库连接信息。例如:

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_demo
spring.datasource.username=root
spring.datasource.password=password

八、最佳实践与优化

(一)性能优化

1. 缓存策略优化

可以通过配置合适的缓存策略优化查询性能。例如,使用 EhCache 作为二级缓存。

<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>

2. SQL 查询优化

可以通过优化 SQL 查询提高性能。例如,避免全表扫描,合理使用索引。

CREATE INDEX idx_username ON users(username);

3. 数据库连接池优化

可以通过配置合适的连接池参数优化数据库连接池性能。例如,使用 HikariCP 作为连接池。

<dataSource type="com.zaxxer.hikari.HikariDataSource">
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
    <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mybatis_demo"/>
    <property name="username" value="root"/>
    <property name="password" value="password"/>
    <property name="maximumPoolSize" value="10"/>
    <property name="minimumIdle" value="5"/>
    <property name="connectionTimeout" value="30000"/>
</dataSource>

(二)代码规范

1. Mapper 接口和 XML 文件的组织规范

  • 命名规范:Mapper 接口和 XML 文件的命名应该一致。例如,UserMapper.javaUserMapper.xml
  • 结构规范:XML 文件的结构应该清晰,易于维护。例如,使用 <mapper> 标签定义 SQL 映射。

2. SQL 编写规范

  • 字段明确:避免使用 SELECT *,明确指定字段。
  • 参数化查询:使用参数化查询,防止 SQL 注入。

(三)项目实战

1. 实际项目案例

可以通过分析一个实际项目的需求,从需求分析到代码实现,完整地展示一个项目的开发过程。例如,用户管理系统,包含用户增删改查、订单管理、权限管理等功能。

2. 常见问题及解决方案

可以通过分析实际项目中遇到的问题,提供解决方案。例如,SQL 执行慢的问题可以通过优化 SQL 查询解决,缓存失效问题可以通过合理配置缓存策略解决,多表查询性能问题可以通过使用视图或存储过程优化查询解决。

九、源码剖析

(一)工作原理

1. 初始化流程

MyBatis 的初始化流程包括解析 mybatis-config.xml 文件、构建 SqlSessionFactory 等。例如:

String resource = "mybatis-config.xml";
Reader reader = Resources.getResourceAsReader(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);

2. 运行流程

MyBatis 的运行流程包括打开 SqlSession、执行 SQL 操作等。例如:

SqlSession sqlSession = sqlSessionFactory.openSession();
try {
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    User user = userMapper.selectUserById(1);
} finally {
    sqlSession.close();
}

(二)核心组件

1. SqlSessionFactory

SqlSessionFactory 是 MyBatis 的核心组件之一,它负责构建 SqlSession。例如:

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);

2. SqlSession

SqlSession 是 MyBatis 的核心组件之一,它负责执行 SQL 操作。例如:

SqlSession sqlSession = sqlSessionFactory.openSession();

3. Mapper

Mapper 是 MyBatis 的核心组件之一,它负责将接口和 SQL 语句映射起来。例如:

UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

4. Executor

Executor 是 MyBatis 的核心组件之一,它负责执行 SQL。例如:

Executor executor = sqlSession.getExecutor();

(三)SQL 解析与执行

1. 动态 SQL 的解析过程

MyBatis 提供了强大的动态 SQL 功能,可以根据不同的条件动态拼接 SQL 语句。例如:

<select id="selectUsersByUsernameAndPassword" parameterType="map" resultType="com.example.model.User">
    SELECT * FROM users
    <where>
        <if test="username != null">
            AND username = #{username}
        </if>
        <if test="password != null">
            AND password = #{password}
        </if>
    </where>
</select>

2. SQL 执行的底层实现

MyBatis 使用 JDBC 执行 SQL。例如:

PreparedStatement ps = connection.prepareStatement(sql);

(四)缓存机制实现

1. 一级缓存

一级缓存是基于 SqlSession 的缓存。在同一个 SqlSession 中,查询相同的数据会直接从缓存中获取。

2. 二级缓存

二级缓存是基于 Mapper 的缓存。可以通过配置文件配置二级缓存策略。例如:

<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>

(五)事务管理实现

1. 事务提交

可以通过 SqlSession 提交事务。例如:

sqlSession.commit();

2. 事务回滚

可以通过 SqlSession 回滚事务。例如:

sqlSession.rollback();

3. 事务传播行为

可以通过 Spring 的 @Transactional 注解配置事务传播行为。例如:

@Transactional(rollbackFor = Exception.class)
public void insertUser(User user) {
    userMapper.insertUser(user);
}

十、面试与进阶

(一)常见面试问题

1. MyBatis 的工作原理

解释 MyBatis 的初始化和运行流程。

2. MyBatis 的缓存机制

解释一级缓存和二级缓存的区别和实现原理。

3. MyBatis 的事务管理

解释事务的提交和回滚机制。

4. MyBatis 与 Hibernate 的区别

从灵活性、性能、易用性等方面对比 MyBatis 和 Hibernate。

5. 动态 SQL 的实现原理

解释 <if><where><foreach> 等标签的解析过程。

(二)进阶技巧

1. 高级查询

实现分组查询(GROUP BY)和分页查询(使用 PageHelper 或手动实现)。

2. 性能调优

使用缓存优化查询性能,优化数据库连接池参数。

3. 源码阅读

阅读 MyBatis 源码,理解核心组件的实现。

十一、扩展学习

(一)MyBatis-Plus

1. 简介

MyBatis-Plus 是 MyBatis 的增强版,提供了更多便捷的功能。

2. 核心特性

(1)CRUD 操作简化

提供了 BaseMapper 接口,简化增删改查操作。例如:

int insert(T entity);
T selectById(Serializable id);
(2)分页插件

内置分页插件,支持多种分页方式。例如:

IPage<T> selectPage(Page<T> page, @Param("ew") Wrapper<T> queryWrapper);
(3)条件构造器

提供了 QueryWrapperUpdateWrapper 等条件构造器,简化 SQL 构造。例如:

QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username", "admin");
(4)逻辑删除

支持逻辑删除,避免物理删除数据。例如:

@TableLogic
private Integer deleted;
(5)乐观锁

支持乐观锁机制,防止并发更新问题。例如:

@Version
private Integer version;

3. 集成与使用

(1)集成

在项目中集成 MyBatis-Plus。例如:

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.3</version>
</dependency>
(2)使用

使用 MyBatis-Plus 提供的功能简化开发。例如:

@Mapper
public interface UserMapper extends BaseMapper<User> {
}

(二)MyBatis-X

1. 简介

MyBatis-X 是 MyBatis 的另一个增强版,提供了更多高级特性。

2. 核心特性

(1)动态表名

支持动态表名,方便多租户场景。例如:

@DynamicTableName
private String tableName;
(2)动态字段

支持动态字段,方便灵活查询。例如:

@DynamicField
private String dynamicField;
(3)高级查询

提供了更强大的查询功能,如多表查询、分组查询等。例如:

List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);

3. 集成与使用

(1)集成

在项目中集成 MyBatis-X。例如:

<dependency>
    <groupId>com.example</groupId>
    <artifactId>mybatis-x</artifactId>
    <version>1.0.0</version>
</dependency>
(2)使用

使用 MyBatis-X 提供的高级特性优化开发。例如:

@Mapper
public interface UserMapper extends BaseMapper<User> {
    @Select("SELECT * FROM users WHERE username = #{username}")
    User selectUserByUsername(@Param("username") String username);
}

十二、实战项目

(一)项目需求分析

1. 功能需求

  • 用户增删改查
  • 订单管理
  • 权限管理

2. 非功能需求

  • 性能优化
  • 安全性
  • 可扩展性

(二)项目设计

1. 数据库设计

  • 创建用户表(users
  • 创建订单表(orders
  • 创建角色表(roles

2. 系统架构设计

  • 使用分层架构(Controller、Service、Mapper、Model)
  • 配置数据源、事务管理、缓存策略等

(三)项目开发

1. 数据库操作

  • 使用 MyBatis 实现用户增删改查功能
  • 实现订单管理功能

2. 业务逻辑实现

  • 在 Service 层实现业务逻辑
  • 实现用户注册、登录、订单创建等

3. 接口开发

  • 使用 Spring Boot 开发 RESTful 接口
  • 实现 UserControllerOrderController

(四)项目优化

1. 性能优化

  • 配置缓存策略
  • 优化数据库连接池参数

2. 安全性优化

  • 防止 SQL 注入
  • 使用 HTTPS 加密通信

3. 可扩展性优化

  • 使用 MyBatis-Plus 或 MyBatis-X 提供的高级特性

(五)项目测试

1. 单元测试

  • 编写单元测试,测试数据库操作功能

2. 集成测试

  • 编写集成测试,测试系统整体功能

3. 性能测试

  • 使用 JMeter 或其他工具进行性能测试

(六)项目部署

1. 部署环境准备

  • 配置服务器(如 Tomcat、Nginx)
  • 配置数据库(如 MySQL、PostgreSQL)

2. 部署方式

  • 使用 Docker 容器化部署
  • 使用 Kubernetes 集群部署
posted @ 2025-03-25 17:08  软件职业规划  阅读(174)  评论(0)    收藏  举报