Mybatis框架详解

Maven

什么是Maven

Maven是一个开源的项目管理和构建工具,它可以帮助开发团队管理项目的构建、依赖管理和文档等工作。它基于项目对象模型(Project Object Model,POM)来描述项目的结构和依赖关系,并使用插件来执行各种构建和开发任务。

xxxxxxxxxx 2024-09-04 17:26:27,011 - DEBUG - Created new connection using: 8f39b138091e4f699e72ac5e49a3836d2024-09-04 17:26:27,017 - INFO - Password updated successfully! New password: 123456bash

  • 项目管理: Maven使用POM文件来描述项目的结构和元数据信息。POM文件是一个XML文件,定义了项目的依赖、构建脚本、开发者信息等。通过POM文件,开发团队可以更方便地管理项目的结构和配置。
  • 依赖管理: Maven可以自动解决项目的依赖关系,下载和管理所需的依赖库。它会从中央仓库或其他配置的仓库中下载依赖,并确保版本的一致性。
  • 构建自动化: Maven使用插件来执行各种构建任务,如编译代码、运行测试、打包项目等。通过简单的命令,开发者可以执行特定的构建任务,而无需手动配置复杂的构建过程。
  • 项目报告: Maven可以生成各种项目报告,如测试报告、代码质量报告、依赖报告等。这些报告帮助开发团队了解项目的状态和质量。
  • 发布和部署: Maven支持项目的发布和部署,可以将构建的产物(如JAR、WAR文件)发布到仓库或部署到服务器上。
  • 多模块项目: Maven支持多模块项目,可以将一个大型项目拆分成多个子模块,每个模块都有自己的POM文件,同时可以继承和共享父项目的配置。
  • 集成持续集成工具: Maven可以与持续集成工具(如Jenkins)集成,实现自动化构建和测试。

Maven安装

下载地址:http://maven.apache.org/
配置环境变量

#~/.bash_profile
export MAVEN_HOME=/Users/allen/workspace/install/apache-maven-3.9.0
PATH=$PATH:$MAVEN_HOME/bin

配置Maven

设置本地仓库

编辑apache-maven-3.9.0/conf/settings.xml

<localRepository>${user.home}/.m2/repository</localRepository>

设置阿里云镜像

<mirrors>
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>central</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
</mirrors>

设置jdk版本

<profiles>
<profile>
<id>jdk-1.8</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>1.8</jdk>
</activation>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
</properties>
</profile>
</profiles>

Maven核心概念

POM

Maven 是一种流行的项目管理和构建工具,用于管理 Java 项目的构建过程、依赖关系和项目生命周期。在 Maven 中,”POM” 是 “Project Object Model” 的缩写,是 Maven 项目的核心配置文件。
POM 文件是 Maven 项目的配置文件,它以 XML 格式编写,并位于项目根目录下的 pom.xml 文件中。POM 文件定义了项目的元数据、构建设置、依赖项、插件配置等信息,让 Maven 能够正确构建和管理项目。

pom文件关键字

关键字 解释
groupId 表示项目所属的组织或包名,通常作为包名使用。
artifactId 表示项目的唯一标识符(例如 JAR 文件名,不含版本号)。
version 表示项目的版本号。
packaging 指定项目的打包类型(例如 jarwarpom 等)。
dependencies 定义项目所需的外部库(依赖项)列表。
dependencyManagement 提供一种管理依赖项版本的方式,通常用于父 POM,并继承给子 POM。
parent 允许从父 POM 继承配置,有助于减少冗余的配置。
modules 列出多模块项目的子模块(子项目)。
properties 保存项目级别的属性,可以在 POM 或其他文件中引用。
build 包含构建过程的设置,包括源代码目录、插件和其他设置。
profiles 定义不同配置集合,可以根据需要激活或禁用这些配置。
repositories 指定 Maven 可以从中下载项目依赖项的仓库。
pluginRepositories 指定 Maven 可以从中下载插件依赖项的仓库。
reporting 配置项目报告的生成(例如代码覆盖率、测试报告等)。
dependency dependencies 部分描述单个依赖项,指定 groupId、artifactId 和 version。
plugin plugins 部分描述单个插件,指定 groupId、artifactId、version 和配置信息。

Maven项目目录结构

目录 说明
src/main/java 存放主要的 Java 源代码文件。
src/main/resources 存放主要的资源文件,例如配置文件、属性文件等。
src/main/webapp 对于 Web 项目,存放 Web 应用程序相关的文件,如 HTML、JSP 文件等。
src/test/java 存放测试用例的 Java 源代码文件。
src/test/resources 存放测试所需的资源文件,如测试配置文件等。
pom.xml Maven 项目的配置文件,定义了项目的元数据、依赖关系、构建配置等。
target 构建输出目录,编译、测试和打包后的输出文件都会放在这里。

Maven版本号管理

<properties>
<spring-version>5.3.17</spring-version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring-version}</version>
</dependency>
</dependencies>

Maven的继承

如子工程大部分都共同使用jar包,可以提取父工程中,使用【继承原理】在子工程中使用;父工程打包方式,必须是pom方式。
父工程pom.xml:

<packaging>pom</packaging>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>

子工程引入父工程相关jar包:

<parent>
<artifactId>maven_demo</artifactId>
<groupId>com.atguigu</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>

Maven的聚合

只要将子工程聚合到父工程中,就可以实现效果:安装或清除父工程时,子工程会进行同步操作。例如:

<modules>
<module>maven_helloworld</module>
<module>HelloFriend</module>
<module>MakeFriend</module>
</modules>

Mybatis

MyBatis 是一款开源的持久层框架,它为 Java 程序提供了数据库访问的简单、灵活和高效的解决方案。MyBatis 的主要目标是将 SQL 与 Java 代码解耦,提供更直观、易于维护的数据库访问方式。它是在 Hibernate 之后另一个备受欢迎的持久层框架,特别适用于对 SQL 有较高要求的开发者。

Mybatis的优势

  • 简单易用:相对于传统的 JDBC 编程,MyBatis 提供了更简单的 API 和配置方式,降低了数据库访问的复杂性。
  • 灵活性:MyBatis 不强制使用特定的对象关系映射(ORM)规则,开发者可以按照自己的需求来定义 SQL 映射关系。
  • SQL 控制:开发者可以完全控制 SQL 的编写和优化,MyBatis 不会对 SQL 进行二次封装,使得性能优化更为灵活。
  • 动态 SQL:MyBatis 提供了强大的动态 SQL 功能,可以根据不同的条件拼接 SQL 语句,提高了 SQL 的灵活性。
  • 批量操作:MyBatis 支持批量操作,可以大大提高数据的处理效率。
  • 缓存支持:MyBatis 提供了一级缓存和二级缓存的支持,可以减少对数据库的频繁访问,提高性能。
  • 开放性:MyBatis 不限制开发者的 SQL 写法和数据库选择,允许开发者在需要的时候使用原生 SQL。

