MyBatis笔记

MyBatis


学习本模块可见手册 MyBatis参考文档
下载第三方 jar 包可以参考 下载资源

1、框架概述

1.1、三层架构

MVC :web 开发中,使用 MVC 架构模式。m:数据,v:视图,c:控制器

​ c 控制器:接收请求,调用 servlet 对象,显示请求的处理结果,当前使用 servlet 作为控制器

​ v 视图:现在使用 jsp,html ,css,js 。显示请求的处理结果,把 m 中的数据显示出来

​ m 数据,来自数据库 MySQL、来自文件、来自网络

MVC 作用:

  1. 实现解耦合
  2. 让 mvc 各司其职
  3. 使得系统扩展更好,更容易维护

三层架构:

  1. 界面层(视图层):接收用户请求,调用 Servlet ,显示请求的处理结果的,包含了 jsp、html、servlet 等对象
  2. 业务逻辑层:处理业务逻辑,使用算法处理数据的,把数据返回给界面层,对应的是 service 包,和包中的类
  3. 持久层(数据库访问层):访问数据库,或者读取文件、访问网络、获取数据。对应的是 dao 包和包中的类

三层架构请求的处理流程:

用户发起请求 ------> 界面层 ------> 业务逻辑层 ------> 持久层 ------> 数据库(mysql)

为什么使用三层架构?

  1. 结构清晰,耦合度低,各层分工明确
  2. 可维护性高,可扩展性高
  3. 有利于标准化
  4. 开发人员可以只关注整个结构中的其中某一层的功能实现
  5. 有利于各层逻辑的复用

三层架构模式和框架,每一层对应着一个框架:

  1. 界面层 ------> SpringMVC 框架
  2. 业务层 ------> Spring 框架
  3. 持久层 ------> MyBatis 框架

1.2、框架

什么是框架?

  1. 框架是一个半成品,已经对基础的代码进行了封装并提供相应的 API ,开发者在使用框架时直接调用封装好的 API 可以省去很多代码编写,从而提高工作效率和开发速度
  2. 框架看做是模板
  3. 框架是可以升级、改造的,框架是安全的
  4. 框架对某一个方面有用,不是全能的
  5. 开发工程师建立在框架的基础之上完成开发部分功能 加 框架自身完成部分功能组成一个完整的产品

框架解决的问题:

  1. 框架能实现技术的整合
  2. 提高开发的效率,降低难度

JDBC 访问数据库的优缺点:

  1. 优点:
    1. 直观,好理解
  2. 缺点:
    1. 创建很多对象 Connection,Statement,ResultSet
    2. 注册驱动
    3. 执行SQL语句
    4. 把 ResultSet 转为 Student 对象、List集合
    5. 关闭资源
    6. sql 语句和业务逻辑代码混合在一起

1.3、MyBatis框架概述

MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射

MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集,MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口、 Java的entity对象 作为数据库中的记录

Mybatis的功能架构分为三层:

  1. API接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。接口层一接收 到调用请求就会调用数据处理层来完成具体的数据处理。
  2. 数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。
  3. 基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是 共用的东西,将他们抽取出来作为最基础的组件,为上层的数据处理层提供最基础的支撑

Mybatis 能做什么:

  1. 注册驱动
  2. 创建 jdbc 中使用的 Connection,Statement,ResultSet
  3. 执行 SQL 语句,得到 ResultSet
  4. 处理 ResultSet ,把记录集中的数据转为 java对象,同时将 java对象封装进 List 集合
  5. 关闭资源
  6. 实现 SQL 语句和 java 代码的解耦合

2、 MyBatis框架入门

2.1、入门示例

2.1.1、实现步骤:

  1. 创建数据库表 student(id, name, email, age),新建 maven 项目

  2. 修改 pom.xml 文件

    mybatis 依赖、mysql 驱动、Junit

  3. 创建实体类 Student ,属性名与数据库字段名保持一致

  4. 创建 Dao 接口,定义操作数据库的方法

  5. 创建 mybatis-mapper.xml 文件,编写 SQL 语句

    mapper文件:和 dao 接口在同一包,一个表对应一个 mapper 文件

  6. 创建 mybatis-config.xml 主配置文件,放在 resources 目录下,仅有一个

    定义创建连接实例的数据源对象(DataSource)

    指定 mapper 文件位置

  7. 测试

2.1.2、pom.xml

<dependencies>
  <!-- mybatis -->
  <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.1</version>
  </dependency>
  <!-- mysql-connector-java -->
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.9</version>
  </dependency>
</dependencies>

2.1.3、mapper 文件

<?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">
    <!--
        1、约束文件:http://mybatis.org/dtd/mybatis-3-mapper.dtd
            作用:定义和限制的当前文件中可以使用的标签和属性,以及标签出现的顺序
        2、根标签: mapper
        3、命名空间:namespace ,必须有值,不能为空,唯一值
                推荐使用 Dao 接口的全限定名称
            作用:参与识别 SQL 语句
        4、在 mapper根标签里面使用 insert,update,delete,select标签写SQL
    -->
<mapper namespace="com.dao.StudentDao">
    <!-- 使用 insert,update,delete,select标签写SQL -->
    <!--根据 id 查询学生信息
        <select>:表示查询操作,里面是 select 语句
        id 属性:要执行的 sql 语句的唯一标识,是一个自定义字符串
                推荐使用 dao 接口中的方法名称
        resultType 属性:告诉 mybatis,执行 sql 语句,把数据赋值给哪个类型的 java 对象
                resultType 的值现在使用的是 java 对象的全限定名称
    -->
    <select id="selectStudentById" resultType="com.domain.Student">
        SELECT * FROM `student` WHERE `id`= #{id}
    </select>
</mapper>

2.1.4、mybatis主配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="mydb">
        <environment id="mydb">
            <transactionManager type="JDBC"/>
            <!-- 配置数据源:创建 Connection 对象 -->
            <dataSource type="POOLED">
                <!--driver 驱动信息-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/book?useUnicode=true&amp;characterEncoding=utf-8"/>
                <property name="username" value="root"/>
                <property name="password" value="admin"/>
            </dataSource>
        </environment>
    </environments>
    <!--
        指定其他 mapper 文件的位置,目的是找到其他文件的 SQL 语句
    -->
    <mappers>
        <!--
            使用 mapper标签的 resource 属性指定其他 mapper 文件的路径,这个路径是从 target/classes 路径开始的
            使用注意:
                1、resource属性值,mapper文件的路径是使用 / 分割路径
                2、一个 mapper标签指定一个 mapper 文件
        -->
        <mapper resource="com/dao/StudentDao.xml"/>
    </mappers>
