01—Mybatis

概述

Mybatis中文官网:https://mybatis.net.cn/

Mybatis是一个优秀的持久层框架,通过XML将SQL与程序解耦,便于维护;并且可以自定义SQL、存储过程以及高级映射

开发流程

  1. 引入Mybatis依赖
  2. 创建核心配置文件
  3. 创建实体类
  4. 创建Mapper映射文件
  5. 初始化SessionFactory(会话工厂) 作用是读取配置文件,加载mapper映射
  6. 利用SqlSession对象操作数据 

基础入门 

环境配置 

Mybatis使用XML格式配置数据库环境信息,默认名称为mybatis-config.xml

Mybatis环境配置标签为<environment> 

environment中包含数据库驱动、URL、用户名、密码

环境配置示例

<!--配置环境,不同的环境不同的id名字-->
<environment id="dev">
    <!--采用JDBC方式对数据库事物进行commit/rollback-->
    <transcationManager type = "JDBC"></transcationManager>
    <!--采用连接池方式管理数据库连接-->
    <dataSource type="POOLED">
    	<property name="driver" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/school"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </dataSource>
</environment>

搭建Mybatis环境

① 创建一个空白的Maven项目

② 配置pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.spark</groupId>
    <artifactId>mybatis-study</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <!--配置阿里云镜像-->
    <repositories>
        <repository>
            <id>aliyun</id>
            <name>aliyun</name>
            <url>https://maven.aliyun.com/repository/public</url>
        </repository>
    </repositories>
    <!--配置依赖-->
    <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.9</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>
    </dependencies>
</project>

③ 在resource中创建Mybatis核心配置文件mybatis-config.xml

<?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="dev">
        <environment id="dev">
            <!--采用JDBC的方式对数据库事务进行commit/rollback-->
            <transactionManager type="JDBC"></transactionManager>
            <!--采用连接池方式管理数据库连接-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/school?useUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

SqlSessionFactory 

SqlSessionFactory是Mybatis的核心对象,用于初始化Mybatis创建SqlSession对象,需要保证SqlSessionFactory在应用全局唯一。

SqlSession对象是Mybatis操作数据库的核心对象,使用JDBC方式与数据库进行交互,并提供了数据表CRUD对应方法

引入单元测试依赖JUnit

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.1</version>
    <scope>test</scope>
</dependency>

在test目录下新建测试类进行测试

package com.spark.test;

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.Reader;
import java.sql.Connection;

/**
 * MybatisTester class
 * description: 测试环境是否配置成功
 *
 * @author Administrator
 * @date 2022/12/31
 */
public class MybatisTester {
    @Test
    public void testConnection() throws IOException {
        // 读取resource目录下的mybatis-config.xml文件
        Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
        // 加载mybatis核心配置文件并创建SqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        // 创建SqlSession对象
        SqlSession sqlSession = null;
        try {
            sqlSession = sqlSessionFactory.openSession();
            // 创建数据库连接,查看是否连接成功
            Connection connection = sqlSession.getConnection();
            System.out.println(connection);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(sqlSession != null){
                // 如果是POOLED连接池方式,close是将连接回收到连接池中
                // 如果是UNPOOLED直连方式,close是调用Connection.close()方法关闭连接
                sqlSession.close();
            }
        }
    }
}

初始化工具类MybatisUtils

编写工具类使得初始化SqlSessionFactory对象全局唯一

在Java目录下创建MybatisUtils类

package com.spark.mybatis.utils;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

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

/**
 * MybatisUtils class
 * description: 初始化工具类用于创建SqlSessionFactory对象并保证全局唯一
 *
 * @author Administrator
 * @date 2022/12/31
 */
