01.SpringBoot集成持久层框架MyBatis详细指南

发布于:2025-07-03 ⋅ 阅读:(16) ⋅ 点赞:(0)

文章目录

1. MyBatis简介

1.1 什么是MyBatis?

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

1.2 MyBatis的核心特点

  • 简单易学:相比JPA/Hibernate,MyBatis更容易理解和掌握
  • SQL控制:开发者可以完全控制SQL语句,适合复杂查询
  • 高性能:直接执行SQL,性能优异
  • 灵活映射:支持复杂的结果集映射
  • 与Spring集成:与Spring框架无缝集成
  • 动态SQL:支持动态构建SQL语句

1.3 MyBatis vs JPA/Hibernate

1.3.1 MyBatis的优势
  • SQL可控性强:可以编写复杂的原生SQL
  • 学习成本低:对于熟悉SQL的开发者更友好
  • 性能优化:可以针对具体业务优化SQL
  • 灵活性高:适合复杂的业务逻辑
1.3.2 JPA/Hibernate的优势
  • 对象关系映射:自动处理对象和表的映射
  • 跨数据库:支持多种数据库,自动生成SQL
  • 缓存机制:内置强大的缓存功能
  • 开发效率:减少SQL编写工作量

1.4 MyBatis核心组件

1.4.1 SqlSessionFactory
  • MyBatis的核心工厂类
  • 用于创建SqlSession
  • 整个应用生命周期中应该只有一个实例
1.4.2 SqlSession
  • 执行SQL命令的主要接口
  • 线程不安全,每次使用后需要关闭
  • 包含了所有执行SQL的方法
1.4.3 Mapper接口
  • 定义数据访问方法的接口
  • MyBatis会自动生成实现类
  • 通过注解或XML配置SQL语句
1.4.4 Configuration
  • MyBatis的配置信息
  • 包含数据源、映射文件等配置

2. SpringBoot集成MyBatis

2.1 添加依赖

2.1.1 Maven依赖配置
<?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 
         https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.14</version>
        <relativePath/>
    </parent>
    
    <groupId>com.example</groupId>
    <artifactId>mybatis-demo</artifactId>
    <version>1.0.0</version>
    <name>mybatis-demo</name>
    <description>SpringBoot集成MyBatis示例项目</description>
    
    <properties>
        <java.version>11</java.version>
        <mybatis.version>3.5.13</mybatis.version>
    </properties>
    
    <dependencies>
        <!-- SpringBoot Web启动器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <!-- MyBatis Spring Boot Starter -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.3.1</version>
        </dependency>
        
        <!-- MySQL驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        
        <!-- HikariCP连接池(SpringBoot默认) -->
        <dependency>
            <groupId>com.zaxxer</groupId>
            <artifactId>HikariCP</artifactId>
        </dependency>
        
        <!-- 数据验证 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        
        <!-- JSON处理 -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>
        
        <!-- 开发工具 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        
        <!-- 测试依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        
        <!-- MyBatis测试 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter-test</artifactId>
            <version>2.3.1</version>
            <scope>test</scope>
        </dependency>
        
        <!-- H2数据库(测试用) -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>test</scope>
        </dependency>
        
        <!-- 分页插件 -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.4.6</version>
        </dependency>
        
        <!-- 代码生成器(可选) -->
        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.4.2</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            
            <!-- MyBatis Generator插件 -->
            <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>
</project>
2.1.2 Gradle依赖配置
plugins {
    id 'org.springframework.boot' version '2.7.14'
    id 'io.spring.dependency-management' version '1.0.15.RELEASE'
    id 'java'
}

group = 'com.example'
version = '1.0.0'
sourceCompatibility = '11'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.3.1'
    implementation 'mysql:mysql-connector-java'
    implementation 'com.github.pagehelper:pagehelper-spring-boot-starter:1.4.6'
    
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter-test:2.3.1'
    testImplementation 'com.h2database:h2'
}

2.2 数据库配置

2.2.1 application.yml配置
# 服务器配置
server:
  port: 8080
  servlet:
    context-path: /

# Spring配置
spring:
  application:
    name: mybatis-demo
  
  # 数据源配置
  datasource:
    # MySQL配置
    url: jdbc:mysql://localhost:3306/mybatis_demo?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
    
    # HikariCP连接池配置
    hikari:
      # 连接池名称
      pool-name: MyBatisHikariCP
      # 最大连接数
      maximum-pool-size: 20
      # 最小空闲连接数
      minimum-idle: 5
      # 连接超时时间(毫秒)
      connection-timeout: 20000
      # 空闲连接存活最大时间(毫秒)
      idle-timeout: 300000
      # 连接池最大存活时间(毫秒)
      max-lifetime: 1200000
      # 连接测试查询
      connection-test-query: SELECT 1

# MyBatis配置
mybatis:
  # 映射文件位置
  mapper-locations: classpath:mapper/*.xml
  # 实体类包路径
  type-aliases-package: com.example.mybatisdemo.entity
  # MyBatis配置文件位置
  config-location: classpath:mybatis-config.xml
  # 执行器类型:SIMPLE, REUSE, BATCH
  executor-type: simple
  # 是否检查映射文件存在
  check-config-location: true

# MyBatis配置参数
mybatis:
  configuration:
    # 开启驼峰命名转换
    map-underscore-to-camel-case: true
    # 开启二级缓存
    cache-enabled: true
    # 延迟加载全局开关
    lazy-loading-enabled: true
    # 当触发方法时,是否加载该对象的所有属性
    aggressive-lazy-loading: false
    # 是否允许单一语句返回多结果集
    multiple-result-sets-enabled: true
    # 使用列标签代替列名
    use-column-label: true
    # 允许JDBC自动生成主键
    use-generated-keys: false
    # 指定默认执行器
    default-executor-type: simple
    # 设置超时时间
    default-statement-timeout: 25000
    # 设置查询返回值数量
    default-fetch-size: 100
    # 允许在嵌套语句中使用分页
    safe-row-bounds-enabled: false
    # 是否开启自动驼峰命名规则映射
    map-underscore-to-camel-case: true
    # 本地缓存机制
    local-cache-scope: session
    # 当没有为参数提供特定的JDBC类型时,为空值指定JDBC类型
    jdbc-type-for-null: other
    # 指定哪个对象的方法触发一次延迟加载
    lazy-load-trigger-methods: equals,clone,hashCode,toString
    # 指定动态SQL中用于开启和关闭的关键字
    default-scripting-language: org.apache.ibatis.scripting.xmltags.XMLLanguageDriver
    # 指定动态SQL生成的默认语言
    call-setters-on-nulls: false
    # 当返回行的所有列都是空时,MyBatis默认返回null
    return-instance-for-empty-row: false
    # 指定MyBatis增加到日志名称的前缀
    log-prefix: 
    # 指定MyBatis所用日志的具体实现
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

# 分页插件配置
pagehelper:
  # 数据库类型
  helper-dialect: mysql
  # 启用合理化,如果pageNum<1会查询第一页,如果pageNum>pages会查询最后一页
  reasonable: true
  # 支持通过Mapper接口参数来传递分页参数
  support-methods-arguments: true
  # 分页参数
  params: count=countSql
  # 返回PageInfo类型
  return-page-info: check

# 日志配置
logging:
  level:
    # MyBatis日志
    com.example.mybatisdemo.mapper: DEBUG
    # SQL日志
    org.mybatis: DEBUG
    # Spring事务日志
    org.springframework.transaction: DEBUG
    # HikariCP日志
    com.zaxxer.hikari: DEBUG
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
2.2.2 多环境配置示例

application-dev.yml(开发环境)

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mybatis_demo_dev?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
    username: dev_user
    password: dev_password
    
mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

logging:
  level:
    root: DEBUG

application-prod.yml(生产环境)

spring:
  datasource:
    url: jdbc:mysql://prod-server:3306/mybatis_demo_prod?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=GMT%2B8
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}
    hikari:
      maximum-pool-size: 50
      minimum-idle: 10

mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl

logging:
  level:
    root: INFO
    com.example.mybatisdemo.mapper: WARN

2.3 MyBatis配置文件

2.3.1 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>
    <!-- 设置 -->
    <settings>
        <!-- 开启驼峰命名转换 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!-- 开启二级缓存 -->
        <setting name="cacheEnabled" value="true"/>
        <!-- 延迟加载的全局开关 -->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!-- 当开启时,任何方法的调用都会加载该对象的所有属性 -->
        <setting name="aggressiveLazyLoading" value="false"/>
        <!-- 是否允许单一语句返回多结果集 -->
        <setting name="multipleResultSetsEnabled" value="true"/>
        <!-- 使用列标签代替列名 -->
        <setting name="useColumnLabel" value="true"/>
        <!-- 允许JDBC自动生成主键 -->
        <setting name="useGeneratedKeys" value="true"/>
        <!-- 指定默认执行器 -->
        <setting name="defaultExecutorType" value="SIMPLE"/>
        <!-- 设置超时时间 -->
        <setting name="defaultStatementTimeout" value="25000"/>
        <!-- 设置查询返回值数量 -->
        <setting name="defaultFetchSize" value="100"/>
        <!-- 允许在嵌套语句中使用分页 -->
        <setting name="safeRowBoundsEnabled" value="false"/>
        <!-- 本地缓存机制 -->
        <setting name="localCacheScope" value="SESSION"/>
        <!-- 当没有为参数提供特定的JDBC类型时,为空值指定JDBC类型 -->
        <setting name="jdbcTypeForNull" value="OTHER"/>
        <!-- 指定哪个对象的方法触发一次延迟加载 -->
        <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
        <!-- 指定动态SQL中用于开启和关闭的关键字 -->
        <setting name="defaultScriptingLanguage" value="org.apache.ibatis.scripting.xmltags.XMLLanguageDriver"/>
        <!-- 当返回行的所有列都是空时,MyBatis默认返回null -->
        <setting name="callSettersOnNulls" value="false"/>
        <!-- 当返回行的所有列都是空时,MyBatis默认返回null -->
        <setting name="returnInstanceForEmptyRow" value="false"/>
        <!-- 指定MyBatis所用日志的具体实现 -->
        <setting name="logImpl" value="SLF4J"/>
    </settings>
    
    <!-- 类型别名 -->
    <typeAliases>
        <!-- 为包内的所有类注册别名 -->
        <package name="com.example.mybatisdemo.entity"/>
    </typeAliases>
    
    <!-- 类型处理器 -->
    <typeHandlers>
        <!-- 自定义类型处理器 -->
        <typeHandler handler="com.example.mybatisdemo.typehandler.JsonTypeHandler"/>
    </typeHandlers>
    
    <!-- 插件 -->
    <plugins>
        <!-- 分页插件 -->
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
            <property name="helperDialect" value="mysql"/>
            <property name="reasonable" value="true"/>
            <property name="supportMethodsArguments" value="true"/>
            <property name="params" value="count=countSql"/>
        </plugin>
    </plugins>
</configuration>

3. 实体类设计

3.1 基础实体类设计

3.1.1 User实体类
package com.example.mybatisdemo.entity;

import com.fasterxml.jackson.annotation.JsonFormat;
import javax.validation.constraints.*;
import java.time.LocalDateTime;
import java.util.List;

/**
 * 用户实体类
 */