</configuration>

2.1.4、日志功能

在 主配置文件 中添加 setting 标签

<configuration>
    <!--设置sql日志 打印在控制台-->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
</configuration>

2.1.5、创建核心对象 SqlSession 测试

//测试 MyBatis 执行 SQL 语句
@Test
public void test1() throws IOException {
    // 1、定义mybatis主配置文件的位置,从target/calsses路径开始的相对路径
    String config = "mybatis.xml";
    // 2、读取主配置文件,使用mybatis框架中的Resources类
    InputStream inputStream = Resources.getResourceAsStream(config);
    // 3、使用SqlSessionFactoryBuilder类,创建SqlSessionFactory对象
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
    // 4、获取 SqlSession 对象
    SqlSession sqlSession = factory.openSession();
    // 5、指定要执行的 sql 语句的 id
    // sql的id = namespace + "." + select|update|delete|insert标签的 id 属性值
    String sqlId = "com.dao.StudentDao.selectStudentById";
    // 6、通过 SqlSession 的方法,执行 sql 语句
    Student student = sqlSession.selectOne(sqlId,3);
    System.out.println("使用mybatis查询某个学生" + student);
    // 7、关闭 SqlSession 对象
    sqlSession.close();
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0ltVsPdE-1643083876893)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20220119165950621.png)]

2.2、手动提交事务

  1. 自动提交事务:

    当你的 SQL 语句执行完毕之后,提交事务,数据库更新操作之后保存数据

  2. 手动提交事务:

    在你需要提交事务的位置,执行方法,提交事务或回滚事务

  3. MyBatis 是手动提交事务,在执行 insert、update、delete 的时候需要执行 sqlSession.commit();提交事务

2.3、MyBatis重要对象

  1. Resources:MyBatis 框架中的对象,用于读取主配置信息

    InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
    
  2. SqlSessionFactoryBuilder:负责创建 SqlSessionFactory 对象

    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
    
  3. SqlSessionFactory:接口,负责创建 SqlSession 对象,实现类 DefaultSqlSessionFactory

    是重量级对象,创建此对象需要更多资源和时间,所以在项目中有一个就够了

    SqlSessionFactory 接口中的方法:
    	openSession() 获取一个默认的 SqlSession 对象,默认是手动提交事务
    	openSession(true) 表示创建一个自动提交事务的 SqlSession 对象
    
  4. SqlSession:接口,提供了大量执行 SQL 语句的方法,实现类 DefaultSqlSession

    selectOne():执行 SQL 语句,最多得到一行记录,返回多余数据会报错
    					(实现类DefaultSqlSession源码底层设置的list.size() > 1抛异常)
    selectList():执行 SQL 语句,返回多行数据
    selectMap():执行 SQL 语句,得到一个 Map 结果
    insert():执行 insert 语句
    update():执行 update 语句
    delete():执行 delete 语句
    commit():提交事务
    rollback():回滚事务
    
  5. MyBatis 底层是 JDBC ,使用PreparedStatement来执行指定的SQL

注意:SqlSession对象不是线程安全的,使用步骤:

  1. 在方法的内部,执行 SQL 语句之前,先获取 SqlSession 对象
  2. 调用 SqlSession 的方法,执行 SQL 语句
  3. 关闭 SqlSession 对象,执行 sqlSession.close();

2.4、 工具类 MyBatisUtil

因为 SqlSession 对象不是线程安全的,所以对于 SqlSession 对象的获取方法只能是始终获取的是那一个对象

就像之前学习 JDBC 创建的工具类来获取 Connection 对象一样,需要用到静态代码块