搭建Mybatis框架

创建maven工程

  1. 创建一个ssm的maven工程
  2. 创建数据库表
    use mybatis;
    create table employee
    (
    id int auto_increment
    primary key,
    last_name varchar(50) null,
    email varchar(50) null,
    salary double(10, 2) null
    );

导入jar包

  1. 需要的第三方jar包可以在https://mvnrepository.com/进行查询
    <dependencies>
    <dependency>
    <groupId>org.mariadb.jdbc</groupId>
    <artifactId>mariadb-java-client</artifactId>
    <version>3.1.4</version>
    </dependency>
    <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.7</version>
    </dependency>
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
    <scope>test</scope>
    </dependency>
    </dependencies>
  2. 在项目resources目录下编写配置文件mybatis-config.xml,配置文件在官网可以获取https://mybatis.org/mybatis-3/zh/getting-started.html
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
    PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "https://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
    <environments default="development">
    <environment id="development">
    <transactionManager type="JDBC"/>
    <dataSource type="POOLED">
    <property name="driver" value="org.mariadb.jdbc.Driver"/>
    <property name="url" value="jdbc:mariadb://acaiblog.top:3306/mybatis"/>
    <property name="username" value="root"/>
    <property name="password" value="123456 "/>
    </dataSource>
    </environment>
    </environments>
    <mappers>
    <mapper resource="org/mybatis/example/BlogMapper.xml"/>
    </mappers>
    </configuration>
  3. 编写方法ssm/src/main/java/com/acaiblog/mybatis/pojo/Employee.java
    package com.acaiblog.mybatis.pojo;

    public class Employee {
    private Integer id;
    private String last_name;
    private String email;
    private Double salary;

    public Employee(Integer id, String last_name, String email, Double salary) {
    this.id = id;
    this.last_name = last_name;
    this.email = email;
    this.salary = salary;
    }

    public Integer getId() {
    return id;
    }

    public String getLast_name() {
    return last_name;
    }

    public String getEmail() {
    return email;
    }

    public Double getSalary() {
    return salary;
    }

    public void setId(Integer id) {
    this.id = id;
    }

    public void setLast_name(String last_name) {
    this.last_name = last_name;
    }

    public void setEmail(String email) {
    this.email = email;
    }

    public void setSalary(Double salary) {
    this.salary = salary;
    }

    @Override
    public String toString() {
    return "Employee{" +
    "id=" + id +
    ", last_name='" + last_name + '\'' +
    ", email='" + email + '\'' +
    ", salary=" + salary +
    '}';
    }
    }
  4. 编写mapper,ssm/src/main/java/com/acaiblog/mybatis/mapper/EmployeeMapper.java
    package com.acaiblog.mybatis.mapper;

    import com.acaiblog.mybatis.pojo.Employee;
    public interface EmployeeMapper {
    /**
    * 通过id获取员工信息
    */
    public Employee selectEmployeeById(int id);
    }
  5. 编写EmployeeMapper接口mapper映射文件,文件内容可以在官网https://mybatis.org/mybatis-3/zh/getting-started.html获取
    编写接口映射mapper文件需要注意以下几点:
  • 接口名称和mapper文件名称保持一致
  • mapper文件namespace与接口全类名保持一致
  • mapper文件中select id与接口方法名保持一致
  • mapper文件中resultType要和方法名称保持一致
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.acaiblog.mybatis.mapper.EmployeeMapper">
    <select id="selectEmployeeById" resultType="com.acaiblog.mybatis.pojo.Employee">
    select * from employee where id=#{id}
    </select>
    </mapper>
  1. 编辑核心配置文件添加mapper映射文件
    <mappers>
    <mapper resource="mapper/EmployeeMapper.xml"/>
    </mappers>
  2. 编写测试文件ssm/src/test/java/TestMybatis.java
    import com.acaiblog.mybatis.mapper.EmployeeMapper;
    import com.acaiblog.mybatis.pojo.Employee;
    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    import org.junit.Test;

    import java.io.IOException;
    import java.io.InputStream;

    public class TestMybatis {
    @Test
    public void testMybatis(){
    try {
    // 从 XML 中构建 SqlSessionFactory
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    // 从 SqlSessionFactory 中获取 SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);

    Employee employee = employeeMapper.selectEmployeeById(1);
    System.out.printf("employee: %s", employee);
    } catch (IOException e) {
    throw new RuntimeException(e);
    }

    }
    }
  3. 运行结果如下:
    employee: Employee{id=1, last_name='阿才', email='baocai.guo@qq.com', salary=10000.0} 

配置Mybatis Log4j日志框架

  1. 在项目pom.xml文件中导入jar依赖包
    <dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
    </dependency>
  2. 创建log4j配置文件
    # log4j.properties
    log4j.rootLogger=DEBUG, stdout
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
  3. 在resources目录下mybatis-config.xml文件中添加
    <configuration>
    <settings>
    <setting name="logImpl" value="LOG4J"/>
    </settings>
    </configuration>
  4. 关闭SLF4J,在项目pom.xml中添加
    <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.32</version> <!-- 使用最新的版本 -->
    </dependency>
    <dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.6</version> <!-- 使用最新的版本 -->
    </dependency>
  5. IDE菜单栏Build》重新构建项目
  6. 打印日志
    import org.apache.log4j.Logger;
    class public TestMybatis{
    private static final Logger logger = Logger.getLogger(TestMybatis.class);
    public static void xxx(String[] args){
    logger.info(String.format("employee: %s", employee));
    }
    }

Mybatis核心配置

MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。

配置标签

配置 作用
configuration 根元素,包含MyBatis的全局配置信息。
settings 用于设置MyBatis的全局设置,如日志实现、缓存设置等。
typeAliases 定义类型别名,简化在Mapper文件中配置时的类名使用。
typeHandlers 用于处理数据库字段与Java对象之间的类型转换。
objectFactory 用于创建结果对象的实例,默认使用DefaultObjectFactory。
plugins 用于在MyBatis执行过程中拦截方法调用,增强功能或定制处理逻辑。
environments 用于配置多个不同的运行环境,如开发环境、测试环境、生产环境等。
environment 用于指定当前使用的运行环境,引用environments中的环境配置。
transactionManager 配置事务管理器的类型,用于控制数据库事务。
dataSource 配置数据源,用于提供数据库连接。
databaseIdProvider 用于在多厂商数据库中根据数据库产品名称提供唯一的标识。
mappers 用于配置Mapper接口文件,告诉MyBatis需要加载哪些Mapper接口。

properties

这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。
例如在mybatis-config.xml中定义:

<configuration>
// 定义属性
<properties>
<property name="driver" value="org.mariadb.jdbc.Driver"/>
</properties>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
// 引用属性
<property name="driver" value="${driver}"/>
</dataSource>
</environment>
</environments>
</configuration>

还可以将数据库相关配置单独写到db.properties文件中然后在mybatis-config.xml文件中引用,
db.properties配置文件:

db.driver=org.mariadb.jdbc.Driver
db.url=jdbc:mariadb://acaiblog.top:3306/mybatis
db.username=root
db.password=123456

mybatis-config.xml配置文件

<configuration>
<properties resource="db.properties"></properties>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${db.driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</dataSource>
</environment>
</environments>
</configuration>

settings

这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项设置的含义、默认值等。

typeAliases

类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。可以在mybatis-config.xml中定义一个别名然后在映射文件中引用。
mybatis-config.xml

<configuration>
<typeAliases>
<typeAlias type="com.acaiblog.mybatis.pojo.Employee" alias="employee"></typeAlias>
</typeAliases>
</configuration>

EmployeeMapper.xml

<mapper namespace="com.acaiblog.mybatis.mapper.EmployeeMapper">
<select id="selectEmployeeById" resultType="employee">
select * from employee where id=#{id}
</select>
</mapper>

environments

主要是用来设置数据库的连接环境

mappers

设置映射文件的路径,支持4中方式:

  1. 使用相对于类路径的资源引用
    <mappers>
    <mapper resource="org/mybatis/builder/PostMapper.xml"/>
    </mappers>
  2. 使用完全限定资源定位符(URL)
    <mappers>
    <mapper url="file:///var/mappers/PostMapper.xml"/>
    </mappers>
  3. 使用映射器接口实现类的完全限定类名
    <mappers>
    <mapper class="org.mybatis.builder.AuthorMapper"/>
    </mappers>
  4. 将包内的映射器接口全部注册为映射器,这种方式要求接口文件包名和映射文件包名一致
    <mappers>
    <package name="org.mybatis.builder"/>
    </mappers>

Mybatis映射文件

根标签

mapper标签要求mapper中的namespace与接口的全类名保持一致

子标签

子标签支持以下几种:

元素 描述
cache 该命名空间的缓存配置。
cache-ref 引用其它命名空间的缓存配置。
resultMap 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。
sql 可被其它语句引用的可重用语句块。
insert 映射插入语句。
update 映射更新语句。
delete 映射删除语句。
select 映射查询语句。

Mybatis CURD

insert

  1. ssm/src/main/java/com/acaiblog/mybatis/mapper/EmployeeMapper.java新增添加数据的接口
    package com.acaiblog.mybatis.mapper;

    import com.acaiblog.mybatis.pojo.Employee;
    public interface EmployeeMapper {
    public void insertEmployee(Employee employee);
    }
  2. 在mapper xml文件ssm/src/main/resources/mapper/EmployeeMapper.xml中定义增加数据的sql
    <insert id="insertEmployee" resultType="employee">
    insert into employee(last_name,email,salary) values(#{last_name},#{email},#{salary})
    </insert>
  3. 在测试文件ssm/src/test/java/TestMybatis.java中定义添加数据的方法
    import com.acaiblog.mybatis.mapper.EmployeeMapper;
    import com.acaiblog.mybatis.pojo.Employee;
    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    import org.junit.Test;
    import org.apache.log4j.Logger;

    import java.io.IOException;
    import java.io.InputStream;

    public class TestMybatis {
    private static final Logger logger = Logger.getLogger(TestMybatis.class);
    @Test
    public void testMybatis(){
    try {
    // 从 XML 中构建 SqlSessionFactory
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    // 从 SqlSessionFactory 中获取 SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
    // 新增数据
    Employee employee1 = new Employee(null,"acai","acai@acai.com",100.0);
    employeeMapper.insertEmployee(employee1);
    //提交事务,添加到数据库
    sqlSession.commit();
    } catch (IOException e) {
    throw new RuntimeException(e);
    }

    }
    }

delete

  1. ssm/src/main/java/com/acaiblog/mybatis/mapper/EmployeeMapper.java中定义删除数据的接口
    public interface EmployeeMapper {
    public void deleteEmployee(int id);
    }
  2. /Users/allen/workspace/code/ssm/src/main/resources/mapper/EmployeeMapper.xml中定义删除数据映射的sql
    <mapper>
    <delete id="deleteEmployeeById">
    delete from employee where id=${id}
    </delete>
    </mapper>
  3. /Users/allen/workspace/code/ssm/src/test/java/TestMybatis.java中定义删除数据的方法
    // 删除数据
    employeeMapper.deleteEmployeeById(3);
    sqlSession.commit();

update

获取主键自增数据

属性 描述
useGeneratedKeys 启用主键生成策略
keyProperty 设置存储属性值
  1. ssm/src/main/resources/mapper/EmployeeMapper.xml新增数据sql设置属性
    <insert id="insertEmployee" useGeneratedKeys="true" keyProperty="id">
    insert into employee(last_name,email,salary) values(#{last_name},#{email},#{salary})
    </insert>
  2. ssm/src/test/java/TestMybatis.java获取新增数据的id
    // 新增数据
    Employee employee1 = new Employee(null,"acai","acai@acai.com",100.0);
    employeeMapper.insertEmployee(employee1);
    //提交事务,添加到数据库
    sqlSession.commit();
    logger.info(String.format("employee id: %s",employee1.getId()));

    获取数据库受影响行数

    直接将接口的返回值设置为int或布尔值
  • int:返回受影响的行数
  • boolean:返回true则数据增加,返回false则数据没有变化
  1. ssm/src/main/java/com/acaiblog/mybatis/mapper/EmployeeMapper.java中修改增加数据接口返回类型为int
    public int insertEmployee(Employee employee);
  2. ssm/src/test/java/TestMybatis.java中获取受影响的行数
    // 新增数据
    Employee employee1 = new Employee(null,"acai","acai@acai.com",100.0);
    int row = employeeMapper.insertEmployee(employee1);
    //提交事务,添加到数据库
    sqlSession.commit();
    logger.info(String.format("新增%s行数据",row));

    Mybatis参数传递

    单个普通参数

    可以任意使用:数据类型、参数的数据类型、参数的名称

    多个普通参数

    Mybatis底层会把参数封装成map结构,参数key是param1,param2或arg1,arg2
  3. 定义接口
    public List<Employee> selectEmployeeMuti(String last_name, double salary);
  4. 定义sql映射
    <select id="selectEmployeeMuti" resultType="employee">
    select * from employee where last_name=#{param1} and salary=#{param2}
    </select>
  5. 定义测试方法
    EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
    List<Employee> employee = employeeMapper.selectEmployeeMuti("acai",100);
    logger.info(String.format("employee: %s", employee));

命名参数

使用@Param起一个名字,Mybatis就会把参数封装到map,key就是定义的名字。
语法:@Param(value=”参数名”)或@Param(“参数名”)

  1. 定义接口
    public List<Employee> selectEmployeeMuti(@Param("last_name") String last_name, @Param("salary") double salary);
  2. 定义sql映射
    <select id="selectEmployeeMuti" resultType="employee">
    select * from employee where last_name=#{last_name} and salary=#{salary}
    </select>
  3. 定义测试方法
    List<Employee> employees = employeeMapper.selectEmployeeMuti("acai",100);
    logger.info(String.format("employees: %s", employees));

POJO参数

支持POJO[JavaBean]入参,参数key是POJO对象的属性

  1. 定义接口
    public List<Employee> selectEmployeeMuti(Employee employee);
  2. 定义sql映射
    <select id="selectEmployeeMuti" resultType="employee">
    select * from employee where last_name=#{last_name} and salary=#{salary}
    </select>
  3. 定义测试方法
    Employee employee = new Employee();
    employee.setLast_name("acai");
    employee.setSalary(100.0);
    List<Employee> employees = employeeMapper.selectEmployeeMuti(employee);
    logger.info(String.format("employees: %s", employees));

    map参数

    Mybatis支持map直接入参map的key=参数的key
  4. 定义接口
    public List<Employee> selectEmployeeMuti(Map<String, Object> map);
  5. 定义sql映射
    <select id="selectEmployeeMuti" resultType="employee">
    select * from employee where last_name=#{last_name} and salary=#{salary}
    </select>
  6. 定义测试方法
    Map<String, Object> map = new HashMap<>();
    map.put("last_name","acai");
    map.put("salary", 100.0);
    List<Employee> employees = employeeMapper.selectEmployeeMuti(map);
    logger.info(String.format("employees: %s", employees));

参数传递#和$的区别

  • #底层入参执行sql语句的对象,使用PreparedStatemend,预编译sql,防止sql注入安全,相对安全。
  • $底层入参执行sql语句的对象,使用Statemend,未解决sql注入安全隐患,不安全。

#和$使用场景

  • #所有场景都可以使用
  • $使用场景如动态化表名

$使用场景:

<select id="selectEmployeeMuti" resultType="employee">
select * from ${tableName}
</select>

查询返回值的四种情况

  1. 查询单行数据返回单个对象
    public Employee selectEmployeeById(int id);
    <select id="selectEmployeeById" resultType="employee">
    select * from employee where id=#{id}
    </select>
  2. 查询多行数据返回对象的集合
    public List<Employee> selectEmployeeMuti(Employee employee);
    <select id="selectEmployeeMuti" resultType="employee">
    select * from employee where last_name=#{last_name} and salary=#{salary}
    </select>
  3. 单行数据返回map,字段作为map的key,查询的结果作为map的value
    public Map<String,Object> selectEmployeeById(int id);
    <select id="selectEmployeeById" resultType="map">
    select * from employee where id=#{id}
    </select>
  4. 多行数据返回map,对象的id作为key,对象作为value
    @MapKey("id")
    public Map<Integer,Employee> selectEmpResultMap();
    <select id="selectEmployeeById" resultType="map">
    select * from employee
    </select>

自动映射和自定义映射

  • 自动映射resultType:指的是自动将表中的字段与类中的属性关联一致。
  • 自定义映射resultMap:自动映射解决不了的问题交给自定义映射
  • 自动映射解决不了的问题:多表关联查询

自动映射

示例:通过员工ID获取员工信息及员工部门信息,发现无法获取关联表字段的值;以下是具体实现步骤。

  1. 创建表sql
    CREATE TABLE dept (
    dept_id INT AUTO_INCREMENT PRIMARY KEY,
    dept_name character(50)
    );
    INSERT INTO mybatis.dept (dept_id, dept_name) VALUES (1, '研发部');
  2. 创建dept POJO
    package com.acaiblog.mybatis.pojo;

    public class Dept {
    private Integer dept_id;
    private String dept_name;

    public Dept(Integer dept_id, String dept_name) {
    this.dept_id = dept_id;
    this.dept_name = dept_name;
    }

    public Integer getDept_id() {
    return dept_id;
    }

    public String getDept_name() {
    return dept_name;
    }

    public void setDept_id(Integer dept_id) {
    this.dept_id = dept_id;
    }

    public void setDept_name(String dept_name) {
    this.dept_name = dept_name;
    }

    @Override
    public String toString() {
    return "Dept{" +
    "dept_id=" + dept_id +
    ", dept_name='" + dept_name + '\'' +
    '}';
    }
    }
  3. 员工类Employee添加Dept对象,toString方法添加dept属性,否则运行测试打印的结果没有dept属性
    public class Employee {
    private Double salary;

    private Dept dept;

    public Dept getDept() {
    return dept;
    }

    public void setDept(Dept dept) {
    this.dept = dept;
    }
    @Override
    public String toString() {
    return "Employee{" +
    "id=" + id +
    ", last_name='" + last_name + '\'' +
    ", email='" + email + '\'' +
    ", salary=" + salary +
    ", dept=" + dept +
    '}';
    }
    }
  4. 定义查询接口
    public interface EmployeeMapper {
    public List<Employee> selectEmpAndDeptByDeptId(int dept_id);
    }
  5. 创建映射xml
    <mapper namespace="com.acaiblog.mybatis.mapper.EmployeeMapper">
    <select id="selectEmpAndDeptByDeptId" resultType="employee">
    select e.id, e.last_name, e.email, e.salary, d.dept_name from employee e,dept d where e.dept_id
    </select>
    </mapper>
  6. 运行结果发现无法获取dept属性的值
    16:10:58.531 [main] DEBUG org.mariadb.jdbc.client.impl.StandardClient - execute query: select e.id, e.last_name, e.email, e.salary, d.dept_name from employee e,dept d where e.dept_id = ?
    2023-08-04 16:10:58 INFO TestMap:26 - dept: [Employee{id=1, last_name='阿才', email='baocai.guo@qq.com', salary=10000.0, dept=null}]

自定义映射

自定义级联映射

参数 描述
column 数据库字段名称
property 定义的pojo类属性名称
  1. ssm/src/main/resources/mapper/EmployeeMapper.xml定义resultMap
    <mapper namespace="com.acaiblog.mybatis.mapper.EmployeeMapper">
    <resultMap id="selectEmpAndDeptMap" type="employee">
    <!--定义主键字段与属性的关联关系-->
    <id column="id" property="id"></id>
    <!--定义非主键字段与属性的关联关系-->
    <result column="last_name" property="last_name"></result>
    <result column="email" property="email"></result>
    <result column="salary" property="salary"></result>
    <!--为员工部门定义自定义关联关系-->
    <result column="dept_id" property="dept.dept_id"></result>
    <result column="dept_name" property="dept.dept_name"></result>
    </resultMap>
    <select id="selectEmpAndDeptByDeptId" resultMap="selectEmpAndDeptMap">
    select e.id, e.last_name, e.email, e.salary, d.dept_name from employee e,dept d where e.dept_id = #{dept_id}
    </select>
    </mapper>
  2. 运行之后返回以下数据
    2023-08-04 16:54:42 INFO  TestMap:26 - dept: [Employee{id=1, last_name='阿才', email='baocai.guo@qq.com', salary=10000.0, dept=Dept{dept_id=null, dept_name='研发部'}}]

自定义association映射

特点

主要处理多对一的关联映射

  1. 定义sql映射
    <mapper namespace="com.acaiblog.mybatis.mapper.EmployeeMapper">
    <resultMap id="selectEmpAndDeptMap" type="employee">
    <!--定义主键字段与属性的关联关系-->
    <id column="id" property="id"></id>
    <!--定义非主键字段与属性的关联关系-->
    <result column="last_name" property="last_name"></result>
    <result column="email" property="email"></result>
    <result column="salary" property="salary"></result>
    <!--为员工部门定义自定义关联关系-->
    <association property="dept" javaType="com.acaiblog.mybatis.pojo.Dept">
    <id column="dept_id" property="dept_id"></id>
    <result column="dept_name" property="dept_name"></result>
    </association>
    </resultMap>
    <select id="selectEmpAndDeptByDeptId" resultMap="selectEmpAndDeptMap">
    select e.id, e.last_name, e.email, e.salary, d.dept_name from employee e,dept d where e.dept_id = #{dept_id}
    </select>
    </mapper>
  2. 运行之后返回以下结果
    2023-08-04 17:05:50 INFO  TestMap:26 - dept: [Employee{id=1, last_name='阿才', email='baocai.guo@qq.com', salary=10000.0, dept=Dept{dept_id=null, dept_name='研发部'}}] 

自定义association映射分步查询

需求:先通过员工id查询部门id,然后通过部门id查询部门名称

  1. 在mybatis配置文件mybatis-config.xml中添加DeptMapper.xml映射文件
    <mappers>
    <mapper resource="mapper/DeptMapper.xml"/>
    </mappers>
  2. 在EmployeeMapper.xml配置文件中定义分步查询
    <mapper namespace="com.acaiblog.mybatis.mapper.EmployeeMapper">
    <resultMap id="selectEmpAndDeptMap" type="employee">
    <!--定义主键字段与属性的关联关系-->
    <id column="id" property="id"></id>
    <!--定义非主键字段与属性的关联关系-->
    <result column="last_name" property="last_name"></result>
    <result column="email" property="email"></result>
    <result column="salary" property="salary"></result>
    <!--为员工部门定义自定义关联关系-->
    <association property="dept" select="com.acaiblog.mybatis.mapper.DeptMapper.selectEmployeeAndDeptById" column="dept_id">

    </association>
    </resultMap>
    <select id="selectEmpAndDeptByDeptId" resultMap="selectEmpAndDeptMap">
    select id,last_name,email,salary,dept_id from employee where id=#{id}
    </select>
    </mapper>
  3. 在DeptMapper.xml配置文件中定义查询sql
    <mapper namespace="com.acaiblog.mybatis.mapper.DeptMapper">
    <select id="selectEmployeeAndDeptById" resultType="dept">
    select dept_id,dept_name from dept where dept_id=#{dept_id}
    </select>
    </mapper>
  4. 查看运行结果
    2023-08-04 17:40:03 INFO  TestMap:26 - dept: [Employee{id=1, last_name='阿才', email='baocai.guo@qq.com', salary=10000.0, dept=Dept{dept_id=1, dept_name='研发部'}}] 

延迟加载

什么是延迟加载

延迟加载也叫懒加载:不需要的时候加载,需要的时候再加载

设置全局开启延迟加载

编辑mybatis-config.xml配置文件

<settings>
<!--开启延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--设置加载的数据是按需加载3.4.2以后版本该配置可以忽略-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>

设置局部开启延迟加载

编辑sql映射配置文件EmployeeMapper.xml,使用fetchType属性进行设置

<resultMap id="empAndDeptResultMapAssocationStep" type="employee">
<association property="dept"
select="com.atguigu.mybatis.mapper.DeptMapper.selectDeptByDeptId"
column="dept_id"
fetchType="eager">
</association>
</resultMap>

自定义collection映射

特点

主要处理一对多的关联映射

实现步骤

  1. 编辑ssm/src/main/java/com/acaiblog/mybatis/pojo/Dept.java
    public class Dept {
    public List<Employee> getEmployeeList() {
    return employeeList;
    }

    @Override
    public String toString() {
    return "Dept{" +
    "dept_id=" + dept_id +
    ", dept_name='" + dept_name + '\'' +
    ", employeeList=" + employeeList +
    '}';
    }

    public void setEmployeeList(List<Employee> employeeList) {
    this.employeeList = employeeList;
    }

    private List<Employee> employeeList;
    }
  2. 新增接口,编辑ssm/src/main/java/com/acaiblog/mybatis/mapper/DeptMapper.java
    public interface DeptMapper {
    // 通过部门id获取部门信息,及部门员工所属信息
    public Dept selectDeptAndEmpByDeptId(int deptId);
    }
  3. 编写sql映射文件
    <resultMap id="deptAndEmpResultMap" type="dept">
    <id property="dept_id" column="dept_id"></id>
    <result property="dept_name" column="dept_name"></result>
    <collection property="employeeList" ofType="com.acaiblog.mybatis.pojo.Employee">
    <id property="id" column="id"></id>
    <result property="last_name" column="last_name"></result>
    <result property="email" column="email"></result>
    <result property="salary" column="salary"></result>
    </collection>
    </resultMap>
    <select id="selectDeptAndEmpByDeptId" resultMap="deptAndEmpResultMap">
    select e.id, e.last_name, e.email, e.salary,d.dept_id, d.dept_name from employee e,dept d where e.dept_id = d.dept_id and d.dept_id = #{dept_id}
    </select>
  4. 编写测试代码
    public class TestMap {
    private static final Logger logger = Logger.getLogger(TestMap.class);
    @Test
    public void testMap() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    DeptMapper deptMapper = sqlSession.getMapper(DeptMapper.class);
    Dept dept = deptMapper.selectDeptAndEmpByDeptId(1);
    logger.info(String.format("dept: %s", dept));

    }
    }
  5. 查询结果
    2023-08-08 10:24:49 INFO  TestMap:26 - dept: Dept{dept_id=1, dept_name='研发部', employeeList=[Employee{id=1, last_name='阿才', email='baocai.guo@qq.com', salary=10000.0, dept=null}]} 

Mybatis动态sql

MyBatis是一款Java持久化框架,用于将数据库操作与Java对象之间的映射关系进行管理。它提供了一种称为动态SQL的功能,允许您根据不同的条件来生成不同的SQL语句。这在实际开发中非常有用,可以避免写大量重复的SQL语句,同时提高了代码的可维护性和灵活性。

动态标签

标签 描述
<if> 用于条件判断,根据条件动态包含SQL片段。
<choose> 类似于Java中的switch语句,根据条件选择不同的分支。
<when> <choose>一起使用,在分支条件成立时执行。
<otherwise> <choose>一起使用,在所有<when>条件不成立时执行。
<trim> 用于动态生成SQL的部分,可以自动去除多余的逗号、AND、OR等。
<where> 用于包裹条件语句,自动生成WHERE关键字并处理多余的AND、OR。
<set> 用于生成UPDATE语句的SET部分,自动生成SET关键字并处理多余的逗号。
<foreach> 用于循环遍历集合,生成重复的SQL片段。
<bind> 用于在SQL中绑定变量。

环境搭建

  1. 创建Employee pojo,编辑:com/acaiblog/mybatis/pojo/Employee.java
    package com.acaiblog.mybatis.pojo;

    public class Employee {
    private Integer id;
    private String lastName;
    private String email;
    private Double salary;

    public Employee(Integer id, String lastName, String email, Double salary) {
    this.id = id;
    this.lastName = lastName;
    this.email = email;
    this.salary = salary;
    }

    public Integer getId() {
    return id;
    }

    public void setId(Integer id) {
    this.id = id;
    }

    public String getLastName() {
    return lastName;
    }

    public void setLastName(String lastName) {
    this.lastName = lastName;
    }

    public String getEmail() {
    return email;
    }

    public void setEmail(String email) {
    this.email = email;
    }

    public Double getSalary() {
    return salary;
    }

    public void setSalary(Double salary) {
    this.salary = salary;
    }

    @Override
    public String toString() {
    return "Employee{" +
    "id=" + id +
    ", lastName='" + lastName + '\'' +
    ", email='" + email + '\'' +
    ", salary=" + salary +
    '}';
    }
    }
  2. 创建查询所有结果接口,编辑:com/acaiblog/mybatis/mapper/EmployeeMapper.java
    package com.acaiblog.mybatis.mapper;

    import com.acaiblog.mybatis.pojo.Employee;

    import java.util.List;

    public interface EmployeeMapper {
    public List<Employee> employeeList();
    }
  3. 编写sql映射文件
    参数解析:
    下面是对您提供的MyBatis XML映射文件内容进行解析,并将每个参数的含义使用Markdown表格展示:
参数 含义
<mapper> MyBatis映射文件的根标签。
<resultMap> 定义的pojo对象属性名映射到数据库字段名称
<resultMap id=""> resultMap id和select resultMap关联在一起
<resultMap type=""> resultMap type和pojo类对象绑定,指定该类的全限定名
property 表示类对象属性名称
column 表示数据库表字段
namespace 映射器(Mapper)的命名空间,指定了该映射文件对应的Java接口或类的全限定名。
<select> 用于定义数据库查询操作的标签。
<mapper namespace="com.acaiblog.mybatis.mapper.EmployeeMapper">
<resultMap id="employeeResultMap" type="com.acaiblog.mybatis.pojo.Employee">
<id property="id" column="id"></id>
<result property="lastName" column="last_name"></result>
<result property="email" column="email"></result>
<result property="salary" column="salary"></result>
</resultMap>
<select id="employeeList" resultMap="employeeResultMap">
select
id,last_name,email,salary
from
employee
</select>
</mapper>
  1. 编写测试方法,编辑:src/test/java/TestDynamicSql.java
    import com.acaiblog.mybatis.mapper.EmployeeMapper;
    import com.acaiblog.mybatis.pojo.Employee;
    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    import org.junit.Test;

    import java.io.IOException;
    import java.io.InputStream;
    import java.util.List;

    import org.apache.log4j.Logger;

    public class TestDynamicSql {
    private static final Logger logger = Logger.getLogger(TestDynamicSql.class);
    @Test
    public void test() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
    List<Employee> employeeList = employeeMapper.employeeList();
    for (Employee employee : employeeList) {
    logger.info(String.format("emp: %s", employee));
    }

    }
    }
  2. 查询结果:
    2023-08-08 14:22:03 INFO  TestDynamicSql:28 - emp: Employee{id=1, lastName='阿才', email='baocai.guo@qq.com', salary=10000.0}
    2023-08-08 14:22:03 INFO TestDynamicSql:28 - emp: Employee{id=4, lastName='慕容峻才', email='murongjuncai@qq.com', salary=100.0}
    2023-08-08 14:22:03 INFO TestDynamicSql:28 - emp: Employee{id=5, lastName='测试', email='test@qq.com', salary=100.0}

if

示例代码

  1. 编辑映射sql文件
    <mapper namespace="com.acaiblog.mybatis.mapper.EmployeeMapper">
    <resultMap id="employeeResultMap" type="com.acaiblog.mybatis.pojo.Employee">
    <id property="id" column="id"></id>
    <result property="lastName" column="last_name"></result>
    <result property="email" column="email"></result>
    <result property="salary" column="salary"></result>
    </resultMap>
    <select id="employeeList" resultMap="employeeResultMap">
    select
    id,last_name,email,salary
    from
    employee
    <if test="id !=null">
    where
    id=#{id}
    </if>
    </select>
    </mapper>
  2. 编写测试代码
    import com.acaiblog.mybatis.mapper.EmployeeMapper;
    import com.acaiblog.mybatis.pojo.Employee;
    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    import org.junit.Test;

    import java.io.IOException;
    import java.io.InputStream;
    import java.util.List;

    import org.apache.log4j.Logger;

    public class TestDynamicSql {
    private static final Logger logger = Logger.getLogger(TestDynamicSql.class);
    @Test
    public void test() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
    Employee employee = new Employee();
    employee.setId(1);
    List<Employee> employeeList = employeeMapper.employeeList(employee);
    for (Employee employees : employeeList) {
    logger.info(String.format("emp: %s", employees));

    }

    }
    }
  3. 运行结果
    2023-08-08 14:23:24 INFO  TestDynamicSql:28 - emp: Employee{id=1, lastName='阿才', email='baocai.guo@qq.com', salary=10000.0} 