public class User {
    
    /**
     * 用户ID
     */
    private Long id;
    
    /**
     * 用户名
     */
    @NotBlank(message = "用户名不能为空")
    @Size(min = 3, max = 20, message = "用户名长度必须在3-20个字符之间")
    private String username;
    
    /**
     * 密码
     */
    @NotBlank(message = "密码不能为空")
    @Size(min = 6, max = 20, message = "密码长度必须在6-20个字符之间")
    private String password;
    
    /**
     * 邮箱
     */
    @NotBlank(message = "邮箱不能为空")
    @Email(message = "邮箱格式不正确")
    private String email;
    
    /**
     * 手机号
     */
    @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
    private String phone;
    
    /**
     * 真实姓名
     */
    @Size(max = 20, message = "真实姓名长度不能超过20个字符")
    private String realName;
    
    /**
     * 年龄
     */
    @Min(value = 1, message = "年龄必须大于0")
    @Max(value = 150, message = "年龄不能超过150")
    private Integer age;
    
    /**
     * 性别:1-男,2-女,0-未知
     */
    private Integer gender;
    
    /**
     * 状态:1-正常,0-禁用
     */
    private Integer status;
    
    /**
     * 用户类型:1-管理员,2-普通用户
     */
    private Integer userType;
    
    /**
     * 头像URL
     */
    private String avatar;
    
    /**
     * 个人简介
     */
    @Size(max = 500, message = "个人简介不能超过500个字符")
    private String bio;
    
    /**
     * 创建时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;
    
    /**
     * 更新时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime updateTime;
    
    /**
     * 创建者ID
     */
    private Long createBy;
    
    /**
     * 更新者ID
     */
    private Long updateBy;
    
    /**
     * 用户角色列表(一对多关系)
     */
    private List<Role> roles;
    
    /**
     * 用户详细信息(一对一关系)
     */
    private UserProfile profile;
    
    // 构造方法
    public User() {}
    
    public User(String username, String password, String email) {
        this.username = username;
        this.password = password;
        this.email = email;
        this.status = 1; // 默认正常状态
        this.userType = 2; // 默认普通用户
        this.createTime = LocalDateTime.now();
        this.updateTime = LocalDateTime.now();
    }
    
    // Getter和Setter方法
    public Long getId() {
        return id;
    }
    
    public void setId(Long 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 String getPhone() {
        return phone;
    }
    
    public void setPhone(String phone) {
        this.phone = phone;
    }
    
    public String getRealName() {
        return realName;
    }
    
    public void setRealName(String realName) {
        this.realName = realName;
    }
    
    public Integer getAge() {
        return age;
    }
    
    public void setAge(Integer age) {
        this.age = age;
    }
    
    public Integer getGender() {
        return gender;
    }
    
    public void setGender(Integer gender) {
        this.gender = gender;
    }
    
    public Integer getStatus() {
        return status;
    }
    
    public void setStatus(Integer status) {
        this.status = status;
    }
    
    public Integer getUserType() {
        return userType;
    }
    
    public void setUserType(Integer userType) {
        this.userType = userType;
    }
    
    public String getAvatar() {
        return avatar;
    }
    
    public void setAvatar(String avatar) {
        this.avatar = avatar;
    }
    
    public String getBio() {
        return bio;
    }
    
    public void setBio(String bio) {
        this.bio = bio;
    }
    
    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;
    }
    
    public Long getCreateBy() {
        return createBy;
    }
    
    public void setCreateBy(Long createBy) {
        this.createBy = createBy;
    }
    
    public Long getUpdateBy() {
        return updateBy;
    }
    
    public void setUpdateBy(Long updateBy) {
        this.updateBy = updateBy;
    }
    
    public List<Role> getRoles() {
        return roles;
    }
    
    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }
    
    public UserProfile getProfile() {
        return profile;
    }
    
    public void setProfile(UserProfile profile) {
        this.profile = profile;
    }
    
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", email='" + email + '\'' +
                ", phone='" + phone + '\'' +
                ", realName='" + realName + '\'' +
                ", age=" + age +
                ", gender=" + gender +
                ", status=" + status +
                ", userType=" + userType +
                ", createTime=" + createTime +
                '}';
    }
}
3.1.2 Role实体类
package com.example.mybatisdemo.entity;

import com.fasterxml.jackson.annotation.JsonFormat;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import java.time.LocalDateTime;
import java.util.List;

/**
 * 角色实体类
 */
public class Role {
    
    /**
     * 角色ID
     */
    private Long id;
    
    /**
     * 角色名称
     */
    @NotBlank(message = "角色名称不能为空")
    @Size(max = 50, message = "角色名称长度不能超过50个字符")
    private String roleName;
    
    /**
     * 角色编码
     */
    @NotBlank(message = "角色编码不能为空")
    @Size(max = 20, message = "角色编码长度不能超过20个字符")
    private String roleCode;
    
    /**
     * 角色描述
     */
    @Size(max = 200, message = "角色描述不能超过200个字符")
    private String description;
    
    /**
     * 状态:1-正常,0-禁用
     */
    private Integer status;
    
    /**
     * 排序号
     */
    private Integer sortOrder;
    
    /**
     * 创建时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;
    
    /**
     * 更新时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime updateTime;
    
    /**
     * 权限列表(多对多关系)
     */
    private List<Permission> permissions;
    
    // 构造方法
    public Role() {}
    
    public Role(String roleName, String roleCode) {
        this.roleName = roleName;
        this.roleCode = roleCode;
        this.status = 1;
        this.createTime = LocalDateTime.now();
        this.updateTime = LocalDateTime.now();
    }
    
    // Getter和Setter方法
    public Long getId() {
        return id;
    }
    
    public void setId(Long id) {
        this.id = id;
    }
    
    public String getRoleName() {
        return roleName;
    }
    
    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }
    
    public String getRoleCode() {
        return roleCode;
    }
    
    public void setRoleCode(String roleCode) {
        this.roleCode = roleCode;
    }
    
    public String getDescription() {
        return description;
    }
    
    public void setDescription(String description) {
        this.description = description;
    }
    
    public Integer getStatus() {
        return status;
    }
    
    public void setStatus(Integer status) {
        this.status = status;
    }
    
    public Integer getSortOrder() {
        return sortOrder;
    }
    
    public void setSortOrder(Integer sortOrder) {
        this.sortOrder = sortOrder;
    }
    
    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;
    }
    
    public List<Permission> getPermissions() {
        return permissions;
    }
    
    public void setPermissions(List<Permission> permissions) {
        this.permissions = permissions;
    }
    
    @Override
    public String toString() {
        return "Role{" +
                "id=" + id +
                ", roleName='" + roleName + '\'' +
                ", roleCode='" + roleCode + '\'' +
                ", description='" + description + '\'' +
                ", status=" + status +
                ", createTime=" + createTime +
                '}';
    }
}
3.1.3 Permission实体类
package com.example.mybatisdemo.entity;

import com.fasterxml.jackson.annotation.JsonFormat;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import java.time.LocalDateTime;

/**
 * 权限实体类
 */
public class Permission {
    
    /**
     * 权限ID
     */
    private Long id;
    
    /**
     * 权限名称
     */
    @NotBlank(message = "权限名称不能为空")
    @Size(max = 50, message = "权限名称长度不能超过50个字符")
    private String permissionName;
    
    /**
     * 权限编码
     */
    @NotBlank(message = "权限编码不能为空")
    @Size(max = 50, message = "权限编码长度不能超过50个字符")
    private String permissionCode;
    
    /**
     * 权限类型:1-菜单,2-按钮,3-接口
     */
    private Integer permissionType;
    
    /**
     * 父权限ID
     */
    private Long parentId;
    
    /**
     * 权限路径
     */
    private String path;
    
    /**
     * 权限描述
     */
    @Size(max = 200, message = "权限描述不能超过200个字符")
    private String description;
    
    /**
     * 状态:1-正常,0-禁用
     */
    private Integer status;
    
    /**
     * 排序号
     */
    private Integer sortOrder;
    
    /**
     * 创建时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;
    
    /**
     * 更新时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime updateTime;
    
    // 构造方法
    public Permission() {}
    
    public Permission(String permissionName, String permissionCode, Integer permissionType) {
        this.permissionName = permissionName;
        this.permissionCode = permissionCode;
        this.permissionType = permissionType;
        this.status = 1;
        this.createTime = LocalDateTime.now();
        this.updateTime = LocalDateTime.now();
    }
    
    // Getter和Setter方法
    public Long getId() {
        return id;
    }
    
    public void setId(Long id) {
        this.id = id;
    }
    
    public String getPermissionName() {
        return permissionName;
    }
    
    public void setPermissionName(String permissionName) {
        this.permissionName = permissionName;
    }
    
    public String getPermissionCode() {
        return permissionCode;
    }
    
    public void setPermissionCode(String permissionCode) {
        this.permissionCode = permissionCode;
    }
    
    public Integer getPermissionType() {
        return permissionType;
    }
    
    public void setPermissionType(Integer permissionType) {
        this.permissionType = permissionType;
    }
    
    public Long getParentId() {
        return parentId;
    }
    
    public void setParentId(Long parentId) {
        this.parentId = parentId;
    }
    
    public String getPath() {
        return path;
    }
    
    public void setPath(String path) {
        this.path = path;
    }
    
    public String getDescription() {
        return description;
    }
    
    public void setDescription(String description) {
        this.description = description;
    }
    
    public Integer getStatus() {
        return status;
    }
    
    public void setStatus(Integer status) {
        this.status = status;
    }
    
    public Integer getSortOrder() {
        return sortOrder;
    }
    
    public void setSortOrder(Integer sortOrder) {
        this.sortOrder = sortOrder;
    }
    
    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 "Permission{" +
                "id=" + id +
                ", permissionName='" + permissionName + '\'' +
                ", permissionCode='" + permissionCode + '\'' +
                ", permissionType=" + permissionType +
                ", status=" + status +
                ", createTime=" + createTime +
                '}';
    }
}
3.1.4 UserProfile实体类
package com.example.mybatisdemo.entity;

import com.fasterxml.jackson.annotation.JsonFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;

/**
 * 用户详细信息实体类
 */
public class UserProfile {
    
    /**
     * 主键ID
     */
    private Long id;
    
    /**
     * 用户ID(外键)
     */
    private Long userId;
    
    /**
     * 昵称
     */
    private String nickname;
    
    /**
     * 生日
     */
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate birthday;
    
    /**
     * 地址
     */
    private String address;
    
    /**
     * 省份
     */
    private String province;
    
    /**
     * 城市
     */
    private String city;
    