public class MyBatisUtil {
    private static SqlSessionFactory factory = null;
    static {
        try {
            InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
            factory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static SqlSession getSqlSession(){
        SqlSession sqlSession = null;
        if (factory != null){
            sqlSession = factory.openSession();  //自动提交事务 factory.openSession(true)
        }
        return sqlSession;
    }
}

使用:

dao 接口 —》 接口实现类,实现类里面调用工具类 MyBatisUtil,并执行SQL语句 —》 测试实现类功能

3、MyBatis框架 Dao 代理

3.1、Dao代理实现数据库操作

3.1.1、传统 Dao 开发方式

mapper 文件:

<mapper namespace="com.dao.StudentDao">
    <select id="selectStudentById" resultType="com.domain.Student">
        SELECT * FROM `student` WHERE `id`= #{id}
    </select>
    <insert id="insertStudent">
        insert into student values(#{id},#{name},#{email},#{age})
    </insert>
</mapper>

StudentDao 接口:

public interface StudentDao {
    Student selectStudentById(Integer id);
    int insertStudent(Student student);
}

StudentDaoImpl 接口实现类:

public class StudentDaoImpl implements StudentDao {
    @Override
    public Student selectStudentById(Integer id) {
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        String sqlId = "com.dao.StudentDao.selectStudentById";
        Student student = sqlSession.selectOne(sqlId,id);
        sqlSession.close();
        return student;
    }
    @Override
    public int insertStudent(Student student) {
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        String sqlId = "com.dao.StudentDao.insertStudent";
        int rows = sqlSession.insert(sqlId, student);
        sqlSession.commit();
        sqlSession.close();
        return rows;
    }
}

测试(Service中调用):

@Test
public void test2(){
    StudentDao studentDao = new StudentDaoImpl();
    Student student = studentDao.selectStudentById(3);
    System.out.println(student);
    Student s = new Student(2,"樱桃","yt@qq.com",33);
    int i = studentDao.insertStudent(s);
    System.out.println("影响了"+i+"条数据");
}

3.1.2、Dao代理分析

dao 代理技术:
就是指由 mybatis 创建 StudentDao 接口的实现类 Proxy(StudentDaoImpl) 对象,完成对 sql 语句的执行,MyBatis 创建一个对象代替手动创建 dao 实现类的功能

在 StudentDaoImpl 实现类中,是通过两行关键代码实现 sql 语句的执行,如下

String sqlId = "com.dao.StudentDao.selectStudentById";
Student student = sqlSession.selectOne(sqlId,id);

在测试方法(Service)中,调用 dao 的方法如下:

StudentDao studentDao = new StudentDaoImpl();
Student student = studentDao.selectStudentById(3);

分析:

  1. 通过测试方法中 Student student = studentDao.selectStudentById(3);

    能得到实现类中的 String sqlId = "com.dao.StudentDao.selectStudentById";

    1. 在测试方法中,通过对象 studentDao 反射能得到全限定类型名称,对象 studentDao 是 StudentDao 类型的,全限定名称为 com.dao.StudentDao
    2. 在测试方法中,方法名 selectStudentById 就是 mapper 文件中的标签 id
  2. 通过 dao 接口中的方法和 mapper 文件中的标签

    能得到实现类中的 Student student = sqlSession.selectOne(sqlId,id);

    1. 根据 dao 接口中的返回值,分析调用哪个查询方法
      如果返回一个对象,调用 sqlSession.selectOne()
      如果返回 List ,调用 sqlSession.selectList()
    2. 根据 mapper 文件中 update|delete|insert 等标签,调用 sqlSession.update() 等方法

使用 dao 代理要求:

1.  mapper 文件中的 `namespace 属性值`,必须是 dao 接口的全限定名称
2.  mapper 文件中 select|update|delete|insert 等标签的 `id 属性值`,必须是 dao 接口中对应方法的方法名称,需要是一模一样

3.1.3、MyBatis代理的实现

使用 SqlSession 对象的方法 getMapper(StudentDao.class)

例如:现在有 StudentDao 接口

SqlSession sqlSession = MyBatisUtil.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
Student student = studentDao.selectStudentById(3);

//上面代码中
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
等同于
StudentDao studentDao = new StudentDaoImpl();
就是获取到实现类对象

代码:

@Test
public void test3(){
    // 1、获取 sqlSession 对象
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    // 2、获取 dao 的代理
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
    Student student = studentDao.selectStudentById(3);
    System.out.println(student);
    // 3、关闭 sqlSession 对象
    sqlSession.close();
}

3.2、mapper与dao的参数

通过 java 程序把数据传入到 mapper 文件中的 SQL 语句,参数主要指 dao 接口方法的形参

3.2.1、parameterType

parameterType :表示参数的类型,指定 dao 方法的形参数据类型

​ 这个形参的数据类型是给 MyBatis 使用,MyBatis 在给 sql 语句的参数赋值时使用

​ PreparedStatement.setXxx(参数位置,参数值)

  1. 第一个用法:使用 java 类型的全限定名称,parameterType=“java.lang.Integer”
  2. 第二个用法:使用 mybatis 定义的别名,parameterType=“integer”
  3. parameterType :mybatis 通过反射机制可以获取 dao 接口方法的参数类型,可以不写

下面使用的是 **java 类型的全限定名称 **

<!--
    parameterType :指定 dao 接口形参的类型
                属性值使用 java 类型的全限定名称或者 mybatis 定义的别名
    mybatis执行的 SQL 语句是:  SELECT * FROM `student` WHERE `id`= ?
     ? 是占位符,使用 JDBC 里面的PreparedStatement执行这样的sql语句

    给 ? 位置赋值
    参数是Integer,执行 setInt()
    参数是String,执行setString()
-->
<select id="selectStudentById" parameterType="java.lang.Integer" resultType="com.domain.Student">
    SELECT * FROM `student` WHERE `id`= #{id}
</select>

下面使用的是 mybatis 定义的别名

<select id="selectStudentById" parameterType="integer" resultType="com.domain.Student">
    SELECT * FROM `student` WHERE `id`= #{id}
</select>

下面不写:

<select id="selectStudentById" resultType="com.domain.Student">
    SELECT * FROM `student` WHERE `id`= #{id}
</select>

3.2.2、仅有一个简单类型参数

简单类型: 指的是 基本数据类型、String类型

接口方法参数仅有一个简单参数:

接口:

//dao 接口方法的形参是一个简单类型参数
Student selectStudentByName(String name);

mapper 文件:

<!--
    dao 接口方法里面是一个简单类型参数
    mapper 文件获取这个参数,使用 #{任意字符}
-->
<select id="selectStudentByName" resultType="com.domain.Student">
    SELECT * FROM `student` WHERE `name`= #{aaaa}
</select>

3.2.3、有多个简单类型参数

简单类型: 指的是 基本数据类型、String类型

接口方法参数有多个简单参数:使用注解 @Param

接口:

/**
 *  dao 接口方法的形参是多个简单类型参数
 *  需要使用mybatis框架提供的注解 @Param 命名参数
 *      位置:在形参类型的前面
 *      属性: value 自定义参数名称,名称与mapper文件中#{}里面的名称一致
 */
Student selectStudentByNameOrAge(@Param(value = "myname")String name, 
                                 @Param(value = "myage")Integer age);

mapper 文件:

<!--
    dao 接口方法里面是多个简单类型参数
    当使用了  @Param 命名参数后,例如 @Param(value = "myname")
    在 mapper 文件中,使用 #{命名的参数名},例如 #{myname}
-->
<select id="selectStudentByNameOrAge" resultType="com.domain.Student">
    SELECT * FROM `student` WHERE `name`= #{myname} or `age`= #{myage}
</select>
接口方法参数有多个简单参数:按位置传递参数(了解)

参数位置:dao 接口中方法的形参列表,从左往右,位置是:0、1、2…

语法格式:#{arg0},#{arg1}

接口:

Student selectStudentByNameOrAge(Integer age, String name);

mapper 文件:

<select id="selectStudentByNameOrAge" resultType="com.domain.Student">
    SELECT * FROM `student` WHERE `name`= #{arg1} or `age`= #{arg0}
</select>

3.2.4、对象类型参数

方法的形参是一个 java 对象,这个 java 对象表示多个参数,使用对象的属性值作为参数使用

使用简单的语法获取对象属性 #{属性名}

java 对象:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    private Integer id;
    private String name;
    private String email;
    private Integer age;
}

@Data
public class QueryParam {
    private String p1;
    private Integer p2;
}

接口:

/**
 *  使用一个 java 对象作为参数,对象的属性有set、get方法
 */
int insertStudent(Student student);
Student selectStudentByParam(QueryParam queryParam);

mapper 文件:

<!--
    dao 接口方法里面是一个 java 对象作为参数,使用对象的属性作为参数值使用
    简单的语法:#{属性名},mybatis 调用此属性的 get 方法获取属性值
-->
<insert id="insertStudent">
    INSERT INTO `student`(`name`,`email`,`age`) VALUES(#{name},#{email},#{age})
</insert>
<select id="selectStudentByParam" resultType="com.domain.Student">
    SELECT * FROM `student` WHERE `name`= #{p1} or `age`= #{p2}
</select>
使用复杂的语法获取对象属性 #{属性名,javaType=属性类型,jdbcType=数据库参数类型}

使用java对象传递参数,对象的属性值就是SQL需要的参数类型值

语法格式: #{属性名,javaType=属性类型,jdbcType=数据库参数类型}

  1. 对象的属性类型就是全限定类型名称
  2. 数据库参数类型需要参考 MyBatis 的文档 mybatis-3.5.1.pdf 第 43 页 4.1.5.4 小节,如下:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zQzNdbRj-1643083876897)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20220120142505582.png)]