where

where用于解决sql语句中where关键字以及条件前面的and或or的问题

实现步骤

  1. 编写sql映射文件
    <select id="employeeList" resultMap="employeeResultMap">
    select
    id,last_name,email,salary
    from
    employee
    <where>
    <if test="id != null">
    id=#{id}
    </if>
    <if test="salary !=null">
    and salary=#{salary}
    </if>
    </where>
    </select>
  2. 测试代码
    EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
    Employee employee = new Employee();
    employee.setId(4);
    employee.setSalary(100.00);
    List<Employee> employeeList = employeeMapper.employeeList(employee);
    for (Employee employees : employeeList) {
    logger.info(String.format("emp: %s", employees));

    }
  3. 运行结果
    2023-08-08 14:49:35 INFO  TestDynamicSql:29 - emp: Employee{id=4, lastName='慕容峻才', email='murongjuncai@qq.com', salary=100.0}
    2023-08-08 14:49:35 INFO TestDynamicSql:29 - emp: Employee{id=5, lastName='测试', email='test@qq.com', salary=100.0}

trim

可以在条件判断完sql语句前后添加或去掉指定符

trim属性

属性 描述
prefix 添加在 <trim> 元素内部的 SQL 片段的开头。
prefixOverrides 逗号分隔的字符串列表,指定要从内部 SQL 片段的开头移除的前缀。
suffix 添加在 <trim> 元素内部的 SQL 片段的结尾。
suffixOverrides 逗号分隔的字符串列表,指定要从内部 SQL 片段的结尾移除的后缀。