    /**
     * 区县
     */
    private String district;
    
    /**
     * 邮政编码
     */
    private String zipCode;
    
    /**
     * 个人网站
     */
    private String website;
    
    /**
     * QQ号码
     */
    private String qq;
    
    /**
     * 微信号
     */
    private String wechat;
    
    /**
     * 微博
     */
    private String weibo;
    
    /**
     * 职业
     */
    private String profession;
    
    /**
     * 公司
     */
    private String company;
    
    /**
     * 学历
     */
    private String education;
    
    /**
     * 毕业学校
     */
    private String school;
    
    /**
     * 兴趣爱好
     */
    private String hobbies;
    
    /**
     * 创建时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;
    
    /**
     * 更新时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime updateTime;
    
    // 构造方法
    public UserProfile() {}
    
    public UserProfile(Long userId) {
        this.userId = userId;
        this.createTime = LocalDateTime.now();
        this.updateTime = LocalDateTime.now();
    }
    
    // Getter和Setter方法(省略部分,只展示几个关键的)
    public Long getId() {
        return id;
    }
    
    public void setId(Long id) {
        this.id = id;
    }
    
    public Long getUserId() {
        return userId;
    }
    
    public void setUserId(Long userId) {
        this.userId = userId;
    }
    
    public String getNickname() {
        return nickname;
    }
    
    public void setNickname(String nickname) {
        this.nickname = nickname;
    }
    
    public LocalDate getBirthday() {
        return birthday;
    }
    
    public void setBirthday(LocalDate birthday) {
        this.birthday = birthday;
    }
    
    // ... 其他getter和setter方法
    
    @Override
    public String toString() {
        return "UserProfile{" +
                "id=" + id +
                ", userId=" + userId +
                ", nickname='" + nickname + '\'' +
                ", birthday=" + birthday +
                ", address='" + address + '\'' +
                ", profession='" + profession + '\'' +
                ", company='" + company + '\'' +
                '}';
    }
} 

3.2 数据库表结构设计

3.2.1 用户表(t_user)
CREATE TABLE `t_user` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID',
  `username` varchar(20) NOT NULL COMMENT '用户名',
  `password` varchar(100) NOT NULL COMMENT '密码',
  `email` varchar(50) NOT NULL COMMENT '邮箱',
  `phone` varchar(11) DEFAULT NULL COMMENT '手机号',
  `real_name` varchar(20) DEFAULT NULL COMMENT '真实姓名',
  `age` int DEFAULT NULL COMMENT '年龄',
  `gender` tinyint DEFAULT '0' COMMENT '性别:1-男,2-女,0-未知',
  `status` tinyint DEFAULT '1' COMMENT '状态:1-正常,0-禁用',
  `user_type` tinyint DEFAULT '2' COMMENT '用户类型:1-管理员,2-普通用户',
  `avatar` varchar(200) DEFAULT NULL COMMENT '头像URL',
  `bio` varchar(500) DEFAULT NULL COMMENT '个人简介',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `create_by` bigint DEFAULT NULL COMMENT '创建者ID',
  `update_by` bigint DEFAULT NULL COMMENT '更新者ID',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_username` (`username`),
  UNIQUE KEY `uk_email` (`email`),
  KEY `idx_status` (`status`),
  KEY `idx_user_type` (`user_type`),
  KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
3.2.2 角色表(t_role)
CREATE TABLE `t_role` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '角色ID',
  `role_name` varchar(50) NOT NULL COMMENT '角色名称',
  `role_code` varchar(20) NOT NULL COMMENT '角色编码',
  `description` varchar(200) DEFAULT NULL COMMENT '角色描述',
  `status` tinyint DEFAULT '1' COMMENT '状态:1-正常,0-禁用',
  `sort_order` int DEFAULT '0' COMMENT '排序号',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_role_code` (`role_code`),
  KEY `idx_status` (`status`),
  KEY `idx_sort_order` (`sort_order`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色表';
3.2.3 权限表(t_permission)
CREATE TABLE `t_permission` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '权限ID',
  `permission_name` varchar(50) NOT NULL COMMENT '权限名称',
  `permission_code` varchar(50) NOT NULL COMMENT '权限编码',
  `permission_type` tinyint NOT NULL COMMENT '权限类型:1-菜单,2-按钮,3-接口',
  `parent_id` bigint DEFAULT '0' COMMENT '父权限ID',
  `path` varchar(200) DEFAULT NULL COMMENT '权限路径',
  `description` varchar(200) DEFAULT NULL COMMENT '权限描述',
  `status` tinyint DEFAULT '1' COMMENT '状态:1-正常,0-禁用',
  `sort_order` int DEFAULT '0' COMMENT '排序号',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_permission_code` (`permission_code`),
  KEY `idx_parent_id` (`parent_id`),
  KEY `idx_permission_type` (`permission_type`),
  KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='权限表';
3.2.4 用户角色关联表(t_user_role)
CREATE TABLE `t_user_role` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `user_id` bigint NOT NULL COMMENT '用户ID',
  `role_id` bigint NOT NULL COMMENT '角色ID',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_user_role` (`user_id`,`role_id`),
  KEY `idx_user_id` (`user_id`),
  KEY `idx_role_id` (`role_id`),
  CONSTRAINT `fk_user_role_user` FOREIGN KEY (`user_id`) REFERENCES `t_user` (`id`) ON DELETE CASCADE,
  CONSTRAINT `fk_user_role_role` FOREIGN KEY (`role_id`) REFERENCES `t_role` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户角色关联表';
3.2.5 角色权限关联表(t_role_permission)
CREATE TABLE `t_role_permission` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `role_id` bigint NOT NULL COMMENT '角色ID',
  `permission_id` bigint NOT NULL COMMENT '权限ID',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_role_permission` (`role_id`,`permission_id`),
  KEY `idx_role_id` (`role_id`),
  KEY `idx_permission_id` (`permission_id`),
  CONSTRAINT `fk_role_permission_role` FOREIGN KEY (`role_id`) REFERENCES `t_role` (`id`) ON DELETE CASCADE,
  CONSTRAINT `fk_role_permission_permission` FOREIGN KEY (`permission_id`) REFERENCES `t_permission` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色权限关联表';
3.2.6 用户详细信息表(t_user_profile)
CREATE TABLE `t_user_profile` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `user_id` bigint NOT NULL COMMENT '用户ID',
  `nickname` varchar(50) DEFAULT NULL COMMENT '昵称',
  `birthday` date DEFAULT NULL COMMENT '生日',
  `address` varchar(200) DEFAULT NULL COMMENT '地址',
  `province` varchar(20) DEFAULT NULL COMMENT '省份',
  `city` varchar(20) DEFAULT NULL COMMENT '城市',
  `district` varchar(20) DEFAULT NULL COMMENT '区县',
  `zip_code` varchar(10) DEFAULT NULL COMMENT '邮政编码',
  `website` varchar(100) DEFAULT NULL COMMENT '个人网站',
  `qq` varchar(20) DEFAULT NULL COMMENT 'QQ号码',
  `wechat` varchar(50) DEFAULT NULL COMMENT '微信号',
  `weibo` varchar(50) DEFAULT NULL COMMENT '微博',
  `profession` varchar(50) DEFAULT NULL COMMENT '职业',
  `company` varchar(100) DEFAULT NULL COMMENT '公司',
  `education` varchar(20) DEFAULT NULL COMMENT '学历',
  `school` varchar(100) DEFAULT NULL COMMENT '毕业学校',
  `hobbies` varchar(200) DEFAULT NULL COMMENT '兴趣爱好',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_user_id` (`user_id`),
  CONSTRAINT `fk_user_profile_user` FOREIGN KEY (`user_id`) REFERENCES `t_user` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户详细信息表';

4. Mapper接口设计

4.1 基础CRUD操作

4.1.1 UserMapper接口
package com.example.mybatisdemo.mapper;

import com.example.mybatisdemo.entity.User;
import org.apache.ibatis.annotations.*;
import org.apache.ibatis.mapping.FetchType;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Map;

/**
 * 用户Mapper接口
 */
@Repository
@Mapper
public interface UserMapper {
    
    /**
     * 根据ID查询用户
     * @param id 用户ID
     * @return 用户信息
     */
    User selectById(Long id);
    
    /**
     * 根据用户名查询用户
     * @param username 用户名
     * @return 用户信息
     */
    User selectByUsername(String username);
    
    /**
     * 根据邮箱查询用户
     * @param email 邮箱
     * @return 用户信息
     */
    User selectByEmail(String email);
    
    /**
     * 查询所有用户
     * @return 用户列表
     */
    List<User> selectAll();
    
    /**
     * 分页查询用户
     * @param offset 偏移量
     * @param limit 限制数量
     * @return 用户列表
     */
    List<User> selectByPage(@Param("offset") int offset, @Param("limit") int limit);
    
    /**
     * 根据条件查询用户
     * @param conditions 查询条件
     * @return 用户列表
     */
    List<User> selectByConditions(Map<String, Object> conditions);
    
    /**
     * 查询用户总数
     * @return 用户总数
     */
    long countAll();
    
    /**
     * 根据条件查询用户数量
     * @param conditions 查询条件
     * @return 用户数量
     */
    long countByConditions(Map<String, Object> conditions);
    
    /**
     * 插入用户
     * @param user 用户信息
     * @return 影响行数
     */
    int insert(User user);
    
    /**
     * 批量插入用户
     * @param users 用户列表
     * @return 影响行数
     */
    int insertBatch(List<User> users);
    
    /**
     * 更新用户
     * @param user 用户信息
     * @return 影响行数
     */
    int update(User user);
    
    /**
     * 选择性更新用户
     * @param user 用户信息
     * @return 影响行数
     */
    int updateSelective(User user);
    
    /**
     * 批量更新用户状态
     * @param ids 用户ID列表
     * @param status 状态
     * @return 影响行数
     */
    int updateStatusBatch(@Param("ids") List<Long> ids, @Param("status") Integer status);
    
    /**
     * 根据ID删除用户
     * @param id 用户ID
     * @return 影响行数
     */
    int deleteById(Long id);
    
    /**
     * 批量删除用户
     * @param ids 用户ID列表
     * @return 影响行数
     */
    int deleteBatch(List<Long> ids);
    
    /**
     * 逻辑删除用户(更新状态为禁用)
     * @param id 用户ID
     * @return 影响行数
     */
    int logicDeleteById(Long id);
    
    /**
     * 查询用户及其角色信息
     * @param id 用户ID
     * @return 用户信息(包含角色)
     */
    User selectWithRoles(Long id);
    
    /**
     * 查询用户及其详细信息
     * @param id 用户ID
     * @return 用户信息(包含详细信息)
     */
    User selectWithProfile(Long id);
    
    /**
     * 查询用户完整信息(包含角色和详细信息)
     * @param id 用户ID
     * @return 用户完整信息
     */
    User selectFullInfo(Long id);
    