接口:

Student selectStudentByObject(Student student);

mapper 文件:

<select id="selectStudentByObject" resultType="com.domain.Student">
    SELECT * FROM `student` WHERE 
        `name`= #{name,javaType=java.lang.String,jdbcType=VARCHAR}
         or
         `age`= #{age,javaType=java.lang.Integer,jdbcType=INTEGER}
</select>

3.2.5、Map 类型参数(了解)

map 作为 dao 接口的参数,使用 key 获取参数值,在 mapper 文件中,语法格式 #{key}

测试使用的时候才能确定 Map 的key名称,不好用

接口:

//使用 Map 作为参数
List<Student> selectStudentByMap(Map<String,Object> map);

mapper 文件:

<!--
    dao 接口方法里面是使用 Map 作为形参
    在 mapper 文件中获取 map 的值,是通过 key 来获取的 #{key}
-->
<select id="selectStudentByMap" resultType="com.domain.Student">
    SELECT * FROM `student` WHERE `name`= #{key1} or `age`= #{key2}
</select>

测试:

@Test
public void test3(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);

    Map<String,Object> map = new HashMap<>();
    map.put("key1","小草莓");
    map.put("key2",12);
    List<Student> students = studentDao.selectStudentByMap(map);

    students.forEach(System.out::println);
    sqlSession.close();
}

3.3、 # 与 $

3.3.1、 #占位符

语法:#{字符}

mybatis 处理 #{} ,底层使用的jdbc对象是 PreparedStatement 对象

<select id="selectStudentById" parameterType="integer" resultType="com.domain.Student">
    SELECT * FROM `student` WHERE `id`= #{id}
</select>


mybatis创建 PreparedStatement 对象,执行 SQL 语句
	String sql = "SELECT * FROM student WHERE id = ?";
	PreparedStatement ps = conn.preparedStatement(sql);
	ps.setInt( 1, 2);   //传递参数
	ResultSet rs = ps.executeQuery();   //执行 SQL 语句

#{} 占位符特点:

  1. 使用的是 PreparedStatement 对象,执行sql语句,效率高
  2. 使用的是 PreparedStatement 对象,能避免sql注入,sql语句执行更安全
  3. #{} 常常作为列值使用,位于等号右侧,#{} 位置的值和数据类型有关

3.3.2、 $占位符

语法:${字符}

mybatis 执行 ${} 占位符的sql语句

<select id="selectStudentById" parameterType="integer" resultType="com.domain.Student">
    SELECT * FROM `student` WHERE `id`= ${studentId}
</select>


${} 表示字符串连接,把SQL语句的其他内容和 ${} 内容使用字符串(+)连接的方式连接在一起
	String sql = "SELECT * FROM student WHERE id = " + "2";
mybatis创建 Statement 对象,执行 SQL 语句
	Statement s = conn.createStatement(sql);
	ResultSet rs = s.executeQuery();   //执行 SQL 语句

${} 占位符特点:

  1. ${} 使用 Statement 对象,执行sql语句,效率低
  2. 使用字符串连接的方式,有 SQL注入风险,有代码安全问题
  3. ${} 数据是原样使用,不会区分数据类型
  4. ${} 常用作表名或列名,在能保证数据安全的情况下使用 ${}

$占位符使用的案例:

接口:

// ${} 占位符的使用,需要使用 @Param 命名
List<Student> selectStudent(@Param(value = "myname") String name);

mapper 文件:

<select id="selectStudent" resultType="com.domain.Student">
    SELECT * FROM `student` WHERE `name`= ${myname}
</select>

失败测试:

List<Student> students = studentDao.selectStudent("zs");
students.forEach(System.out::println);

下面可以看到 ${} 数据是原样使用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fAYJJWlX-1643083876900)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20220120153608163.png)]

相当于直接复制到 SQLyog 去执行,发现需要 引号 ,因为是字符串

成功测试:(说明有SQL注入风险)

List<Student> students = studentDao.selectStudent("'zs'");
students.forEach(System.out::println);

3.3.3、 $占位符表示表名或列名

所以只可以使用 $占位符 表示表名或列名

在 mysql 里面排序的代码应该是:
SELECT * FROM `student` ORDER BY id

而不是:
SELECT * FROM `student` ORDER BY "id"

通用的排序操作

接口:

List<Student> queryStudent(@Param(value = "myname") String s
                           , @Param(value = "colName") String name);

mapper 文件:

<select id="queryStudent" resultType="com.domain.Student">
    SELECT * FROM `student` WHERE `name`= #{myname} order by ${colName}
</select>

测试:

List<Student> students = studentDao.queryStudent("zs","id");
students.forEach(System.out::println);

3.4、封装输出结果

封装输出结果:MyBatis 执行 SQL 语句,得到 ResultSet,转换为 java 对象

3.4.1、resultType

resultType 属性:在执行 select 时使用,作为 select 标签的属性出现

resultType :表示结果类型,mysql执行 SQL 语句,得到 java 对象的类型,属性值有两种

  	  1. Java类型的全限定名称
             	  2. 使用别名
3.4.1.1、对象类型

dao 接口:

Student selectStudentById(Integer id);

mapper 文件:

<select id="selectStudentById" resultType="com.domain.Student">
    SELECT * FROM `student` WHERE `id`= #{id}
</select>


resultType :上面使用的是java类型的全限定名称,表示 mybatis 执行sql,把ResultType中的数据转为Student类型的对象。MyBatis会做以下操作:
	1、调用com.domain.Student的无参构造器,创建对象
		Student student = Class.forName("Student").newInstance(); //使用反射创建对象
	2、同名的列赋值给同名的属性
		Student.setInt(rs.getInt("id"));
		Student.setString(rs.getString("name"));
	3、得到java对象,如果dao接口返回值是List集合,mybatis把Student放入List集合