示例代码

<select id="employeeList" resultMap="employeeResultMap">
select
id,last_name,email,salary
from
employee
<where>
<!--如果salary为null则删除id=#{id} and后面的and-->
<trim suffixOverrides="and">
<if test="id != null">
id=#{id} and
</if>
<if test="salary != null">
salary=#{salary}
</if>
</trim>
</where>
</select>

set

主要解决修改sql中可能多出的一个逗号的问题。

示例代码

<update>
update
employee
<set>
<if test="id != null">
id=#{id} and
</if>
<if test="salary != null">
salary=#{salary}
</if>
</set>
where
id=#{id}
</update>

choose

用于分支判断类似java中的switch case,只会满足所有分支中的一个

示例代码

<select id="selectEmpByOneOpr" resultType="employee">
select
<include refid="emp_col"></include>
from
tbl_employee
<where>
<choose>
<when test="id != null">
id = #{id}
</when>
<when test="lastName != null">
last_name = #{lastName}
</when>
<when test="email != null">
email = #{email}
</when>
<when test="salary != null">
salary = #{salary}
</when>
<otherwise>
1=1
</otherwise>
</choose>
</where>
</select>

foreach

类似java中for循环

foreach属性

属性 描述
collection 要迭代的集合
item 当前从集合中迭代出的元素
separator 元素与元素之间的分隔符
open 开始字符
close 结束字符