    /**
     * 根据角色ID查询用户列表
     * @param roleId 角色ID
     * @return 用户列表
     */
    List<User> selectByRoleId(Long roleId);
    
    /**
     * 模糊查询用户
     * @param keyword 关键词
     * @return 用户列表
     */
    List<User> selectByKeyword(String keyword);
    
    /**
     * 根据年龄范围查询用户
     * @param minAge 最小年龄
     * @param maxAge 最大年龄
     * @return 用户列表
     */
    List<User> selectByAgeRange(@Param("minAge") Integer minAge, @Param("maxAge") Integer maxAge);
    
    /**
     * 查询指定日期后注册的用户
     * @param date 日期
     * @return 用户列表
     */
    List<User> selectByCreateTimeAfter(String date);
    
    /**
     * 检查用户名是否存在
     * @param username 用户名
     * @return 是否存在
     */
    @Select("SELECT COUNT(1) FROM t_user WHERE username = #{username}")
    boolean existsByUsername(String username);
    
    /**
     * 检查邮箱是否存在
     * @param email 邮箱
     * @return 是否存在
     */
    @Select("SELECT COUNT(1) FROM t_user WHERE email = #{email}")
    boolean existsByEmail(String email);
    
    /**
     * 使用注解方式查询用户基本信息
     * @param id 用户ID
     * @return 用户信息
     */
    @Select("SELECT id, username, email, phone, real_name, age, gender, status, user_type, " +
            "avatar, bio, create_time, update_time FROM t_user WHERE id = #{id}")
    @Results(id = "userResultMap", value = {
        @Result(property = "id", column = "id", id = true),
        @Result(property = "username", column = "username"),
        @Result(property = "email", column = "email"),
        @Result(property = "phone", column = "phone"),
        @Result(property = "realName", column = "real_name"),
        @Result(property = "age", column = "age"),
        @Result(property = "gender", column = "gender"),
        @Result(property = "status", column = "status"),
        @Result(property = "userType", column = "user_type"),
        @Result(property = "avatar", column = "avatar"),
        @Result(property = "bio", column = "bio"),
        @Result(property = "createTime", column = "create_time"),
        @Result(property = "updateTime", column = "update_time")
    })
    User selectByIdWithAnnotation(Long id);
    
    /**
     * 使用注解方式插入用户
     * @param user 用户信息
     * @return 影响行数
     */
    @Insert("INSERT INTO t_user(username, password, email, phone, real_name, age, gender, " +
            "status, user_type, avatar, bio, create_time, update_time) " +
            "VALUES(#{username}, #{password}, #{email}, #{phone}, #{realName}, #{age}, " +
            "#{gender}, #{status}, #{userType}, #{avatar}, #{bio}, #{createTime}, #{updateTime})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    int insertWithAnnotation(User user);
    
    /**
     * 使用注解方式更新用户
     * @param user 用户信息
     * @return 影响行数
     */
    @Update("UPDATE t_user SET username = #{username}, email = #{email}, phone = #{phone}, " +
            "real_name = #{realName}, age = #{age}, gender = #{gender}, status = #{status}, " +
            "user_type = #{userType}, avatar = #{avatar}, bio = #{bio}, update_time = #{updateTime} " +
            "WHERE id = #{id}")
    int updateWithAnnotation(User user);
    
    /**
     * 使用注解方式删除用户
     * @param id 用户ID
     * @return 影响行数
     */
    @Delete("DELETE FROM t_user WHERE id = #{id}")
    int deleteByIdWithAnnotation(Long id);
}
4.1.2 RoleMapper接口
package com.example.mybatisdemo.mapper;

import com.example.mybatisdemo.entity.Role;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Map;

/**
 * 角色Mapper接口
 */
@Repository
@Mapper
public interface RoleMapper {
    
    /**
     * 根据ID查询角色
     * @param id 角色ID
     * @return 角色信息
     */
    Role selectById(Long id);
    
    /**
     * 根据角色编码查询角色
     * @param roleCode 角色编码
     * @return 角色信息
     */
    Role selectByRoleCode(String roleCode);
    
    /**
     * 查询所有角色
     * @return 角色列表
     */
    List<Role> selectAll();
    
    /**
     * 根据状态查询角色
     * @param status 状态
     * @return 角色列表
     */
    List<Role> selectByStatus(Integer status);
    
    /**
     * 根据条件查询角色
     * @param conditions 查询条件
     * @return 角色列表
     */
    List<Role> selectByConditions(Map<String, Object> conditions);
    
    /**
     * 查询角色总数
     * @return 角色总数
     */
    long countAll();
    
    /**
     * 插入角色
     * @param role 角色信息
     * @return 影响行数
     */
    int insert(Role role);
    
    /**
     * 更新角色
     * @param role 角色信息
     * @return 影响行数
     */
    int update(Role role);
    
    /**
     * 选择性更新角色
     * @param role 角色信息
     * @return 影响行数
     */
    int updateSelective(Role role);
    
    /**
     * 根据ID删除角色
     * @param id 角色ID
     * @return 影响行数
     */
    int deleteById(Long id);
    
    /**
     * 批量删除角色
     * @param ids 角色ID列表
     * @return 影响行数
     */
    int deleteBatch(List<Long> ids);
    
    /**
     * 查询角色及其权限信息
     * @param id 角色ID
     * @return 角色信息(包含权限)
     */
    Role selectWithPermissions(Long id);
    
    /**
     * 根据用户ID查询角色列表
     * @param userId 用户ID
     * @return 角色列表
     */
    List<Role> selectByUserId(Long userId);
    
    /**
     * 检查角色编码是否存在
     * @param roleCode 角色编码
     * @return 是否存在
     */
    boolean existsByRoleCode(String roleCode);
}
4.1.3 PermissionMapper接口
package com.example.mybatisdemo.mapper;

import com.example.mybatisdemo.entity.Permission;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Map;

/**
 * 权限Mapper接口
 */
@Repository
@Mapper
public interface PermissionMapper {
    
    /**
     * 根据ID查询权限
     * @param id 权限ID
     * @return 权限信息
     */
    Permission selectById(Long id);
    
    /**
     * 根据权限编码查询权限
     * @param permissionCode 权限编码
     * @return 权限信息
     */
    Permission selectByPermissionCode(String permissionCode);
    
    /**
     * 查询所有权限
     * @return 权限列表
     */
    List<Permission> selectAll();
    
    /**
     * 根据权限类型查询权限
     * @param permissionType 权限类型
     * @return 权限列表
     */
    List<Permission> selectByType(Integer permissionType);
    
    /**
     * 根据父权限ID查询子权限
     * @param parentId 父权限ID
     * @return 权限列表
     */
    List<Permission> selectByParentId(Long parentId);
    
    /**
     * 查询顶级权限(父权限ID为0或null)
     * @return 权限列表
     */
    List<Permission> selectTopLevel();
    
    /**
     * 根据条件查询权限
     * @param conditions 查询条件
     * @return 权限列表
     */
    List<Permission> selectByConditions(Map<String, Object> conditions);
    
    /**
     * 查询权限总数
     * @return 权限总数
     */
    long countAll();
    
    /**
     * 插入权限
     * @param permission 权限信息
     * @return 影响行数
     */
    int insert(Permission permission);
    
    /**
     * 更新权限
     * @param permission 权限信息
     * @return 影响行数
     */
    int update(Permission permission);
    
    /**
     * 选择性更新权限
     * @param permission 权限信息
     * @return 影响行数
     */
    int updateSelective(Permission permission);
    
    /**
     * 根据ID删除权限
     * @param id 权限ID
     * @return 影响行数
     */
    int deleteById(Long id);
    
    /**
     * 批量删除权限
     * @param ids 权限ID列表
     * @return 影响行数
     */
    int deleteBatch(List<Long> ids);
    
    /**
     * 根据角色ID查询权限列表
     * @param roleId 角色ID
     * @return 权限列表
     */
    List<Permission> selectByRoleId(Long roleId);
    
    /**
     * 根据用户ID查询权限列表
     * @param userId 用户ID
     * @return 权限列表
     */
    List<Permission> selectByUserId(Long userId);
    
    /**
     * 检查权限编码是否存在
     * @param permissionCode 权限编码
     * @return 是否存在
     */
    boolean existsByPermissionCode(String permissionCode);
}
4.1.4 UserRoleMapper接口
package com.example.mybatisdemo.mapper;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * 用户角色关联Mapper接口
 */
@Repository
@Mapper
public interface UserRoleMapper {
    
    /**
     * 为用户分配角色
     * @param userId 用户ID
     * @param roleId 角色ID
     * @return 影响行数
     */
    int insert(@Param("userId") Long userId, @Param("roleId") Long roleId);
    
    /**
     * 批量为用户分配角色
     * @param userId 用户ID
     * @param roleIds 角色ID列表
     * @return 影响行数
     */
    int insertBatch(@Param("userId") Long userId, @Param("roleIds") List<Long> roleIds);
    
    /**
     * 删除用户角色关联
     * @param userId 用户ID
     * @param roleId 角色ID
     * @return 影响行数
     */
    int delete(@Param("userId") Long userId, @Param("roleId") Long roleId);
    
    /**
     * 删除用户的所有角色
     * @param userId 用户ID
     * @return 影响行数
     */
    int deleteByUserId(Long userId);
    
    /**
     * 删除角色的所有用户关联
     * @param roleId 角色ID
     * @return 影响行数
     */
    int deleteByRoleId(Long roleId);
    
    /**
     * 查询用户的角色ID列表
     * @param userId 用户ID
     * @return 角色ID列表
     */
    List<Long> selectRoleIdsByUserId(Long userId);
    
    /**
     * 查询角色的用户ID列表
     * @param roleId 角色ID
     * @return 用户ID列表
     */
    List<Long> selectUserIdsByRoleId(Long roleId);
    
    /**
     * 检查用户是否拥有指定角色
     * @param userId 用户ID
     * @param roleId 角色ID
     * @return 是否存在
     */
    boolean exists(@Param("userId") Long userId, @Param("roleId") Long roleId);
    
    /**
     * 统计用户的角色数量
     * @param userId 用户ID
     * @return 角色数量
     */
    int countByUserId(Long userId);
    
    /**
     * 统计角色的用户数量
     * @param roleId 角色ID
     * @return 用户数量
     */
    int countByRoleId(Long roleId);
}
4.1.5 UserProfileMapper接口
package com.example.mybatisdemo.mapper;

import com.example.mybatisdemo.entity.UserProfile;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

/**
 * 用户详细信息Mapper接口
 */
@Repository
@Mapper
public interface UserProfileMapper {
    
    /**
     * 根据ID查询用户详细信息
     * @param id 主键ID
     * @return 用户详细信息
     */
    UserProfile selectById(Long id);
    
    /**
     * 根据用户ID查询用户详细信息
     * @param userId 用户ID
     * @return 用户详细信息
     */
    UserProfile selectByUserId(Long userId);
    