所以执行 Student mystudent = studentDao.selectStudentById(1); 得到数据库中 id=1 这行数据
这行数据的列值,赋值给mystudent对象的属性,得到mystudent这个对象,就是得到 id=1 这行数据

测试:

Student student = studentDao.selectStudentById(12);
注意:

resultType 会默认将同名的列赋值给同名的属性,这意味着,如果属性名与数据库列名不一致,就无法进行赋值

TestStudent对象如下:

@Data
public class TestStudent {
    private Integer ttid;
    private String ttname;
    private String email;
    private Integer age;
}

dao 接口:

TestStudent selectStudentById(@Param(value = "stuid")Integer id);

mapper 文件:

<select id="selectStudentById" resultType="com.domain.TestStudent">
    SELECT * FROM `student` WHERE `id`= #{stuid}
</select>

测试结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0eV4wO5V-1643083876901)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20220121155308700.png)]

输出的对象信息,对于与列名不同的属性名,不会有值

3.4.1.2、简单类型

dao 接口:

Long selectCount();

mapper 文件:

<!--
    执行 SQL 语句,得到的是一个值(一行一列)
		resultType="java.lang.Long"
-->
<select id="selectCount" resultType="long">
    SELECT count(*) FROM `student`
</select>
3.4.1.3、Map 类型

注意:

SQL执行得到的是一行记录,才可以使用

dao 接口:

//查询结果返回是一个Map
Map<Object, Object> selectMap(Integer id);

mapper 文件:

<!--
    执行SQL语句,得到一个Map结构数据,mybatis执行SQL,把ResultSet转为map
    SQL执行结果,列名作为map的key,列值作为对应的value
        只有当SQL执行得到的是一行记录,才可以使用,否则会报错
		
-->
<select id="selectMap" resultType="java.util.HashMap">
    SELECT id,name FROM `student` WHERE `id`= #{stuid}
</select>

测试:

StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
Map<Object, Object> map = studentDao.selectMap(3);
System.out.println(map);        //{name=大西瓜, id=3}
System.out.println(map.get("name"));       //大西瓜
System.out.println(map.get("id"));         //3
3.4.1.4、resultType 练习题

练习题:

输入省份 id,得到省份id、省份name、城市id、城市name

直接使用 mysql 查询,代码如下:

SELECT 
    p.`id` pid,
    p.`name` pname,
    c.`id` cid,
    c.`name` cname 
  FROM
    `province` p 
    INNER JOIN `city` c 
      ON c.`provinceid` = p.`id` 
  WHERE p.`id` = 1 

使用 MyBatis

ProAndCity 类定义如下:

@Data
public class ProAndCity {
    private Integer pid;
    private String pname;
    private Integer cid;
    private String cname;
}

dao 接口:

List<ProAndCity> selectProAndCityById(@Param(value = "proid")Integer id);

mapper 文件:

<select id="selectProAndCityById" resultType="com.domain.ProAndCity">
    SELECT p.`id` pid,p.`name` pname,c.`id` cid,c.`name` cname
        FROM `province` p INNER JOIN `city` c ON c.`provinceid`=p.`id`
        WHERE p.`id` = #{proid}
</select>

测试:

@Test
public void test1(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
    List<ProAndCity> proAndCities = studentDao.selectProAndCityById(1);
    System.out.println(proAndCities);
    sqlSession.close();
}

输出结果:

[
	ProAndCity(pid=1, pname=江苏, cid=1, cname=南京), 
	ProAndCity(pid=1, pname=江苏, cid=2, cname=苏州)
]

3.4.2、resultMap

resultMap :结果映射。自定义列名和 java 对象属性的对应关系,常用在列名和属性名不同的情况

使用步骤:

  1. 先定义 resultMap 标签,指定列名和属性名的对应关系
  2. 在 select 标签使用 resultMap 属性,指定上面定义的 resultMap 标签的 id 值

TestStudent对象如下:

@Data
public class TestStudent {
    private Integer ttid;
    private String ttname;
    private String email;
    private Integer age;
}

dao 接口:

TestStudent selectStudentById(@Param(value = "stuid")Integer id);

mapper 文件:

<!-- 使用 resultMap 标签定义列和属性的关系 -->
<!--
    定义 resultMap
        id :给 resultMap 的映射关系起个名称,唯一值
        type :java 类型的全限定名称
-->
<resultMap id="myMap" type="com.domain.TestStudent">
    <!-- 定义列名和属性名的对应关系,列名和属性名一致的不用定义 -->
    <!--主键列使用 id 标签-->
    <id column="id" property="ttid"/>
    <!--非主键列使用 result 标签-->
    <result column="name" property="ttname"/>
</resultMap>

<!-- 使用 resultMap 属性,指定映射关系的 id 值-->
<select id="selectStudentById" resultMap="myMap">
    SELECT * FROM `student` WHERE `id`= #{stuid}
</select>

测试结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ws8PA8ug-1643083876902)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20220121171805697.png)]

3.5、自定义别名

自定义别名步骤:

  1. 在主配置文件中,使用 typeAliases标签声明别名,注意位置在设置日志之后
  2. 在 mapper 文件中,resultType="别名"

3.5.1、第一种: 使用 typeAlias 标签

声明别名, MyBatis 主配置文件中

<!--声明别名-->
<typeAliases>
    <!--
        第一种语法格式:
            type :java类型的全限定名称(自定义类型)
            alias : 自定义别名

		优点:别名自定义
		确定:每个类型必须单独定义
    -->
    <typeAlias type="com.domain.Student" alias="stu"/>
</typeAliases>

mapper 文件中

<select id="selectStudentById" resultType="stu">
    SELECT * FROM `student` WHERE `id`= #{id}
</select>

3.5.2、第二种:使用 package 标签

如果有重复类名会报错

声明别名, MyBatis 主配置文件中

<!--声明别名-->
<typeAliases>
    <!--
        第二种方式:
            name :包名,mybatis 会把这个包中所有类名作为别名(不用区分大小写)

		优点:使用方便,一次性给多个类定义别名
		缺点:别名不能自定义,必须是类名,如果有重复类名会报错
    -->
    <package name="com.domain"/>
</typeAliases>

mapper 文件中

<select id="selectStudentById" resultType="student">
    SELECT * FROM `student` WHERE `id`= #{id}
</select>

当出现以下情况,执行时候会报错

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vrsfZ6x9-1643083876903)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20220121154553275.png)]