示例代码

<insert id="batchInsertEmp">
INSERT INTO
tbl_employee(last_name,email,salary)
VALUES
<foreach collection="employees" item="emp" separator=",">
(#{emp.lastName},#{emp.email},#{emp.salary})
</foreach>
</insert>

sql

提取可重用SQL片段

  1. 定义sql片段
    <sql id="selectEmployee">
    select id,last_name,salary from employee
    </sql>
  2. 引用sql片段
    <select id="" resultType="">
    <include refid="selectEmployee"></include>
    </select>

Mybatis缓存机制

MyBatis提供了多级缓存机制,以提升数据库查询性能。这些缓存可以在不同的作用域内(Session、Mapper、全局)进行配置和使用。

一级缓存

  • 一级缓存是默认启用的,它位于SqlSession的生命周期范围内。在同一个SqlSession中,当查询被执行时,查询的结果会被缓存在该Session中。如果再次执行相同的查询,MyBatis会首先检查一级缓存,如果查询条件、语句以及参数都相同,就会直接从缓存中返回结果,而不再去数据库查询。
  • 一级缓存是基于对象引用的,意味着缓存中的数据会在SqlSession关闭时被销毁。也就是说,在一个SqlSession中查询之后,如果更新了相同数据,那么缓存会失效。

一级缓存5种失效的情况

  1. 不同的SqlSession对应不同的一级缓存
  2. 同一个SqlSession但是查询条件不同
  3. 同一个SqlSession两次查询操作期间,执行了增删改操作
  4. 两个查询操作期间执行了清除缓存操作
  5. 两个查询操作期间执行了事务操作

二级缓存

  • 二级缓存是在Mapper级别的缓存,可以被多个SqlSession共享。它需要手动配置启用,并且需要在Mapper接口中设置元素。二级缓存将缓存结果对象,使得多个SqlSession可以共享这些缓存数据。
  • 当进行查询时,MyBatis首先检查二级缓存,如果命中缓存,就会从缓存中返回结果,否则再去数据库查询。需要注意的是,被缓存的对象必须是可序列化的,因为二级缓存可以支持分布式环境。
  • 缓存的清除和更新由Mapper接口中的配置和操作决定,比如调用元素中的evict()方法或执行更新操作时。

二级缓存特点

  • 默认关闭,需要开启使用
  • 二级缓存需要提交SqlSession或关闭SqlSession时才会生效

二级缓存使用步骤

  1. 全局配置开启
    <settings>
    <!--开启二级缓存-->
    <setting name="cacheEnabled" value="true"/>
    </settings>
  2. 映射文件添加缓存标签
    <mapper namespace="com.acaiblog.mybatis.mapper.EmployeeMapper">
    <cache></cache>
    </mapper>
  3. pojo需要实现序列化接口
    public class Employee implements Serializable {
    private static final long serialVersionUID = -5384018548765866486L;
    }
  4. 关闭或提交SqlSession时将数据缓存到二级缓存

二级缓存底层原理

  • 第一次获取数据时先从数据库中获取数据,然后将数据缓存到一级缓存
  • 当我们提交或关闭SqlSession时将数据缓存到二级缓存
  • 以后获取数据时先从一级缓存获取数据再从二级缓存获取数据
  • 如果二级缓存也没有指定数据时,需要从数据库获取数据

二级缓存属性

属性 描述 默认值
cacheEnabled 启用或禁用二级缓存。如果设置为 false,则二级缓存将被禁用。 true
cacheType 缓存实现的类型。支持多种缓存实现,如 PerpetualCache、FifoCache、LRUCache 等。 -
flushInterval 刷新缓存的时间间隔(毫秒)。在指定的时间间隔内,有更新操作则刷新缓存。 -
size 缓存最大存储对象数量。当达到此数量时,根据缓存策略删除一些对象。 -
readOnly 缓存是否只读。如果为 true,表示缓存中的数据不会被修改。 false
blocking 启用或禁用阻塞缓存。如果启用,一个线程加载数据时,其他线程会被阻塞。 false
eviction 缓存淘汰策略,如 LRU(最近最少使用)、FIFO(先进先出)等。 -

二级缓存失效情况

  • 两次查询之间,执行增删改操作会同时删除一级和二级缓存
  • SqlSession.clearCache()只是用来清除一级缓存

第三方缓存EhCache

EhCache是一个java进程缓存框架,具有快速,精干等特点。是Hibernate中默认的CacheProvider。使用第三方缓存需要先开启二级缓存

使用步骤

  1. 导入包
    <dependency>
    <groupId>org.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>3.9.4</version> <!-- 使用最新版本号 -->
    </dependency>
  2. 编写配置文件
    <?xml version="1.0" encoding="UTF-8"?>
    <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="http://www.ehcache.org/ehcache.xsd"
    updateCheck="false">

    <!-- 定义缓存区域 -->
    <cache name=" "
    maxEntriesLocalHeap="1000"
    eternal="false"
    timeToIdleSeconds="300"
    timeToLiveSeconds="600">
    </cache>
    </ehcache>
  3. 映射文件使用cache标签
    <mapper namespace="com.acaiblog.mybatis.mapper.EmployeeMapper">
    <cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
    </mapper>

Mybatis逆向工程

MyBatis 逆向工程是一种自动生成数据库表对应的 Java 实体类、Mapper 接口以及 XML 配置文件的技术。它能够根据数据库表的结构自动生成代码,从而减少手动编写重复代码的工作量。逆向工程在项目开发中能够提高开发效率,减少错误,同时保持数据库与 Java 代码之间的一致性。
官方文档:https://mybatis.org/generator/

使用步骤

  1. 导入依赖包
    <dependencies>
    <dependency>
    <groupId>org.mariadb.jdbc</groupId>
    <artifactId>mariadb-java-client</artifactId>
    <version>3.1.4</version>
    </dependency>
    <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.7</version>
    </dependency>
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
    <scope>test</scope>
    </dependency>
    <dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version> <!-- 根据你的项目选择合适的版本 -->
    </dependency>
    <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.32</version> <!-- 使用最新的版本 -->
    </dependency>
    <dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.6</version> <!-- Use the appropriate version -->
    </dependency>
    <dependency>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-core</artifactId>
    <version>1.4.2</version>
    </dependency>
    </dependencies>
  2. 创建配置文件mgb.xml
    <!DOCTYPE generatorConfiguration PUBLIC
    "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
    "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
    <generatorConfiguration>
    <!--如果是MyBatis3Simple基本增删该查;如果是MyBatis3带条件的增删改查(QBC风格)-->
    <context id="simple" targetRuntime="MyBatis3Simple">
    <jdbcConnection driverClass="org.mariadb.jdbc.Driver"
    connectionURL="jdbc:mariadb://acaiblog.top:3306/mybatis"
    userId="root"
    password="123456">
    </jdbcConnection>
    <!--设置JavaBean生成策略-->
    <javaModelGenerator targetPackage="com.acaiblog.mybatis.pojo" targetProject="src/main/java"/>
    <!--设置sql映射生成策略,生成sql映射文件的相对路径-->
    <sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources"/>
    <!--设置map接口生成策略-->
    <javaClientGenerator type="XMLMAPPER" targetPackage="com.acaiblog.mybatis.mapper" targetProject="src/main/java"/>
    <!--逆向分析的数据库表-->
    <table tableName="employee" />
    <table tableName="dept"/>
    </context>
    </generatorConfiguration>
  3. 设置mariadb参数
    set global net_buffer_length=1000000;
    set global max_allowed_packet=1000000000;
  4. 测试代码
    import org.junit.Test;
    import org.mybatis.generator.api.MyBatisGenerator;
    import org.mybatis.generator.config.Configuration;
    import org.mybatis.generator.config.xml.ConfigurationParser;
    import org.mybatis.generator.exception.InvalidConfigurationException;
    import org.mybatis.generator.exception.XMLParserException;
    import org.mybatis.generator.internal.DefaultShellCallback;

    import java.io.File;
    import java.io.IOException;
    import java.sql.SQLException;
    import java.util.ArrayList;
    import java.util.List;

    public class TestMbg {
    @Test
    public void test() throws Exception{
    List<String> warnings = new ArrayList<String>();
    boolean overwrite = true;
    File configFile = new File("src/main/resources/mbg.xml");
    ConfigurationParser cp = new ConfigurationParser(warnings);
    Configuration config = cp.parseConfiguration(configFile);
    DefaultShellCallback callback = new DefaultShellCallback(overwrite);
    MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
    myBatisGenerator.generate(null);
    }
    }

Mybatis分页插件PageHelper

PageHelper是mybatis第三方分页插件,官网地址:https://github.com/pagehelper/Mybatis-PageHelper/blob/master/README_zh.md

使用步骤

  1. 导入依赖包
    <!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
    <dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.3.3</version>
    </dependency>
  2. mybatis-config.xml配置文件中配置分页插件
    <!--分页插件-->
    <plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
    </plugins>
  3. 查询之前使用pagehelper开启分页
    import com.acaiblog.mybatis.mapper.EmployeeMapper;
    import com.acaiblog.mybatis.pojo.Employee;
    import com.github.pagehelper.Page;
    import com.github.pagehelper.PageHelper;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    import org.junit.Test;

    import org.apache.ibatis.io.Resources;
    import org.apache.log4j.Logger;
    import java.io.InputStream;
    import java.util.List;

    public class TestPageHelper {
    private static final Logger logger = Logger.getLogger(TestPageHelper.class);
    @Test
    public void test() throws Exception{
    String resources = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resources);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
    // 开启分页
    Page<Object> page = PageHelper.startPage(1,2);
    List<Employee> employees = employeeMapper.selectAll();
    for (Employee employee : employees){
    logger.info(String.format("result: %s", employee));
    }
    logger.info(String.format("数据共计:%s条,当前页码: %s",page.getPages(),page.getPageNum()));

    }
    }
  4. 将结果封装到PageInfo