public class MybatisUtils {
    // 使用static关键字修饰,使得sqlSessionFactory对象属于类,并保证全局唯一
    private static SqlSessionFactory sqlSessionFactory = null;
    // 使用static代码块初始化时创建sqlSessionFactory对象
    static{
        Reader reader = null;
        try {
            reader = Resources.getResourceAsReader("mybatis-config.xml");
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        } catch (IOException e) {
            e.printStackTrace();
            // 在初始化时报错,抛出ExceptionInInitializerError通知调用者
            throw new ExceptionInInitializerError(e);
        }
    }
    // 获取SqlSession对象用于操作数据库
    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();
    }
    // 关闭SqlSession连接
    public static void closeSqlSession(SqlSession sqlSession){
        if(sqlSession!=null){
            sqlSession.close();
        }
    }
}

在测试类中编写测试方法

@Test
public void testInitialization(){
    SqlSession sqlSession =null;
    try {
        // 通过工具类获取SqlSession对象
        sqlSession = MybatisUtils.getSqlSession();
        // 创建连接,测试是否连接成功
        Connection connection = sqlSession.getConnection();
        System.out.println(connection);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        // 关闭连接
        MybatisUtils.closeSqlSession(sqlSession);
    }

}

Mybatis数据查询

数据查询实现步骤

  1. 创建实体类
  2. 创建Mapper XML
  3. 编写<select> SQL标签
  4. 开启驼峰命名映射
  5. 新增<mapper>
  6. SqlSession执行语句 

创建实体类

package com.spark.mybatis.entity;

/**
 * Student class
 * description: 实体类
 *
 * @author Administrator
 * @date 2022/12/31
 */
public class Student {
    private Integer id; // id
    private String name; // 姓名
    private String stuno; // 学号
    private Integer age; // 年龄
    private String sex; // 性别
    private Integer cId; // 班级id

    public Student() {
    }

    public Student(Integer id, String name, String stuno, Integer age, String sex, Integer cId) {
        this.id = id;
        this.name = name;
        this.stuno = stuno;
        this.age = age;
        this.sex = sex;
        this.cId = cId;
    }

    public Integer getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getStuno() {
        return stuno;
    }