3.6、列名和属性名不一致

在查询数据的时候,当数据库的列名与 想封装数据的java对象的属性名 布依族吧的时候

解决方法:

  1. 使用 resultMap :自定义列名和属性名称对应关系
  2. 使用 resultType :使用列别名,让别名和 java 对象的属性名称一致(详见3.4.1.4、resultType 练习题)

3.7、like

在学习 mysql 的时候,使用 like 来进行模糊查询,那么在 mybatis 中如何表现?

3.7.1、第一种方式

在 java 程序中,把 like 的内容组装好,把这个内容传入到 SQL 语句

WHERE name like #{name}String name = "%大%";

dao 接口:

//like第一种方式
List<Student> queryLikeOne(@Param(value = "name") String name);

mapper 文件:

<select id="queryLikeOne" resultType="com.domain.Student">
    SELECT * FROM `student` WHERE `name`like #{name}
</select>

测试:

@Test
public void test(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
    String name = "%大%";
    List<Student> students = studentDao.queryLikeOne(name);
    students.forEach(System.out::println);
    sqlSession.close();
}

执行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oeh5evI5-1643083876904)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20220121173602157.png)]

3.7.2、第二种方式

SQL 语句 like 的格式为:WHERE name like "%"空格#{name}空格"%"

dao 接口:

//like第二种方式
List<Student> queryLikeTwo(@Param(value = "name") String name);

mapper 文件:

<select id="queryLikeTwo" resultType="com.domain.Student">
	SELECT * FROM `student` WHERE `name` like "%" #{name} "%"
</select>

测试:

String name = "大";
List<Student> students = studentDao.queryLikeTwo(name);

4、MyBatis框架动态 SQL

动态 SQL :同一个 dao 方法,根据不同的条件可以表示不同的 SQL 语句,主要是 where 部分有变化

使用 mybatis 提供的标签,实现动态 SQL ,主要使用 if、where、foreach、sql

使用动态 SQL 的时候,dao 方法的形参使用 java 对象

什么时候使用动态 SQL ?

当需要实现多条件查询的时候,比如可以根据姓名和年龄查询信息,同时还要实现年龄为空,仅使用姓名也可以查询信息;或者姓名为空,仅使用年龄也可以查询信息

4.1、if 标签

语法:
<if test="boolean判断结果">
	sql 代码
</if>

在 mapper 文件中,使用如下:

当条件满足的时候,会将 if 标签内的语句直接加在当前 SQL 语句后面

比如,当条件全部满足的时候,就是SELECT * FROM student where name = #{name} or age > #{age}

<select id="selectStudent" resultType="com.domain.Student">
    SELECT * FROM `student` where
    <if test="name!=null and name!=''">
        name = #{name}
    </if>
    <if test="age > 0">
        or age > #{age}
    </if>
</select>

dao 接口:

// if
List<Student> selectStudent(Student student);

测试:

@Test
public void test(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
    Student student = new Student(null,"zs",null,88);
    List<Student> students = studentDao.selectStudent(student);
    students.forEach(System.out::println);
    sqlSession.close();
}

执行结果如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4nHXT5m9-1643083876905)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20220124164150508.png)]

注意,此时会有两个问题:

  1. 对于 test 里面的判断语句的 大于小于号,需要使用实体符号替换

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U5Y8sJZm-1643083876906)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20220124164804574.png)]

  2. 因为当test的条件满足的时候,是直接将语句拼接在SQL语句后面,所以当有多个 if 标签的时候,if语句之间需要有 and、or 连接词

  3. 如果每一句都有 and、or 连接词,但是第一个if标签的test判断不通过的时候,就会出现SELECT * FROM student where or age > #{age},报错

解决办法:

既然要保证紧挨着 where 关键字的语句没有 and、or 连接词

不妨自己写一个语句紧挨着 where 关键字,然后在后面的 if 标签里面每个句子都是用连接词,如下:

<select id="selectStudent" resultType="com.domain.Student">
    SELECT * FROM `student` where 1!=1
    <if test="name!=null and name!=''">
        or name = #{name}
    </if>
    <if test="age &gt; 0">
        or age > #{age}
    </if>
</select>

4.2、where 标签

使用 if 标签时,容易引起 SQL 语句语法错误。所以使用 where 标签来解决 if 产生的语法问题

where 标签内是一个或多个 if 标签,每个if标签的语句都要使用 and、or 连接词

​ where 标签会删除和它最近的 and、or 连接词;

​ 当有一个 if 标签判断条件为 true 时,where 标签会转为 WHERE关键字拼接在 SQL 语句后面;

​ 当没有任何 if 标签条件判断为 true 时,会忽略整个 where 标签;

语法:
<where>
    <if test="条件1">sql代码1</if>
    <if test="条件2">sql代码2</if>
</where>

dao 接口:

// where
List<Student> selectWhere(Student student);

mapper 文件中:

<select id="selectWhere" resultType="com.domain.Student">
    SELECT * FROM `student`
    <where>
        <if test="name!=null and name!=''">
            or name = #{name}
        </if>
        <if test="age &gt; 0">
            or age > #{age}
        </if>
    </where>
</select>

测试:

@Test
public void test(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
    Student student = new Student(null,"",null,88);
    List<Student> students = studentDao.selectWhere(student);
    students.forEach(System.out::println);
    sqlSession.close();
}

执行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eo1zqwkL-1643083876906)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20220124172958092.png)]

4.3、foreach 标签

foreach 标签用于实现对于数组与集合的遍历,一般使用在 IN 语句中,需要注意:

​ collection 表示要遍历集合的类型,list、array 等

​ open、close、separator 是对遍历内容的 SQL 拼接

语法:
<foreach collection="集合类型" open="开始的字符" close="结束的字符" item="集合中的成员"
         		separator="集合成员之间的分隔符">
    #{item}
</foreach>

标签属性:
collection:表示循环的对象是数组、集合
			如果 dao 接口方法形参是数组,使用collection="array"
			如果 dao 接口方法形参是集合,使用collection="list"
open:循环开始时的字符         sql.append("(");   //循环之前
close:循环结束时的字符        sql.append(")");   //循环之后
item:集合成员,自定义变量      Integer item = idlist.get(i);  // 集合中的成员 item
separator:成员之间的分隔符    sql.append(",");  //集合成员之间的分隔符
#{item}:获取集合成员的值

4.3.1、手动实现循环