PageInfo内置方法

方法签名 描述
int getPageNum() 获取当前页码。
int getPageSize() 获取每页显示的记录数。
int getSize() 获取当前页的记录数。
int getStartRow() 获取当前页第一个元素在数据库中的行号。
int getEndRow() 获取当前页最后一个元素在数据库中的行号。
long getTotal() 获取总记录数。
int getPages() 获取总页数。
List<T> getList() 获取当前页的数据列表。
int getPrePage() 获取前一页页码。
int getNextPage() 获取下一页页码。
boolean isFirstPage() 判断是否为第一页。
boolean isLastPage() 判断是否为最后一页。
boolean hasPreviousPage() 判断是否有前一页。
boolean hasNextPage() 判断是否有下一页。
int[] getNavigatepageNums() 获取导航页码数组。
int getNavigateFirstPage() 获取导航页中的第一页页码。
int getNavigateLastPage() 获取导航页中的最后一页页码。
int getNavigatePages() 获取导航页码数量。
int getNavigatePageNums() 获取当前页导航页码数组。
PageInfo<T> of(List<T> list) 根据给定数据列表创建 PageInfo 对象。
PageInfo<T> of(List<T> list, int navigatePages) 根据给定数据列表和导航页码数量创建 PageInfo 对象。

示例代码

import com.acaiblog.mybatis.mapper.EmployeeMapper;
import com.acaiblog.mybatis.pojo.Employee;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import org.apache.ibatis.io.Resources;
import org.apache.log4j.Logger;
import java.io.InputStream;
import java.util.List;

public class TestPageHelper {
private static final Logger logger = Logger.getLogger(TestPageHelper.class);
@Test
public void test() throws Exception{
String resources = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resources);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
// 开启分页
Page<Object> page = PageHelper.startPage(1,2);

List<Employee> employees = employeeMapper.selectAll();
// 封装到pageInfo
PageInfo<Employee> pageInfo = new PageInfo<>(employees,8);
for (Employee employee : employees){
logger.info(String.format("result: %s", employee));
}
logger.info(String.format("总数: %s",pageInfo.getPageNum()));

}
}
文章作者: 慕容峻才
文章链接: https://www.acaiblog.top/Mybatis/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 阿才的博客
微信打赏
支付宝打赏