    public void setStuno(String stuno) {
        this.stuno = stuno;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Integer getcId() {
        return cId;
    }

    public void setcId(Integer cId) {
        this.cId = cId;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", stuno='" + stuno + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                ", cId=" + cId +
                '}';
    }
}

② 在resources目录下新建mapper目录,并创建StudentMapper.xml

<?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="student">
    <select id="selectAll" resultType="com.spark.mybatis.entity.Student">
        select * from student;
    </select>
</mapper>

③ 在mybatis-config.xml中开启驼峰命名,并添加mapper映射

<?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>
    <settings>
        <!--开启驼峰命名-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    <!--配置环境-->
    <environments default="dev">
        <environment id="dev">
            <!--采用JDBC的方式对数据库事务进行commit/rollback-->
            <transactionManager type="JDBC"></transactionManager>
            <!--采用连接池方式管理数据库连接-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/school?useUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <!--设置mappers映射-->
    <mappers>
        <!--添加resources目录下的mapper映射-->
        <mapper resource="mappers/StudentMapper.xml"></mapper>
    </mappers>
</configuration>

使用SqlSession对象执行SQL语句

@Test
public void testSelectAll(){
    SqlSession sqlSession =null;
    try {
        sqlSession = MybatisUtils.getSqlSession();
        // 执行SQL
        // student.selectAll student表示mapper的namespace命名空间 selectAll表示select标签的id
        List<Student> list = sqlSession.selectList("student.selectAll");
        for (Student student : list) {
            System.out.println(student);
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        MybatisUtils.closeSqlSession(sqlSession);
    }

}

SQL传参 

当执行select语句时,可能会通过传递参数作为查询条件去查询数据

Mybatis如何处理动态传参问题? 

在select标签中新增parameterType属性去指定参数类型,之后在sql语句中对查询条件进行赋值

<select id="selectById" parameterType="Integer" resultType="com.spark.mybatis.entity.Student">
    select * from student where id = #{value};
</select>

单参数传递 

编写select标签

<!--单参数传递-->
<select id="selectById" parameterType="Integer" resultType="com.spark.mybatis.entity.Student">
    select * from student where id = #{value};
</select>

编写测试方法

@Test
public void testSelectById(){
    SqlSession sqlSession =null;
    try {
        sqlSession = MybatisUtils.getSqlSession();
        Student student = sqlSession.selectOne("student.selectById", 1);
        System.out.println(student);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        MybatisUtils.closeSqlSession(sqlSession);
    }
}

多参数传递 

编写select标签

<!--多参数传递-->
<select id="selectStudents" parameterType="java.util.Map" resultType="com.spark.mybatis.entity.Student">
    select * from student where sex = #{sex} and c_id = #{cId};
</select>

编写测试方法

@Test
public void selectStudents(){
    SqlSession sqlSession =null;
    try {
        sqlSession = MybatisUtils.getSqlSession();
        Map<String,Object> map = new HashMap<>();
        map.put("sex","女");
        map.put("cId",2);
        List<Student> list = sqlSession.selectList("student.selectStudents",map);
        for (Student student : list) {
            System.out.println(student);
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        MybatisUtils.closeSqlSession(sqlSession);
    }
}

多表关联查询 

获取查询结果 

SQL查询操作大多数为多表关联查询。

现在有两张表student和classroom,通过Mybatis获取多表查询结果

编写select查询标签

<!--多表查询-->
<select id="selectStudentsAndClass" resultType="java.util.LinkedHashMap">
    select s.*,c.c_name,c.c_number
    from student s , classroom c
    where s.c_id = c.id;
</select>

编写测试代码

@Test
public void testSelectStudentsAndClass(){
    SqlSession sqlSession =null;
    try {
        sqlSession = MybatisUtils.getSqlSession();
        List<Map> list = sqlSession.selectList("student.selectStudentsAndClass");
        for (Map map : list) {
            System.out.println(map);
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        MybatisUtils.closeSqlSession(sqlSession);
    }
}

ResultMap结果映射 

虽然使用Map可以保存多表查询结果,但是Map太过灵活不便于维护

如何使用对象去保存多表查询的结果?

  • ResultMap可以将查询结果映射为复杂类型的Java对象
  • ResultMap适用于Java对象保存多表关联结果
  • ResultMap支持对象关联查询等高级特性 

创建复杂类型Java对象接收多表查询结果

DTO对象为数据传输对象,用于对原生对象进行拓展

package com.spark.mybatis.dto;

import com.spark.mybatis.entity.Student;

/**
 * StudentDTO class
 * description: student类的数据传输对象
 *
 * @author Administrator
 * @date 2022/12/31
 */
public class StudentDTO {
    private Student student = new Student();
    private String cName;
    private Integer cNumber;

    public StudentDTO() {
    }

    public StudentDTO(Student student, String cName, Integer cNumber) {
        this.student = student;
        this.cName = cName;
        this.cNumber = cNumber;
    }

    public Student getStudent() {
        return student;
    }

    public void setStudent(Student student) {
        this.student = student;
    }

    public String getcName() {
        return cName;
    }

    public void setcName(String cName) {
        this.cName = cName;
    }

    public Integer getcNumber() {
        return cNumber;
    }

    public void setcNumber(Integer cNumber) {
        this.cNumber = cNumber;
    }

    @Override
    public String toString() {
        return "StudentDTO{" +
                "student=" + student +
                ", cName='" + cName + '\'' +
                ", cNumber=" + cNumber +
                '}';
    }
}

编写select查询标签

<!--ResultMap结果映射-->
<resultMap id="BaseResultMap" type="com.spark.mybatis.dto.StudentDTO">
    <id column="id" property="student.id"></id>
    <result column="name" property="student.name"></result>
    <result column="age" property="student.age"></result>
    <result column="sex" property="student.sex"></result>
    <result column="stuno" property="student.stuno"></result>
    <result column="c_id" property="student.cId"></result>
    <result column="c_name" property="cName"></result>
    <result column="c_number" property="cNumber"></result>
</resultMap>
<select id="selectStudentDTO" resultMap="BaseResultMap">
    select s.*,c.c_name,c.c_number
    from student s , classroom c
    where s.c_id = c.id;
</select>

编写测试方法

@Test
public void testSelectStudentDTO(){
    SqlSession sqlSession =null;
    try {
        sqlSession = MybatisUtils.getSqlSession();
        List<StudentDTO> list = sqlSession.selectList("student.selectStudentDTO");
        for (StudentDTO studentDTO : list) {
            System.out.println(studentDTO);
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        MybatisUtils.closeSqlSession(sqlSession);
    }
}

数据操作 

数据插入 

插入数据使用insert标签

编写insert插入标签

<!--数据插入-->
<insert id="insertStudent" parameterType="com.spark.mybatis.entity.Student">
    insert into `student` (`name`, `stuno`, `age`, `sex`, `c_id`)
    values (#{name},#{stuno},#{age},#{sex},#{cId});
    <!--
            查询主键自增结果
            学生表id字段为自增主键
            selectKey标签的作用是查询insert语句执行后主键最后添加的值并赋值给学生类的id
        -->
    <selectKey resultType="Integer" keyColumn="id" order="AFTER">
        select last_insert_id();
    </selectKey>
</insert>

测试方法

@Test
public void testInsertStudent(){
    SqlSession sqlSession =null;
    try {
        sqlSession = MybatisUtils.getSqlSession();
        Student student = new Student();
        student.setName("小花");
        student.setAge(18);
        student.setSex("女");
        student.setStuno("2022005");
        student.setcId(1);
        int num = sqlSession.insert("student.insertStudent", student);
        System.out.println(num);
        // 提交事务
        sqlSession.commit();
    } catch (Exception e) {
        // 如果插入出现异常,回滚事务
        if(sqlSession!=null){
            sqlSession.rollback();
        }
        e.printStackTrace();
    } finally {
        MybatisUtils.closeSqlSession(sqlSession);
    }
}

selectKey和useGeneratedKeys的区别 

selectKey示例

<!--数据插入-->
<insert id="insertStudent" parameterType="com.spark.mybatis.entity.Student">
    insert into `student` (`name`, `stuno`, `age`, `sex`, `c_id`)
    values (#{name},#{stuno},#{age},#{sex},#{cId});
    <!--
            查询主键自增结果
            学生表id字段为自增主键
            selectKey标签的作用是查询insert语句执行后主键最后添加的值并赋值给学生类的id
        -->
    <selectKey resultType="Integer" keyColumn="id" order="AFTER">
        select last_insert_id();
    </selectKey>
</insert>

useGeneratedKeys示例

<!--数据插入-->
<insert id="insertStudent2" 
        parameterType="com.spark.mybatis.entity.Student"
        useGeneratedKeys="true"
        keyColumn="id"
        keyProperty="id"
        >
    insert into `student` (`name`, `stuno`, `age`, `sex`, `c_id`)
    values (#{name},#{stuno},#{age},#{sex},#{cId});
</insert>

二者区别:

① 显示与隐式

  • selectKey标签需要明确编写获取最新主键的SQL语句
  • useGeneratedKeys属性会自动根据驱动生成对应的SQL语句 

② 应用场景

  • selectKey支持所有的关系型数据库
  • useGeneratedKeys只支持“自增主键”数据库 

更新与删除 

更新使用update标签

<!--数据更新-->
<update id="updateStudent" parameterType="com.spark.mybatis.entity.Student">
    update student
    set
    name = #{name},
    sex = #{sex},
    age = #{age},
    c_id = #{cId}
    where
    stuno = #{stuno};
</update>

编写测试方法

@Test
public void testUpdateStudent(){
    SqlSession sqlSession =null;
    try {
        sqlSession = MybatisUtils.getSqlSession();
        Student student = sqlSession.selectOne("student.selectById",1);
        student.setName("张小飞");
        sqlSession.update("student.updateStudent",student);
        // 提交事务
        sqlSession.commit();
    } catch (Exception e) {
        // 如果插入出现异常,回滚事务
        if(sqlSession!=null){
            sqlSession.rollback();
        }
        e.printStackTrace();
    } finally {
        MybatisUtils.closeSqlSession(sqlSession);
    }
}

删除使用delete标签

<!--删除数据-->
<delete id="deleteStudent" parameterType="String">
    delete from student where stuno = #{stuno};
</delete>

编写测试方法

@Test
public void testDeleteStudent(){
    SqlSession sqlSession =null;
    try {
        sqlSession = MybatisUtils.getSqlSession();
        Student student = sqlSession.selectOne("student.selectById",1);
        sqlSession.delete("student.deleteStudent",student.getStuno());
        // 提交事务
        sqlSession.commit();
    } catch (Exception e) {
        // 如果插入出现异常,回滚事务
        if(sqlSession!=null){
            sqlSession.rollback();
        }
        e.printStackTrace();
    } finally {
        MybatisUtils.closeSqlSession(sqlSession);
    }
}

预防SQL注入攻击

SQL注入是指攻击者利用SQL漏洞,绕过系统约束,越权获取数据的方式

Mybatis预防SQL注入

两种传值方式:

${} 文本替换,未经任何处理对SQL文本替换

#{} 预编译传值,使用预编译传值可以预防SQL注入

Mybatis工作流程 

  1. Java应用程序首先会根据Mybatis核心配置文件加载全局设置项,其中包含了数据库连接、mapper映射等等
  2. 初始化阶段通过SqlSessionFactoryBuilder创建SqlSessionFactory对象,SqlSessionFactory对象会调用openSession()方法去获取SqlSession对象,从而可以操作数据库对数据表进行增删改查
  3. 对于数据表的写操作,SqlSession对象会根据SQL的执行情况进行事务的提交和回滚,最后SQL执行完毕,SqlSession对象会执行close()方法
  4. 如果mybatis-config.xml文件中数据库连接是POOLED连接池方式,执行close方法,会将连接回收到连接池中如果是UNPOOLED直连方式,执行close方法,会执行Connection.close()方法关闭连接 

Mybatis高级特性 

日志管理 

当使用Mybatis执行Java程序时,例如执行SQL语句,无法得知SQL的执行情况,这时可以通过配置日志在控制台显示执行情况,以便于对程序进行调试维护

日志的门面通常是Slf4j和Apache Commons-Loggings,日志的具体实现有log4j、logback等等

开启日志的步骤

在pom.xml中引入依赖

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.9</version>
</dependency>

在resources目录下新建logback.xml文件对日志进行配置

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <!--向控制台输出日志-->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <!--定义日志输出格式-->
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    <!--
        定义日志打印的级别(从高到低)
        error: 错误 - 系统故障日志
        warn: 警告 - 存在风险或使用不当的日志
        info: 一般性消息
        debug: 程序内部用于调式信息
        trace: 程序运行的跟踪信息
    -->
    <root level="debug">
        <appender-ref ref="console"/>
    </root>
</configuration>

动态SQL 

概述 

动态SQL可以根据不同条件生成不同SQL,允许在映射文件中编写灵活的SQL语句,以便于根据参数的不同情况来动态生成SQL语句

动态SQL的作用

  • 条件灵活:使用动态SQL可以根据不同的条件生成不同的SQL语句,使得查询、更新或删除数据时能够根据具体情况进行灵活的处理
  • 查询优化:动态SQL可以根据运行时的条件动态调整查询语句,从而更好的适应实际情况,提供查询性能
  • 动态表名和字段名:当需要根据不同的场景来操作不同的表或字段,可以通过动态SQL来动态构建表名和字段名,实现灵活性和扩展性
  • 防止SQL注入:通过使用参数化查询或者绑定变量的方式来构建动态SQL,可以有效防止SQL注入攻击,提升系统的安全性

常用的动态SQL标签

标签作用
if根据指定的条件判断是否包含某部分SQL代码,使得SQL语句在运行时更具灵活性
where生成动态的WHERE子句,只有满足条件时才包含WHERE子句,避免不必要的WHERE关键字
choose根据不同的条件选择执行不同的SQL片段,实现类似于switch-case语句的功能
foreach对集合进行循环,并在SQL语句中使用循环的结果,可以用于动态构建IN或VALUES子句
set生成动态的SET子句,只有满足条件时才包含SET子句,用于动态更新表中的字段
trim对SQL语句进行修剪和重组,去掉多余的AND或OR等,以便根据不同的条件动态生成合适的SQL语句

where和if标签 

 使用where标签和if标签编写动态SQL

<!--动态SQL-->
<select id="dynamicSQL" resultType="com.spark.mybatis.entity.Student">
    select * from student
    <where>
        <if test="sex != null">
            and sex = #{sex}
        </if>
        <if test="cId != null">
            and c_id = #{cId}
        </if>
    </where>
</select>

编写测试方法

@Test
public void testDynamicSQL(){
    SqlSession sqlSession =null;
    try {
        sqlSession = MybatisUtils.getSqlSession();
        Map<String,Object> map = new HashMap<>();
        map.put("sex","女");
        map.put("cId",2);
        List<Student> list = sqlSession.selectList("student.selectStudents",map);
        for (Student student : list) {
            System.out.println(student);
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        MybatisUtils.closeSqlSession(sqlSession);
    }
}

注意:使用where标签会动态的去掉第一个条件前的and条件,如果所有的参数没有值则不加where关键字;但是单使用if标签的话,建议 where 关键字后跟1=1,即:

select 列名 from 表名 where 1=1 and 条件 = 条件值;

否则,当参数值不为空时,生成的SQL会报错,即:

select 列名 from 表名 where and 条件 = 条件值;

choose标签 

<choose>标签中有两部分,<when>和<otherwise> 

when用于定义条件成立时执行的代码块,其中的test属性用于指定该条件分支的判断条件

otherwise用于定义默认的代码块,当所有的when条件都不成立时,将执行<otherwise>中定义的代码块

<select id="getSuppliersAll" resultType="pojo.Supplier">
   select supCode,supName,supContact,supPhone,supFax,createdTime from t_supplier
   <where>
       <choose>  <!--相当于Switch-->
           <when test="supCode!=null and supCode!=''">  <!--相当于case-->
                and supCode like #{supCode}
           </when>
           <when test="supName!=null and supName!=''">  <!--相当于case-->
                and supName like #{supName}
           </when>
       </choose>
   </where>
</select>

set标签 

set标签可以根据传入的字段值动态的去更新需要更新的字段,忽略不需要更新的字段

    <!--动态修改-->
    <update id="UpdateManySupplier">
        update t_supplier
        <set>
            <if test="supCode!=null and supCode!=''">
                supCode=#{supCode},
            </if>
            <if test="supName!=null and supName!=''">
                supName=#{supName},
            </if>
            <if test="supPhone!=null and supPhone!=''">
                supPhone=#{supPhone},
            </if>
        </set>
        where id=#{id}
    </update>

trim标签 

trim标签允许你在模版引擎或XML处理器中对字符串进行修剪操作,包括去除空白字符,去除指定的前缀和后缀,以及根据条件进行修剪。提供了一种方便和灵活的方式来处理和清理字符串数据

属性作用
prefix指定一个字符串前缀,它将被添加到修剪后的字符串的开头。通常用于添加特定的字符或标记
suffix指定一个字符串后缀,它将被添加到修剪后的字符串的结尾。常用于添加特定的字符或标记
prefixOverrides指定一个字符串前缀,当修剪后的字符串以该前缀开头时,该前缀将被移除
suffixOverrides指定一个字符串后缀,在修剪后的字符串以该后缀结尾时,该后缀将被移除

动态查询

    <!--动态查询-->
    <select id="getSuppliersAll" resultType="pojo.Supplier">
        select supCode,supName,supContact,supPhone,supFax,createdTime from t_supplier
        <trim prefix="where" prefixOverrides="and|or" suffix=" LIMIT #{index},#{pageSize}">
            <if test="supCode!=null and supCode!=''">
                and supCode like #{supCode}
            </if>
            <if test="supName!=null and supName!=''">
                and supName like #{supName}
            </if>
            <if test="supPhone!=null and supPhone!=''">
                and supPhone like #{supPhone}
            </if>
        </trim>
    </select>

动态修改

 <update id="UpdateManySupplier">
        update t_supplier
        <trim prefix="set" suffixOverrides="," suffix="where id=#{id}">
            <if test="supCode!=null and supCode!=''">
                supCode=#{supCode},
            </if>
            <if test="supName!=null and supName!=''">
                supName=#{supName},
            </if>
            <if test="supPhone!=null and supPhone!=''">
                supPhone=#{supPhone},
            </if>
        </trim>
    </update>

二级缓存 

一级缓存默认开启,缓存范围 SqlSession会话

二级缓存需要手动开启,缓存范围 Mapper namespace

二级缓存运行规则

  • 二级缓存开启后默认所有查询操作均使用缓存
  • 写操作commit提交时对该namespace缓存强制清空(保证数据一致性)
  • 在select标签中配置useCache = false 可以不用缓存
  • 在SQL标签中配置flushCache = true 代表SQL执行完毕后强制清空缓存 

开启二级缓存

在mapper文件中开启二级缓存

<!--开启二级缓存-->
<cache eviction="LRU" flushInterval="600000" size="512" readOnly="true"></cache>

eviction:缓存的清除策略,当缓存对象数量达到上限后,自动触发对应算法对缓存对象清除

  • LRU 移除最长时间不被使用的对象
  • LFU 移除使用次数最少的对象
  • FIFO 按对象进入缓存的顺序来移除
  • SOFT 移除基于垃圾收集器状态和软引用规则的对象
  • WEAK 更积极地移除基于垃圾收集器状态和弱引用规则的对象 

flushInterval:代表间隔多长时间自动清空缓存,单位毫秒

size:缓存存储上限,用于报错对象或集合(1个集合算1个对象)的数量上限

readOnly:设置为true时,返回只读缓存,每次从缓存取出的是缓存对象的本身,执行效率较高;

设置为false时,每次取出的是缓存对象的“副本”,每一次取出的对象都是不同的,安全性较高

分页插件 

Mybatis提供了PageHelper分页插件

  • Maven引入PageHelper与jsqlparser
  • mybatis-config.xml增加Plugin配置
  • 代码中使用PageHelper.startPage()自动分页 

引入依赖

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.3.0</version>
</dependency>
<dependency>
    <groupId>com.github.jsqlparser</groupId>
    <artifactId>jsqlparser</artifactId>
    <version>4.3</version>
</dependency>

配置Plugin

<!--开启分页插件-->
<plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
        <!--数据库类型-->
        <property name="helperDialect" value="mysql"/>
        <!--分页合理化-->
        <property name="reasonable" value="true"/>
    </plugin>
</plugins>

测试

@Test
public void testSelectPage(){
    SqlSession sqlSession =null;
    try {
        sqlSession = MybatisUtils.getSqlSession();
        // 查询第几页,每页几条数据
        PageHelper.startPage(1,2);
        Page<Student> page = (Page) sqlSession.selectList("student.selectAll");
        // 获取当前页记录
        List<Student> list = page.getResult();
        // 获取总页数
        System.out.println(page.getPages());
        // 获取总记录数
        System.out.println(page.getTotal());
        for (Student student : list) {
            System.out.println(student);
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        MybatisUtils.closeSqlSession(sqlSession);
    }
}

整合C3P0连接池 

引入依赖

<dependency>
    <groupId>com.mchange</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.5.4</version>
</dependency>

创建数据源工厂

package com.spark.mybatis.datasource;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;

/**
 * C3P0DataSourceFactory class
 * description: C3P0连接池工厂
 *
 * @author Administrator
 * @date 2023/1/2
 */

/**
 * C3P0与mybatis兼容使用的数据源工厂类
 */
public class C3P0DataSourceFactory extends UnpooledDataSourceFactory {
    public C3P0DataSourceFactory(){
        this.dataSource = new ComboPooledDataSource();
    }
}

更改mybatis-config.xml文件中数据源配置

<dataSource type="com.spark.mybatis.datasource.C3P0DataSourceFactory">
    <property name="driverClass" value="com.mysql.jdbc.Driver"/>
    <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/school?useUnicode=true&amp;characterEncoding=UTF-8"/>
    <property name="user" value="root"/>
    <property name="password" value="root"/>
    <property name="initialPoolSize" value="5"/>
    <property name="maxPoolSize" value="20"/>
    <property name="minPoolSize" value="5"/>
</dataSource>

批处理 

向标签中插入批处理

<!--批量添加-->
<insert id="batchInsert"
        parameterType="java.util.List"
        >
    insert into `student` (`name`, `stuno`, `age`, `sex`, `c_id`)
    values
    <foreach collection="list" index="index" item="item" separator=",">
        (#{item.name},#{item.stuno},#{item.age},#{item.sex},#{item.cId})
    </foreach>
</insert>
<!--批量删除-->
<delete id="batchDelete" parameterType="java.util.List">
    delete from `student` where id in
    <foreach collection="list" index="index" item="item" open="(" close=")" separator=",">
        #{item}
    </foreach>
</delete>

编写测试方法 

@Test
public void testBatchInsert(){
    SqlSession sqlSession =null;
    try {
        sqlSession = MybatisUtils.getSqlSession();
        Student student;
        List<Student> list = new ArrayList<Student>();
        for(int i=0;i<100;i++){
            student = new Student();
            student.setName("测试用户"+(i+1));
            student.setAge(18);
            student.setSex("男");
            student.setStuno("202300"+(i+1));
            student.setcId(1);
            list.add(student);
        }
        sqlSession.insert("student.batchInsert", list);
        // 提交事务
        sqlSession.commit();
    } catch (Exception e) {
        // 如果插入出现异常,回滚事务
        if(sqlSession!=null){
            sqlSession.rollback();
        }
        e.printStackTrace();
    } finally {
        MybatisUtils.closeSqlSession(sqlSession);
    }
}
@Test
public void testBatchDelete(){
    SqlSession sqlSession =null;
    try {
        sqlSession = MybatisUtils.getSqlSession();
        List<Integer> list = new ArrayList<>();
        for(int i=1;i<=100;i++){
            list.add(i);
        }
        sqlSession.delete("student.batchDelete",list);
        // 提交事务
        sqlSession.commit();
    } catch (Exception e) {
        // 如果插入出现异常,回滚事务
        if(sqlSession!=null){
            sqlSession.rollback();
        }
        e.printStackTrace();
    } finally {
        MybatisUtils.closeSqlSession(sqlSession);
    }
}

注解开发

Mybatis通过注解开发可以简化配置过程

创建Dao接口

package com.spark.mybatis.dao;

import com.spark.mybatis.entity.Student;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface StudentDAO {
    @Select("select * from Student where c_id = #{cId}")
    public List<Student> getStudentsByClassNo(@Param("cId") Integer cId);
}

在配置类中加载Dao

<mappers>
    <!--
            加载DAO类两种写法
        -->
    <!--<mapper class="com.spark.mybatis.dao.StudentDAO"></mapper>-->
    <package name="com.spark.mybatis.dao"/>
</mappers>

测试

@Test
public void testDao(){
    SqlSession sqlSession =null;
    try {
        sqlSession = MybatisUtils.getSqlSession();
        StudentDAO dao = sqlSession.getMapper(StudentDAO.class);
        List<Student> list = dao.getStudentsByClassNo(2);
        System.out.println(list);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        MybatisUtils.closeSqlSession(sqlSession);
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值