当需要查询 id 在集合 idlist{19,22,23} 中的学生信息,即SELECT * FROM student WHERE id IN(19,22,23)

@Test
public void test2(){
    List<Integer> idlist = new ArrayList<>();
    idlist.add(19);
    idlist.add(22);
    idlist.add(23);
    //查询集合中 id 的学生信息
    // SELECT * FROM student WHERE id IN(19,22,23)
    StringBuffer sql = new StringBuffer("");
    sql.append("SELECT * FROM student WHERE id IN");
    // 使用循环,将集合中的数据追加到 sql 字符串中
    sql.append("(");   //循环之前
    for (int i = 0; i < idlist.size(); i++) {
        Integer item = idlist.get(i);  // 集合中的成员 item
        sql.append(item);  //添加成员到 sql 字符串
        sql.append(",");  //集合成员之间的分隔符
    }
    sql.deleteCharAt(sql.length()-1);  //删除最后多余的逗号,
    sql.append(")");   //循环之后
    System.out.println(sql);
}

输出结果:

SELECT * FROM student WHERE id IN(19,22,23)

4.3.2、遍历 List<简单类型>

dao 接口:

// foreach --遍历 List<简单类型>
List<Student> selectForeachOne(List<Integer> idlist);

mapper 文件:

<select id="selectForeachOne" resultType="com.domain.Student">
    SELECT * FROM `student` where id in
    <foreach collection="list" open="(" close=")" item="myid" separator=",">
        #{myid}
    </foreach>
</select>

测试:

@Test
public void testSelectForeachOne(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
    List<Integer> idlist = new ArrayList<>();
    idlist.add(19);
    idlist.add(22);
    idlist.add(23);
    List<Student> students = studentDao.selectForeachOne(idlist);
    students.forEach(System.out::println);
    sqlSession.close();
}

注意:

  1. 因为传入的是集合,需要判断集合是否为 null,如果在 mapper 文件中使用 if 标签,如下

    <select id="selectForeachOne" resultType="com.domain.Student">
        SELECT * FROM `student` WHERE id IN
        <if test="list != null">
            <foreach collection="list" open="(" close=")" item="myid" separator=",">
                #{myid}
            </foreach>
        </if>
    </select>
    
    但当集合为 null 的时候,执行的SQL语句就是 SELECT * FROM `student` where id in,有语法错误
    
  2. 如果将 where 语句也放在 if 标签里面,如下

    <select id="selectForeachOne" resultType="com.domain.Student">
        SELECT * FROM `student` 
        <if test="list != null">
            WHERE id IN
            <foreach collection="list" open="(" close=")" item="myid" separator=",">
                #{myid}
            </foreach>
        </if>
    </select>
    
    1、当集合不为 null但是集合内为空的时候,就会执行if标签,但是遍历没有数据
    	执行的SQL语句是 SELECT * FROM `student` where id in,有语法错误
    2、且当集合为 null的时候,会查询出所有学生信息
    	执行的SQL语句是 SELECT * FROM `student`,存在信息泄露问题
    

解决方法:

<select id="selectForeachOne" resultType="com.domain.Student">
    SELECT * FROM `student` 
    <if test="list != null and list.size &gt; 0">
        WHERE id IN
        <foreach collection="list" open="(" close=")" item="myid" separator=",">
            #{myid}
        </foreach>
    </if>
</select>

4.3.2、遍历 List<对象类型>

dao 接口:

// foreach --遍历 List<对象类型>
List<Student> selectForeachTwo(List<Student> studentList);

mapper 文件:

<select id="selectForeachTwo" resultType="com.domain.Student">
    SELECT * FROM `student`
    <if test="list != null and list.size &gt; 0">
        where id in
        <foreach collection="list" open="(" close=")" item="stu" separator=",">
            #{stu.id}
        </foreach>
    </if>
</select>

测试:

@Test
public void testSelectForeacTwo(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
    List<Student> studentList = new ArrayList<>();
    studentList.add(new Student(22,null,null,null));
    studentList.add(new Student(23,null,null,null));
    List<Student> students = studentDao.selectForeachTwo(studentList);
    students.forEach(System.out::println);
    sqlSession.close();
}

4.4、代码片段 sql 标签

sql 标签表示一段 SQL 代码,可以是表名、几个字段、WHERE条件等都可以,可在其他地方复用 sql 标签的内容

使用方式:

	1、在mapper 文件中定义sql代码片段 <sql id="唯一字符串"> 部分sql语句 </sql>
	2、在其他位置,使用 <include>标签引用某个代码片段

示例:

<!-- 定义代码片段 -->
<sql id="ss">
    SELECT * FROM `student`
</sql>
<sql id="sl">
    name,email
</sql>


<!-- 使用 -->
<!-- 表示 SELECT * FROM `student` where name = #{name} -->
<select id="selectStudent" resultType="com.domain.Student">
    <include refid="ss"/> where name = #{name}
</select>

<!-- 表示 SELECT name,email FROM `student` where name = #{name} -->
<select id="queryStudent" resultType="com.domain.Student">
    SELECT <include refid="sl"/> FROM `student` where name = #{name}
</select>

5、主配置文件

MyBatis配置文件两大类:1、主配置文件; 2、 mapper 文件

  1. 主配置文件:提供MyBatis全局设置,包含日志、数据源、mapper文件位置
  2. mapper 文件:写 sql 语句的,一个数据库表对应一个 mapper 文件

5.1、settings

settings 是全局设置,影响整个MyBatis的运行,一般使用默认值就可以了,用的最多的是日志设置

一个配置完整的 settings 元素的示例如下:

<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="multipleResultSetsEnabled" value="true"/>
  <setting name="useColumnLabel" value="true"/>
  <setting name="useGeneratedKeys" value="false"/>
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  <setting name="defaultExecutorType" value="SIMPLE"/>
  <setting name="defaultStatementTimeout" value="25"/>
  <setting name="defaultFetchSize" value="100"/>
  <setting name="safeRowBoundsEnabled" value="false"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  <setting name="localCacheScope" value="SESSION"/>
  <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>

5.2、typeAliases 类型别名

<typeAliases>
    <!--
        第一种语法格式:
            type :java类型的全限定名称(自定义类型)
            alias : 自定义别名

		优点:别名自定义
		确定:每个类型必须单独定义
    -->
    <typeAlias type="com.domain.Student" alias="stu"/>
    <typeAlias type="com.vo.QueryParam" alias="quep"/>
    <!--
        第二种方式:
            name :包名,mybatis 会把这个包中所有类名作为别名(不用区分大小写)

		优点:使用方便,一次性给多个类定义别名
		缺点:别名不能自定义,必须是类名,如果有重复类名会报错
    -->
    <package name="com.domain"/>
    <package name="com.vo"/>