    /**
     * 插入用户详细信息
     * @param userProfile 用户详细信息
     * @return 影响行数
     */
    int insert(UserProfile userProfile);
    
    /**
     * 更新用户详细信息
     * @param userProfile 用户详细信息
     * @return 影响行数
     */
    int update(UserProfile userProfile);
    
    /**
     * 选择性更新用户详细信息
     * @param userProfile 用户详细信息
     * @return 影响行数
     */
    int updateSelective(UserProfile userProfile);
    
    /**
     * 根据ID删除用户详细信息
     * @param id 主键ID
     * @return 影响行数
     */
    int deleteById(Long id);
    
    /**
     * 根据用户ID删除用户详细信息
     * @param userId 用户ID
     * @return 影响行数
     */
    int deleteByUserId(Long userId);
    
    /**
     * 检查用户是否已有详细信息
     * @param userId 用户ID
     * @return 是否存在
     */
    boolean existsByUserId(Long userId);
} 

5. XML映射文件详解

5.1 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.mybatisdemo.mapper.UserMapper">
    
    <!-- 结果映射 -->
    <resultMap id="BaseResultMap" type="User">
        <id column="id" property="id" jdbcType="BIGINT"/>
        <result column="username" property="username" jdbcType="VARCHAR"/>
        <result column="password" property="password" jdbcType="VARCHAR"/>
        <result column="email" property="email" jdbcType="VARCHAR"/>
        <result column="phone" property="phone" jdbcType="VARCHAR"/>
        <result column="real_name" property="realName" jdbcType="VARCHAR"/>
        <result column="age" property="age" jdbcType="INTEGER"/>
        <result column="gender" property="gender" jdbcType="TINYINT"/>
        <result column="status" property="status" jdbcType="TINYINT"/>
        <result column="user_type" property="userType" jdbcType="TINYINT"/>
        <result column="avatar" property="avatar" jdbcType="VARCHAR"/>
        <result column="bio" property="bio" jdbcType="VARCHAR"/>
        <result column="create_time" property="createTime" jdbcType="TIMESTAMP"/>
        <result column="update_time" property="updateTime" jdbcType="TIMESTAMP"/>
        <result column="create_by" property="createBy" jdbcType="BIGINT"/>
        <result column="update_by" property="updateBy" jdbcType="BIGINT"/>
    </resultMap>
    
    <!-- 用户与角色关联结果映射 -->
    <resultMap id="UserWithRolesResultMap" type="User" extends="BaseResultMap">
        <collection property="roles" ofType="Role" 
                   select="com.example.mybatisdemo.mapper.RoleMapper.selectByUserId" 
                   column="id" fetchType="lazy"/>
    </resultMap>
    
    <!-- 用户与详细信息关联结果映射 -->
    <resultMap id="UserWithProfileResultMap" type="User" extends="BaseResultMap">
        <association property="profile" javaType="UserProfile" 
                    select="com.example.mybatisdemo.mapper.UserProfileMapper.selectByUserId" 
                    column="id" fetchType="lazy"/>
    </resultMap>
    
    <!-- 用户完整信息结果映射 -->
    <resultMap id="UserFullInfoResultMap" type="User" extends="BaseResultMap">
        <collection property="roles" ofType="Role" 
                   select="com.example.mybatisdemo.mapper.RoleMapper.selectByUserId" 
                   column="id" fetchType="lazy"/>
        <association property="profile" javaType="UserProfile" 
                    select="com.example.mybatisdemo.mapper.UserProfileMapper.selectByUserId" 
                    column="id" fetchType="lazy"/>
    </resultMap>
    
    <!-- 基础列名 -->
    <sql id="Base_Column_List">
        id, username, password, email, phone, real_name, age, gender, 
        status, user_type, avatar, bio, create_time, update_time, create_by, update_by
    </sql>
    
    <!-- 查询条件 -->
    <sql id="Base_Where_Clause">
        <where>
            <if test="id != null">
                AND id = #{id}
            </if>
            <if test="username != null and username != ''">
                AND username = #{username}
            </if>
            <if test="email != null and email != ''">
                AND email = #{email}
            </if>
            <if test="phone != null and phone != ''">
                AND phone = #{phone}
            </if>
            <if test="realName != null and realName != ''">
                AND real_name LIKE CONCAT('%', #{realName}, '%')
            </if>
            <if test="age != null">
                AND age = #{age}
            </if>
            <if test="gender != null">
                AND gender = #{gender}
            </if>
            <if test="status != null">
                AND status = #{status}
            </if>
            <if test="userType != null">
                AND user_type = #{userType}
            </if>
            <if test="minAge != null">
                AND age >= #{minAge}
            </if>
            <if test="maxAge != null">
                AND age <= #{maxAge}
            </if>
            <if test="createTimeStart != null">
                AND create_time >= #{createTimeStart}
            </if>
            <if test="createTimeEnd != null">
                AND create_time <= #{createTimeEnd}
            </if>
            <if test="keyword != null and keyword != ''">
                AND (username LIKE CONCAT('%', #{keyword}, '%') 
                    OR real_name LIKE CONCAT('%', #{keyword}, '%')
                    OR email LIKE CONCAT('%', #{keyword}, '%')
                    OR phone LIKE CONCAT('%', #{keyword}, '%'))
            </if>
        </where>
    </sql>
    
    <!-- 根据ID查询用户 -->
    <select id="selectById" parameterType="Long" resultMap="BaseResultMap">
        SELECT 
        <include refid="Base_Column_List"/>
        FROM t_user 
        WHERE id = #{id}
    </select>
    
    <!-- 根据用户名查询用户 -->
    <select id="selectByUsername" parameterType="String" resultMap="BaseResultMap">
        SELECT 
        <include refid="Base_Column_List"/>
        FROM t_user 
        WHERE username = #{username}
    </select>
    
    <!-- 根据邮箱查询用户 -->
    <select id="selectByEmail" parameterType="String" resultMap="BaseResultMap">
        SELECT 
        <include refid="Base_Column_List"/>
        FROM t_user 
        WHERE email = #{email}
    </select>
    
    <!-- 查询所有用户 -->
    <select id="selectAll" resultMap="BaseResultMap">
        SELECT 
        <include refid="Base_Column_List"/>
        FROM t_user 
        ORDER BY create_time DESC
    </select>
    
    <!-- 分页查询用户 -->
    <select id="selectByPage" resultMap="BaseResultMap">
        SELECT 
        <include refid="Base_Column_List"/>
        FROM t_user 
        ORDER BY create_time DESC
        LIMIT #{offset}, #{limit}
    </select>
    
    <!-- 根据条件查询用户 -->
    <select id="selectByConditions" parameterType="Map" resultMap="BaseResultMap">
        SELECT 
        <include refid="Base_Column_List"/>
        FROM t_user
        <include refid="Base_Where_Clause"/>
        ORDER BY create_time DESC
    </select>
    
    <!-- 查询用户总数 -->
    <select id="countAll" resultType="long">
        SELECT COUNT(1) FROM t_user
    </select>
    
    <!-- 根据条件查询用户数量 -->
    <select id="countByConditions" parameterType="Map" resultType="long">
        SELECT COUNT(1) FROM t_user
        <include refid="Base_Where_Clause"/>
    </select>
    
    <!-- 插入用户 -->
    <insert id="insert" parameterType="User" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO t_user (
            username, password, email, phone, real_name, age, gender, 
            status, user_type, avatar, bio, create_time, update_time, create_by, update_by
        ) VALUES (
            #{username}, #{password}, #{email}, #{phone}, #{realName}, #{age}, #{gender},
            #{status}, #{userType}, #{avatar}, #{bio}, #{createTime}, #{updateTime}, #{createBy}, #{updateBy}
        )
    </insert>
    
    <!-- 批量插入用户 -->
    <insert id="insertBatch" parameterType="List" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO t_user (
            username, password, email, phone, real_name, age, gender, 
            status, user_type, avatar, bio, create_time, update_time, create_by, update_by
        ) VALUES
        <foreach collection="list" item="user" separator=",">
            (#{user.username}, #{user.password}, #{user.email}, #{user.phone}, #{user.realName}, 
             #{user.age}, #{user.gender}, #{user.status}, #{user.userType}, #{user.avatar}, 
             #{user.bio}, #{user.createTime}, #{user.updateTime}, #{user.createBy}, #{user.updateBy})
        </foreach>
    </insert>
    
    <!-- 更新用户 -->
    <update id="update" parameterType="User">
        UPDATE t_user SET
            username = #{username},
            password = #{password},
            email = #{email},
            phone = #{phone},
            real_name = #{realName},
            age = #{age},
            gender = #{gender},
            status = #{status},
            user_type = #{userType},
            avatar = #{avatar},
            bio = #{bio},
            update_time = #{updateTime},
            update_by = #{updateBy}
        WHERE id = #{id}
    </update>
    
    <!-- 选择性更新用户 -->
    <update id="updateSelective" parameterType="User">
        UPDATE t_user 
        <set>
            <if test="username != null and username != ''">
                username = #{username},
            </if>
            <if test="password != null and password != ''">
                password = #{password},
            </if>
            <if test="email != null and email != ''">
                email = #{email},
            </if>
            <if test="phone != null and phone != ''">
                phone = #{phone},
            </if>
            <if test="realName != null and realName != ''">
                real_name = #{realName},
            </if>
            <if test="age != null">
                age = #{age},
            </if>
            <if test="gender != null">
                gender = #{gender},
            </if>
            <if test="status != null">
                status = #{status},
            </if>
            <if test="userType != null">
                user_type = #{userType},
            </if>
            <if test="avatar != null">
                avatar = #{avatar},
            </if>
            <if test="bio != null">
                bio = #{bio},
            </if>
            <if test="updateTime != null">
                update_time = #{updateTime},
            </if>
            <if test="updateBy != null">
                update_by = #{updateBy},
            </if>
        </set>
        WHERE id = #{id}
    </update>
    
    <!-- 批量更新用户状态 -->
    <update id="updateStatusBatch">
        UPDATE t_user SET status = #{status}, update_time = NOW()
        WHERE id IN
        <foreach collection="ids" item="id" open="(" separator="," close=")">
            #{id}
        </foreach>
    </update>
    
    <!-- 根据ID删除用户 -->
    <delete id="deleteById" parameterType="Long">
        DELETE FROM t_user WHERE id = #{id}
    </delete>
    
    <!-- 批量删除用户 -->
    <delete id="deleteBatch" parameterType="List">
        DELETE FROM t_user WHERE id IN
        <foreach collection="list" item="id" open="(" separator="," close=")">
            #{id}
        </foreach>
    </delete>
    
    <!-- 逻辑删除用户 -->
    <update id="logicDeleteById" parameterType="Long">
        UPDATE t_user SET status = 0, update_time = NOW() WHERE id = #{id}
    </update>
    
    <!-- 查询用户及其角色信息 -->
    <select id="selectWithRoles" parameterType="Long" resultMap="UserWithRolesResultMap">
        SELECT 
        <include refid="Base_Column_List"/>
        FROM t_user 
        WHERE id = #{id}
    </select>
    
    <!-- 查询用户及其详细信息 -->
    <select id="selectWithProfile" parameterType="Long" resultMap="UserWithProfileResultMap">
        SELECT 
        <include refid="Base_Column_List"/>
        FROM t_user 
        WHERE id = #{id}
    </select>
    
    <!-- 查询用户完整信息 -->
    <select id="selectFullInfo" parameterType="Long" resultMap="UserFullInfoResultMap">
        SELECT 
        <include refid="Base_Column_List"/>
        FROM t_user 
        WHERE id = #{id}
    </select>
    
    <!-- 根据角色ID查询用户列表 -->
    <select id="selectByRoleId" parameterType="Long" resultMap="BaseResultMap">
        SELECT 
        <include refid="Base_Column_List"/>
        FROM t_user u
        INNER JOIN t_user_role ur ON u.id = ur.user_id
        WHERE ur.role_id = #{roleId}
        ORDER BY u.create_time DESC
    </select>
    
    <!-- 模糊查询用户 -->
    <select id="selectByKeyword" parameterType="String" resultMap="BaseResultMap">
        SELECT 
        <include refid="Base_Column_List"/>
        FROM t_user 
        WHERE username LIKE CONCAT('%', #{keyword}, '%')
           OR real_name LIKE CONCAT('%', #{keyword}, '%')
           OR email LIKE CONCAT('%', #{keyword}, '%')
           OR phone LIKE CONCAT('%', #{keyword}, '%')
        ORDER BY create_time DESC
    </select>
    
    <!-- 根据年龄范围查询用户 -->
    <select id="selectByAgeRange" resultMap="BaseResultMap">
        SELECT 
        <include refid="Base_Column_List"/>
        FROM t_user 
        WHERE age BETWEEN #{minAge} AND #{maxAge}
        ORDER BY age ASC, create_time DESC
    </select>
    
    <!-- 查询指定日期后注册的用户 -->
    <select id="selectByCreateTimeAfter" parameterType="String" resultMap="BaseResultMap">
        SELECT 
        <include refid="Base_Column_List"/>
        FROM t_user 
        WHERE create_time >= #{date}
        ORDER BY create_time DESC
    </select>
</mapper>

5.2 RoleMapper.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.mybatisdemo.mapper.RoleMapper">
    
    <!-- 结果映射 -->
    <resultMap id="BaseResultMap" type="Role">
        <id column="id" property="id" jdbcType="BIGINT"/>
        <result column="role_name" property="roleName" jdbcType="VARCHAR"/>
        <result column="role_code" property="roleCode" jdbcType="VARCHAR"/>
        <result column="description" property="description" jdbcType="VARCHAR"/>
        <result column="status" property="status" jdbcType="TINYINT"/>
        <result column="sort_order" property="sortOrder" jdbcType="INTEGER"/>
        <result column="create_time" property="createTime" jdbcType="TIMESTAMP"/>
        <result column="update_time" property="updateTime" jdbcType="TIMESTAMP"/>
    </resultMap>
    
    <!-- 角色与权限关联结果映射 -->
    <resultMap id="RoleWithPermissionsResultMap" type="Role" extends="BaseResultMap">
        <collection property="permissions" ofType="Permission" 
                   select="com.example.mybatisdemo.mapper.PermissionMapper.selectByRoleId" 
                   column="id" fetchType="lazy"/>
    </resultMap>
    
    <!-- 基础列名 -->
    <sql id="Base_Column_List">
        id, role_name, role_code, description, status, sort_order, create_time, update_time
    </sql>
    
    <!-- 查询条件 -->
    <sql id="Base_Where_Clause">
        <where>
            <if test="id != null">
                AND id = #{id}
            </if>
            <if test="roleName != null and roleName != ''">
                AND role_name LIKE CONCAT('%', #{roleName}, '%')
            </if>
            <if test="roleCode != null and roleCode != ''">
                AND role_code = #{roleCode}
            </if>
            <if test="status != null">
                AND status = #{status}
            </if>
            <if test="keyword != null and keyword != ''">
                AND (role_name LIKE CONCAT('%', #{keyword}, '%') 
                    OR role_code LIKE CONCAT('%', #{keyword}, '%')
                    OR description LIKE CONCAT('%', #{keyword}, '%'))
            </if>
        </where>
    </sql>
    
    <!-- 根据ID查询角色 -->
    <select id="selectById" parameterType="Long" resultMap="BaseResultMap">
        SELECT 
        <include refid="Base_Column_List"/>
        FROM t_role 
        WHERE id = #{id}
    </select>
    
    <!-- 根据角色编码查询角色 -->
    <select id="selectByRoleCode" parameterType="String" resultMap="BaseResultMap">
        SELECT 
        <include refid="Base_Column_List"/>
        FROM t_role 
        WHERE role_code = #{roleCode}
    </select>
    
    <!-- 查询所有角色 -->
    <select id="selectAll" resultMap="BaseResultMap">
        SELECT 
        <include refid="Base_Column_List"/>
        FROM t_role 
        ORDER BY sort_order ASC, create_time DESC
    </select>
    
    <!-- 根据状态查询角色 -->
    <select id="selectByStatus" parameterType="Integer" resultMap="BaseResultMap">
        SELECT 
        <include refid="Base_Column_List"/>
        FROM t_role 
        WHERE status = #{status}
        ORDER BY sort_order ASC, create_time DESC
    </select>
    
    <!-- 根据条件查询角色 -->
    <select id="selectByConditions" parameterType="Map" resultMap="BaseResultMap">
        SELECT 
        <include refid="Base_Column_List"/>
        FROM t_role
        <include refid="Base_Where_Clause"/>
        ORDER BY sort_order ASC, create_time DESC
    </select>
    
    <!-- 查询角色总数 -->
    <select id="countAll" resultType="long">
        SELECT COUNT(1) FROM t_role
    </select>
    
    <!-- 插入角色 -->
    <insert id="insert" parameterType="Role" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO t_role (
            role_name, role_code, description, status, sort_order, create_time, update_time
        ) VALUES (
            #{roleName}, #{roleCode}, #{description}, #{status}, #{sortOrder}, #{createTime}, #{updateTime}
        )
    </insert>
    
    <!-- 更新角色 -->
    <update id="update" parameterType="Role">
        UPDATE t_role SET
            role_name = #{roleName},
            role_code = #{roleCode},
            description = #{description},
            status = #{status},
            sort_order = #{sortOrder},
            update_time = #{updateTime}
        WHERE id = #{id}
    </update>
    
    <!-- 选择性更新角色 -->
    <update id="updateSelective" parameterType="Role">
        UPDATE t_role 
        <set>
            <if test="roleName != null and roleName != ''">
                role_name = #{roleName},
            </if>
            <if test="roleCode != null and roleCode != ''">
                role_code = #{roleCode},
            </if>
            <if test="description != null">
                description = #{description},
            </if>
            <if test="status != null">
                status = #{status},
            </if>
            <if test="sortOrder != null">
                sort_order = #{sortOrder},
            </if>
            <if test="updateTime != null">
                update_time = #{updateTime},
            </if>
        </set>
        WHERE id = #{id}
    </update>
    
    <!-- 根据ID删除角色 -->
    <delete id="deleteById" parameterType="Long">
        DELETE FROM t_role WHERE id = #{id}
    </delete>
    
    <!-- 批量删除角色 -->
    <delete id="deleteBatch" parameterType="List">
        DELETE FROM t_role WHERE id IN
        <foreach collection="list" item="id" open="(" separator="," close=")">
            #{id}
        </foreach>
    </delete>
    
    <!-- 查询角色及其权限信息 -->
    <select id="selectWithPermissions" parameterType="Long" resultMap="RoleWithPermissionsResultMap">
        SELECT 
        <include refid="Base_Column_List"/>
        FROM t_role 
        WHERE id = #{id}
    </select>
    
    <!-- 根据用户ID查询角色列表 -->
    <select id="selectByUserId" parameterType="Long" resultMap="BaseResultMap">
        SELECT 
        <include refid="Base_Column_List"/>
        FROM t_role r
        INNER JOIN t_user_role ur ON r.id = ur.role_id
        WHERE ur.user_id = #{userId}
        ORDER BY r.sort_order ASC, r.create_time DESC
    </select>
    
    <!-- 检查角色编码是否存在 -->
    <select id="existsByRoleCode" parameterType="String" resultType="boolean">
        SELECT COUNT(1) > 0 FROM t_role WHERE role_code = #{roleCode}
    </select>
</mapper>

5.3 动态SQL详解

5.3.1 if标签使用
<!-- 动态查询条件示例 -->
<select id="selectByConditions" parameterType="Map" resultMap="BaseResultMap">
    SELECT * FROM t_user
    <where>
        <!-- if标签:条件判断 -->
        <if test="username != null and username != ''">
            AND username = #{username}
        </if>
        <if test="email != null and email != ''">
            AND email = #{email}
        </if>
        <if test="status != null">
            AND status = #{status}
        </if>
        <!-- 数值类型判断 -->
        <if test="age != null and age > 0">
            AND age = #{age}
        </if>
        <!-- 日期范围查询 -->
        <if test="startDate != null">
            AND create_time >= #{startDate}
        </if>
        <if test="endDate != null">
            AND create_time <= #{endDate}
        </if>
        <!-- 列表判断 -->
        <if test="userIds != null and userIds.size() > 0">
            AND id IN
            <foreach collection="userIds" item="id" open="(" separator="," close=")">
                #{id}
            </foreach>
        </if>
    </where>
    ORDER BY create_time DESC
</select>
5.3.2 choose、when、otherwise标签使用
<!-- 多条件选择示例 -->
<select id="selectByType" parameterType="Map" resultMap="BaseResultMap">
    SELECT * FROM t_user
    <where>
        <choose>
            <when test="type == 'admin'">
                AND user_type = 1
            </when>
            <when test="type == 'user'">
                AND user_type = 2
            </when>
            <when test="type == 'guest'">
                AND user_type = 3
            </when>
            <otherwise>
                AND status = 1
            </otherwise>
        </choose>
        
        <!-- 排序方式选择 -->
        <choose>
            <when test="orderBy == 'username'">
                ORDER BY username ASC
            </when>
            <when test="orderBy == 'createTime'">
                ORDER BY create_time DESC
            </when>
            <otherwise>
                ORDER BY id DESC
            </otherwise>
        </choose>
    </where>
</select>
5.3.3 foreach标签详解
<!-- 批量操作示例 -->
<insert id="insertBatch" parameterType="List">
    INSERT INTO t_user (username, email, status) VALUES
    <foreach collection="list" item="user" separator=",">
        (#{user.username}, #{user.email}, #{user.status})
    </foreach>
</insert>

<!-- IN条件查询 -->
<select id="selectByIds" parameterType="List" resultMap="BaseResultMap">
    SELECT * FROM t_user 
    WHERE id IN
    <foreach collection="list" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</select>

<!-- 批量更新示例 -->
<update id="updateBatch" parameterType="List">
    <foreach collection="list" item="user" separator=";">
        UPDATE t_user 
        SET username = #{user.username}, email = #{user.email}
        WHERE id = #{user.id}
    </foreach>
</update>

<!-- 复杂的foreach使用 -->
<select id="selectByMultipleConditions" parameterType="Map" resultMap="BaseResultMap">
    SELECT * FROM t_user
    <where>
        <if test="conditions != null and conditions.size() > 0">
            <foreach collection="conditions" item="condition" separator=" OR ">
                (
                <if test="condition.username != null">
                    username = #{condition.username}
                </if>
                <if test="condition.email != null">
                    <if test="condition.username != null"> AND </if>
                    email = #{condition.email}
                </if>
                )
            </foreach>
        </if>
    </where>
</select>
5.3.4 set和trim标签使用
<!-- set标签:动态更新 -->
<update id="updateSelective" parameterType="User">
    UPDATE t_user
    <set>
        <if test="username != null and username != ''">
            username = #{username},
        </if>
        <if test="email != null and email != ''">
            email = #{email},
        </if>
        <if test="status != null">
            status = #{status},
        </if>
        <if test="updateTime != null">
            update_time = #{updateTime},
        </if>
    </set>
    WHERE id = #{id}
</update>

<!-- trim标签:更灵活的动态SQL -->
<select id="selectByTrimConditions" parameterType="Map" resultMap="BaseResultMap">
    SELECT * FROM t_user
    <trim prefix="WHERE" prefixOverrides="AND |OR ">
        <if test="username != null and username != ''">
            AND username = #{username}
        </if>
        <if test="email != null and email != ''">
            AND email = #{email}
        </if>
        <if test="status != null">
            AND status = #{status}
        </if>
    </trim>
</select>

<!-- trim在INSERT中的使用 -->
<insert id="insertSelective" parameterType="User" useGeneratedKeys="true" keyProperty="id">
    INSERT INTO t_user
    <trim prefix="(" suffix=")" suffixOverrides=",">
        <if test="username != null">username,</if>
        <if test="email != null">email,</if>
        <if test="password != null">password,</if>
        <if test="status != null">status,</if>
        <if test="createTime != null">create_time,</if>
    </trim>
    <trim prefix="VALUES (" suffix=")" suffixOverrides=",">
        <if test="username != null">#{username},</if>
        <if test="email != null">#{email},</if>
        <if test="password != null">#{password},</if>
        <if test="status != null">#{status},</if>
        <if test="createTime != null">#{createTime},</if>
    </trim>
</insert>
5.3.5 bind标签使用
<!-- bind标签:创建变量 -->
<select id="selectByLikeName" parameterType="String" resultMap="BaseResultMap">
    <bind name="pattern" value="'%' + _parameter + '%'" />
    SELECT * FROM t_user 
    WHERE username LIKE #{pattern}
</select>

<!-- 复杂的bind使用 -->
<select id="selectByComplexConditions" parameterType="Map" resultMap="BaseResultMap">
    <bind name="usernamePattern" value="username != null ? '%' + username + '%' : null" />
    <bind name="emailPattern" value="email != null ? '%' + email + '%' : null" />
    
    SELECT * FROM t_user
    <where>
        <if test="usernamePattern != null">
            AND username LIKE #{usernamePattern}
        </if>
        <if test="emailPattern != null">
            AND email LIKE #{emailPattern}
        </if>
    </where>
</select>

</code_block_to_apply_changes_from>

6. MyBatis高级特性

6.1 关联查询和结果映射

6.1.1 一对一关联(association)
<!-- 用户与用户详情一对一关联 -->
<resultMap id="UserWithProfileResultMap" type="User">
    <id column="id" property="id"/>
    <result column="username" property="username"/>
    <result column="email" property="email"/>
    <!-- 一对一关联 -->
    <association property="profile" javaType="UserProfile">
        <id column="profile_id" property="id"/>
        <result column="nickname" property="nickname"/>
        <result column="birthday" property="birthday"/>
        <result column="address" property="address"/>
    </association>
</resultMap>

<!-- 嵌套查询方式 -->
<resultMap id="UserWithProfileNestedResultMap" type="User">
    <id column="id" property="id"/>
    <result column="username" property="username"/>
    <association property="profile" javaType="UserProfile" 
                select="selectUserProfileByUserId" column="id"/>
</resultMap>

<!-- 联合查询 -->
<select id="selectUserWithProfile" resultMap="UserWithProfileResultMap">
    SELECT u.id, u.username, u.email,
           p.id as profile_id, p.nickname, p.birthday, p.address
    FROM t_user u
    LEFT JOIN t_user_profile p ON u.id = p.user_id
    WHERE u.id = #{id}
</select>
6.1.2 一对多关联(collection)
<!-- 用户与角色一对多关联 -->
<resultMap id="UserWithRolesResultMap" type="User">
    <id column="id" property="id"/>
    <result column="username" property="username"/>
    <result column="email" property="email"/>
    <!-- 一对多关联 -->
    <collection property="roles" ofType="Role">
        <id column="role_id" property="id"/>
        <result column="role_name" property="roleName"/>
        <result column="role_code" property="roleCode"/>
    </collection>
</resultMap>

<!-- 联合查询 -->
<select id="selectUserWithRoles" resultMap="UserWithRolesResultMap">
    SELECT u.id, u.username, u.email,
           r.id as role_id, r.role_name, r.role_code
    FROM t_user u
    LEFT JOIN t_user_role ur ON u.id = ur.user_id
    LEFT JOIN t_role r ON ur.role_id = r.id
    WHERE u.id = #{id}
</select>
6.1.3 多对多关联
<!-- 角色与权限多对多关联 -->
<resultMap id="RoleWithPermissionsResultMap" type="Role">
    <id column="id" property="id"/>
    <result column="role_name" property="roleName"/>
    <result column="role_code" property="roleCode"/>
    <collection property="permissions" ofType="Permission">
        <id column="permission_id" property="id"/>
        <result column="permission_name" property="permissionName"/>
        <result column="permission_code" property="permissionCode"/>
    </collection>
</resultMap>

<select id="selectRoleWithPermissions" resultMap="RoleWithPermissionsResultMap">
    SELECT r.id, r.role_name, r.role_code,
           p.id as permission_id, p.permission_name, p.permission_code
    FROM t_role r
    LEFT JOIN t_role_permission rp ON r.id = rp.role_id
    LEFT JOIN t_permission p ON rp.permission_id = p.id
    WHERE r.id = #{id}
</select>

6.2 缓存机制

6.2.1 一级缓存(本地缓存)
// 一级缓存示例
@Test
public void testFirstLevelCache() {
    SqlSession sqlSession = sqlSessionFactory.openSession();
    try {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        
        // 第一次查询,从数据库查询
        User user1 = mapper.selectById(1L);
        System.out.println("第一次查询:" + user1);
        
        // 第二次查询,从一级缓存查询
        User user2 = mapper.selectById(1L);
        System.out.println("第二次查询:" + user2);
        
        System.out.println("两次查询结果是否相同:" + (user1 == user2)); // true
    } finally {
        sqlSession.close();
    }
}
6.2.2 二级缓存配置
<!-- 在Mapper.xml中启用二级缓存 -->
<cache 
    eviction="LRU" 
    flushInterval="60000" 
    size="512" 
    readOnly="true"/>

<!-- 或者使用Redis作为二级缓存 -->
<cache type="org.mybatis.caches.redis.RedisCache" 
       eviction="LRU" 
       flushInterval="60000"/>
// 在实体类中实现Serializable接口
public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    // ... 其他属性
}
6.2.3 缓存配置说明
<!-- 缓存配置参数详解 -->
<cache 
    eviction="LRU"          <!-- 缓存回收策略:LRU、FIFO、SOFT、WEAK -->
    flushInterval="60000"   <!-- 缓存刷新间隔(毫秒) -->
    size="512"              <!-- 缓存大小 -->
    readOnly="false"        <!-- 是否只读 -->
    blocking="false"/>      <!-- 是否阻塞 -->

<!-- 针对特定查询禁用缓存 -->
<select id="selectById" useCache="false" resultMap="BaseResultMap">
    SELECT * FROM t_user WHERE id = #{id}
</select>

<!-- 针对特定操作清空缓存 -->
<insert id="insert" flushCache="true" parameterType="User">
    INSERT INTO t_user ...
</insert>

6.3 分页插件详解

6.3.1 PageHelper配置
# application.yml中的配置
pagehelper:
  helper-dialect: mysql
  reasonable: true
  support-methods-arguments: true
  params: count=countSql
  return-page-info: check
  page-size-zero: false
  auto-runtime-dialect: false
6.3.2 分页使用示例
// 服务层分页查询
@Service
public class UserService {
    
    @Autowired
    private UserMapper userMapper;
    
    /**
     * 分页查询用户
     */
    public PageInfo<User> findUsers(int pageNum, int pageSize, String keyword) {
        // 设置分页参数
        PageHelper.startPage(pageNum, pageSize);
        
        // 执行查询
        List<User> users;
        if (keyword != null && !keyword.trim().isEmpty()) {
            users = userMapper.selectByKeyword(keyword);
        } else {
            users = userMapper.selectAll();
        }
        
        // 封装分页信息
        return new PageInfo<>(users);
    }
    
    /**
     * 带排序的分页查询
     */
    public PageInfo<User> findUsersWithSort(int pageNum, int pageSize, String sortField, String sortOrder) {
        // 构建排序字符串
        String orderBy = sortField + " " + sortOrder;
        PageHelper.startPage(pageNum, pageSize, orderBy);
        
        List<User> users = userMapper.selectAll();
        return new PageInfo<>(users);
    }
    
    /**
     * 自定义分页查询
     */
    public PageInfo<User> findUsersByConditions(UserQueryVO queryVO) {
        PageHelper.startPage(queryVO.getPageNum(), queryVO.getPageSize());
        
        Map<String, Object> conditions = new HashMap<>();
        conditions.put("username", queryVO.getUsername());
        conditions.put("email", queryVO.getEmail());
        conditions.put("status", queryVO.getStatus());
        conditions.put("minAge", queryVO.getMinAge());
        conditions.put("maxAge", queryVO.getMaxAge());
        
        List<User> users = userMapper.selectByConditions(conditions);
        PageInfo<User> pageInfo = new PageInfo<>(users);
        
        // 设置额外信息
        pageInfo.setNavigatePages(8); // 导航页码数
        return pageInfo;
    }
}
6.3.3 控制器层分页处理
@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    /**
     * 分页查询用户
     */
    @GetMapping
    public ResponseEntity<Map<String, Object>> getUsers(
            @RequestParam(defaultValue = "1") int pageNum,
            @RequestParam(defaultValue = "10") int pageSize,
            @RequestParam(required = false) String keyword,
            @RequestParam(defaultValue = "createTime") String sortField,
            @RequestParam(defaultValue = "desc") String sortOrder) {
        
        try {
            PageInfo<User> pageInfo;
            if (keyword != null && !keyword.trim().isEmpty()) {
                pageInfo = userService.findUsers(pageNum, pageSize, keyword);
            } else {
                pageInfo = userService.findUsersWithSort(pageNum, pageSize, sortField, sortOrder);
            }
            
            Map<String, Object> response = new HashMap<>();
            response.put("users", pageInfo.getList());
            response.put("total", pageInfo.getTotal());
            response.put("pageNum", pageInfo.getPageNum());
            response.put("pageSize", pageInfo.getPageSize());
            response.put("pages", pageInfo.getPages());
            response.put("hasNextPage", pageInfo.isHasNextPage());
            response.put("hasPreviousPage", pageInfo.isHasPreviousPage());
            response.put("navigatePages", pageInfo.getNavigatePages());
            response.put("navigatepageNums", pageInfo.getNavigatepageNums());
            
            return ResponseEntity.ok(response);
        } catch (Exception e) {
            Map<String, Object> errorResponse = new HashMap<>();
            errorResponse.put("error", "查询失败:" + e.getMessage());
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
        }
    }
}

6.4 类型处理器(TypeHandler)

6.4.1 自定义JSON类型处理器
// JSON类型处理器
@Component
public class JsonTypeHandler<T> extends BaseTypeHandler<T> {
    
    private static final ObjectMapper objectMapper = new ObjectMapper();
    private Class<T> type;
    
    public JsonTypeHandler(Class<T> type) {
        this.type = type;
    }
    
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
        try {
            ps.setString(i, objectMapper.writeValueAsString(parameter));
        } catch (JsonProcessingException e) {
            throw new SQLException("Error converting object to JSON", e);
        }
    }
    
    @Override
    public T getNullableResult(ResultSet rs, String columnName) throws SQLException {
        String json = rs.getString(columnName);
        return parseJson(json);
    }
    
    @Override
    public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        String json = rs.getString(columnIndex);
        return parseJson(json);
    }
    
    @Override
    public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        String json = cs.getString(columnIndex);
        return parseJson(json);
    }
    
    private T parseJson(String json) throws SQLException {
        if (json == null || json.trim().isEmpty()) {
            return null;
        }
        try {
            return objectMapper.readValue(json, type);
        } catch (JsonProcessingException e) {
            throw new SQLException("Error parsing JSON", e);
        }
    }
}
6.4.2 枚举类型处理器
// 枚举类
public enum UserStatus {
    ACTIVE(1, "正常"),
    INACTIVE(0, "禁用"),
    PENDING(2, "待审核");
    
    private final int code;
    private final String description;
    
    UserStatus(int code, String description) {
        this.code = code;
        this.description = description;
    }
    
    public int getCode() { return code; }
    public String getDescription() { return description; }
    
    public static UserStatus fromCode(int code) {
        for (UserStatus status : values()) {
            if (status.code == code) {
                return status;
            }
        }
        throw new IllegalArgumentException("Unknown status code: " + code);
    }
}

// 枚举类型处理器
public class UserStatusTypeHandler extends BaseTypeHandler<UserStatus> {
    
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, UserStatus parameter, JdbcType jdbcType) throws SQLException {
        ps.setInt(i, parameter.getCode());
    }
    
    @Override
    public UserStatus getNullableResult(ResultSet rs, String columnName) throws SQLException {
        int code = rs.getInt(columnName);
        return rs.wasNull() ? null : UserStatus.fromCode(code);
    }
    
    @Override
    public UserStatus getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        int code = rs.getInt(columnIndex);
        return rs.wasNull() ? null : UserStatus.fromCode(code);
    }
    
    @Override
    public UserStatus getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        int code = cs.getInt(columnIndex);
        return cs.wasNull() ? null : UserStatus.fromCode(code);
    }
}
6.4.3 在MyBatis中注册类型处理器
<!-- mybatis-config.xml中注册 -->
<typeHandlers>
    <typeHandler handler="com.example.mybatisdemo.typehandler.JsonTypeHandler"/>
    <typeHandler handler="com.example.mybatisdemo.typehandler.UserStatusTypeHandler" 
                javaType="com.example.mybatisdemo.enums.UserStatus"/>
</typeHandlers>

6.5 MyBatis拦截器(插件)

6.5.1 自定义分页拦截器
@Intercepts({
    @Signature(type = Executor.class, method = "query", args = {
        MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class
    })
})
public class PaginationInterceptor implements Interceptor {
    
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        Object parameter = args[1];
        RowBounds rowBounds = (RowBounds) args[2];
        
        // 如果需要分页
        if (rowBounds != null && rowBounds != RowBounds.DEFAULT) {
            // 获取原始SQL
            BoundSql boundSql = ms.getBoundSql(parameter);
            String originalSql = boundSql.getSql();
            
            // 构建分页SQL
            String pagingSql = originalSql + " LIMIT " + rowBounds.getOffset() + ", " + rowBounds.getLimit();
            
            // 创建新的BoundSql
            BoundSql newBoundSql = new BoundSql(ms.getConfiguration(), pagingSql,
                    boundSql.getParameterMappings(), parameter);
            
            // 创建新的MappedStatement
            MappedStatement newMs = copyMappedStatement(ms, new BoundSqlSqlSource(newBoundSql));
            args[0] = newMs;
            args[2] = RowBounds.DEFAULT;
        }
        
        return invocation.proceed();
    }
    
    private MappedStatement copyMappedStatement(MappedStatement ms, SqlSource newSqlSource) {
        MappedStatement.Builder builder = new MappedStatement.Builder(ms.getConfiguration(),
                ms.getId(), newSqlSource, ms.getSqlCommandType());
        
        builder.resource(ms.getResource());
        builder.fetchSize(ms.getFetchSize());
        builder.statementType(ms.getStatementType());
        builder.keyGenerator(ms.getKeyGenerator());
        builder.timeout(ms.getTimeout());
        builder.parameterMap(ms.getParameterMap());
        builder.resultMaps(ms.getResultMaps());
        builder.resultSetType(ms.getResultSetType());
        builder.cache(ms.getCache());
        builder.flushCacheRequired(ms.isFlushCacheRequired());
        builder.useCache(ms.isUseCache());
        
        return builder.build();
    }
    
    public static class BoundSqlSqlSource implements SqlSource {
        private BoundSql boundSql;
        
        public BoundSqlSqlSource(BoundSql boundSql) {
            this.boundSql = boundSql;
        }
        
        @Override
        public BoundSql getBoundSql(Object parameterObject) {
            return boundSql;
        }
    }
}
6.5.2 SQL执行时间监控拦截器
@Intercepts({
    @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
    @Signature(type = Executor.class, method = "query", args = {
        MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class
    })
})
public class SqlTimeInterceptor implements Interceptor {
    
    private static final Logger logger = LoggerFactory.getLogger(SqlTimeInterceptor.class);
    
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        long startTime = System.currentTimeMillis();
        
        try {
            return invocation.proceed();
        } finally {
            long endTime = System.currentTimeMillis();
            long sqlTime = endTime - startTime;
            
            MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
            String sqlId = mappedStatement.getId();
            
            logger.info("SQL执行时间统计 - {}:{}ms", sqlId, sqlTime);
            
            if (sqlTime > 1000) {
                logger.warn("慢SQL警告 - {}:{}ms", sqlId, sqlTime);
            }
        }
    }
}

6.6 常见问题解决

6.6.1 N+1查询问题
// 问题:N+1查询
// 查询用户时,每个用户都会触发一次角色查询
public List<User> getBadUsers() {
    List<User> users = userMapper.selectAll(); // 1次查询
    for (User user : users) {
        List<Role> roles = roleMapper.selectByUserId(user.getId()); // N次查询
        user.setRoles(roles);
    }
    return users;
}

// 解决方案1:使用联合查询
<select id="selectUsersWithRoles" resultMap="UserWithRolesResultMap">
    SELECT u.*, r.id as role_id, r.role_name, r.role_code
    FROM t_user u
    LEFT JOIN t_user_role ur ON u.id = ur.user_id
    LEFT JOIN t_role r ON ur.role_id = r.id
</select>

// 解决方案2:分批查询
public List<User> getGoodUsers() {
    List<User> users = userMapper.selectAll();
    if (!users.isEmpty()) {
        List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toList());
        List<UserRole> userRoles = userRoleMapper.selectByUserIds(userIds);
        
        // 将角色信息分组并设置到用户对象中
        Map<Long, List<Role>> roleMap = userRoles.stream()
            .collect(Collectors.groupingBy(UserRole::getUserId,
                Collectors.mapping(UserRole::getRole, Collectors.toList())));
        
        users.forEach(user -> user.setRoles(roleMap.get(user.getId())));
    }
    return users;
}
6.6.2 大数据量查询优化
// 流式查询处理大数据量
@Select("SELECT * FROM t_user WHERE status = 1")
@Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = 1000)
void selectAllUsersWithCursor(ResultHandler<User> handler);

// 使用示例
public void processAllUsers() {
    userMapper.selectAllUsersWithCursor(resultContext -> {
        User user = resultContext.getResultObject();
        // 处理单个用户
        processUser(user);
    });
}

// 分页处理大数据量
public void processAllUsersByPage() {
    int pageSize = 1000;
    int pageNum = 1;
    List<User> users;
    
    do {
        PageHelper.startPage(pageNum, pageSize, false); // 不查询总数
        users = userMapper.selectAll();
        
        for (User user : users) {
            processUser(user);
        }
        
        pageNum++;
    } while (users.size() == pageSize);
}

7. 最佳实践总结

7.1 性能优化建议

  1. 合理使用缓存:开启二级缓存,使用Redis集群
  2. 避免N+1查询:使用联合查询或分批查询
  3. 优化SQL语句:添加适当索引,避免全表扫描
  4. 分页查询:大数据量时使用分页,避免内存溢出
  5. 连接池配置:合理配置数据库连接池参数

7.2 代码规范

  1. Mapper接口命名:使用动词+名词形式,如selectById、updateUser
  2. SQL语句:保持简洁清晰,适当添加注释
  3. 参数验证:在Service层进行参数校验
  4. 异常处理:统一异常处理机制
  5. 事务管理:合理使用@Transactional注解

7.3 安全考虑

  1. SQL注入防护:使用参数化查询,避免字符串拼接
  2. 敏感信息保护:密码等敏感信息加密存储
  3. 权限控制:实现细粒度的权限控制
  4. 日志记录:记录关键操作日志

这个MyBatis集成指南涵盖了从基础配置到高级特性的全面内容,为新手提供了详细的学习路径和实用示例。通过学习这些内容,您可以熟练掌握MyBatis在SpringBoot项目中的使用。


网站公告

今日签到

点亮在社区的每一天
去签到