</typeAliases>

5.3、配置环境

environments :环境标签,在该标签里面可以配置多个 environment

​ 属性:default ,必须是某个 environment 的 id 属性值,表示 mybatis 默认连接的数据库

environment :表示一个数据库的连接信息

​ 属性:id 自定义的环境的标识,唯一值

transactionManager :事务管理器

​ 属性:type 表示事务管理器的类型

​ 属性值: 1) JDBC ,使用 Connection 对象,由 MyBatis 自己完成事务的处理

​ 2) MANAGED ,管理,表示把事务的处理交给容器实现(由其他软件完成事务提交与回滚)

dataSource :数据源,创建 Connection 对象,连接数据库

​ 属性:type 数据源的类型

​ 属性值:1) POOLED ,mybatis会在内存中创建PooledDataSource类,管理多个Connection连接对象, 使用连接池

​ 2)UNPOOLED ,不使用连接池,mybatis创建UnPooledDataSource类,每次执行SQL语句先 创建Connection对象,再执行SQL语句,最后关闭Connection

​ 3) JNDI ,java的命名和目录服务

<environments default="mydb">
    <environment id="mydb">
        <transactionManager type="JDBC"/>
        <!-- 配置数据源:创建 Connection 对象 -->
        <dataSource type="POOLED">
            <!--driver 驱动信息-->
            <property name="driver" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/book"/>
            <property name="username" value="root"/>
            <property name="password" value="admin"/>
        </dataSource>
    </environment>
</environments>

5.4、使用数据库属性配置文件

需要把数据库的配置信息放到一个单独文件中,独立管理。文件扩展名是 properties ,在这个文件中,使用自定义 key=value 的格式表示数据

使用步骤:

  1. 在 resources 目录中,创建 xxx.properties

  2. 在文件中,使用 key=value 的格式定义数据

    jdbc.username=root

  3. 在 mybatis 主配置文件中,使用 properties 标签引用外部的属性配置文件

  4. 在使用值的位置,使用 ${key} 获取 key 对应的 value (等号右侧的值)

示例:

resources/jdbc.properties 文件

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/book
jdbc.username=root
jdbc.password=admin

resources/mybatis.xml 主配置文件

<environments default="mydb">
    <environment id="mydb">
        <transactionManager type="JDBC"/>
        <!-- 配置数据源:创建 Connection 对象 -->
        <dataSource type="POOLED">
            <!--driver 驱动信息-->
            <property name="driver" value="${jdbc.driver}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </dataSource>
    </environment>
</environments>

5.5、 mapper 标签

使用 mapper 指定其他 mapper 文件的位置

mapper 标签使用的格式有两个常用的方式

<!--
    指定其他 mapper 文件的位置,目的是找到其他文件的 SQL 语句
-->
<mappers>
    <!-- 第一种方式,resource="mapper文件的路径"
        使用 mapper标签的 resource 属性指定其他 mapper 文件的路径,这个路径是从 target/classes 路径开始的
        使用注意:
            1、resource属性值,mapper文件的路径是使用 / 分割路径
            2、一个 mapper标签指定一个 mapper 文件
        优点:文件清晰,加载的文件是明确的,文件的位置比较灵活
        缺点:文件较多时,代码量会比较大,管理难度大
    -->
    <mapper resource="com/dao/StudentDao.xml"/>
    <!-- 第二种方式,使用 <package name="mapper文件所在的包名"/>
        特点:
            1、resource属性值,mapper文件的路径是使用 . 分割路径
            2、将这个包中所有 mapper 文件,一次加载
        使用要求:
            1、mapper文件和 dao 接口在同一目录
            2、mapper文件和 dao 接口名称完全一样
    -->
    <package name="com.dao"/>
</mappers>

6、PageHelper分页插件

PageHelper 做数据分页,会在你的 select 语句后面加入分页的SQL内容

比如如果使用 mysql 数据库,它就会在 select * from student 后面加入 limit 语句

使用步骤:

  1. 加入 pagehelper 依赖

    <!-- pagehelper -->
    <dependency>
      <groupId>com.github.pagehelper</groupId>
      <artifactId>pagehelper</artifactId>
      <version>5.1.0</version>
    </dependency>
    
  2. 在 mybatis 主配置文件,加入 plugin 声明(environments元素前)

    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
    </plugins>
    
  3. 在 select 语句之前,调用 PageHelper.startPage(页码,每页大小)

    @Test
    public void testPage(){
        // 1、获取 SqlSession
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        // 2、获取 dao 的代理
        StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
    
        //调用 PageHelper 的方法
        PageHelper.startPage(2,2);
    
        List<Student> students = studentDao.queryStudent();
        students.forEach(System.out::println);
        // 3、关闭 SqlSession
        sqlSession.close();
    }
    

PageHelper 做数据分页,会在你的 select 语句后面加入分页的SQL内容

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lx12H5jo-1643083876908)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20220125120134660.png)]

mapper 文件:

<select id="queryStudent" resultType="com.domain.Student">
    SELECT * FROM `student`
</select>

dao 接口:

List<Student> queryStudent();

7、获取插入数据生成的主键(了解)

在开发中,如果需要获取到数据库中自动生成的主键,那么使用 MyBatis 应该如何实现呢?

<insert id="insert" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
    INSERT INTO `user`(`name`,`age`,`salary`) VALUES(#{name},#{age},#{salary})
</insert>

useGeneratedKeys:是否要获取自动生成的主键
keyColumn:表中的主键列
keyProperties:主键列对应的 User 类的属性
以上就表示从获取哪个列的值封装到哪个属性中

通过上面的配置,在执行了保存操作后,mybatis 会自动将主键值封装到传递进来的 User 对象的 id 属性中

所以,此时的 User 对象的 id 属性就有值了(在保存之前是没有的)

parameterType :
该属性表示参数类型,此处插入操作可以不用写,因为插入操作调用的时候用户会传入对象,由MyBatis自己通过用户传入的对象去推导

在实现类中获取 id 值,或者在测试类中获取 id 值都可,但是注意要在事务提交之后进行获取才行

测试类中:

img

实现类中:

img

执行结果:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值