MyBatis入门与实践全总结

MyBatis入门与实践全总结

MyBatis入门与实践全总结

一、 MyBatis简介

mybatis
思维导图:
mybatis

1.1 MyBatis历史

  • MyBatis最初是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation迁移到了Google Code。随着开发团队转投Google Code旗下, iBatis3.x正式更名为MyBatis。
  • 代码于2013年11月迁移到Github
  • iBatis一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。 iBatis提供的持久层框架包括SQL Maps(MyBatis封装JDBC过程)和Data Access Objects(DAO)。

1.2 什么是 MyBatis

  • MyBatis 是一款优秀的持久层框架
  • 它支持定制化 SQL、存储过程以及高级映射。
  • MyBatis 免除了几乎所有的 JDBC 代码以及手动设置参数和手动获取结果集的工作
  • MyBatis 可以通过简单的 XML(常用) 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通的 Java 对象)为数据库中的记录。
  • MyBatis 是一个 半自动的ORM(Object Relation Mapping,【Object:对象(实体类) ;Relation:关系(关系型数据库的数据);Mapping:映射】,将实体类对象与数据库中的数据形成映射关系)框架。(_JDBC_纯手动;MyBatis 半自动,需要自己写sql语句;Hibernate全自动

1.3 MyBatis下载

MyBatis下载:
MyBatis下载
MyBatis下载(Maven仓库)
MyBatis官方文档网址:MyBatis官方文档网址
中文文档:中文文档
具体编程,可以详见文档!

MyBatisMaven项目:
mybatis

点击上图中的,Download Latest 下载最新版本的MyBatis:此时的最新版本是3.5.9,点击mybatis-3.5.9.zip即可下载MyBatis

mybatis

mybatis-3.5.9.zip是jar包
Source code (zip)和 Source code (tar.gz)是不同格式的MyBatis源码,tar.gz是linux系统下的压缩文件格式

mybatis

我们平时创建项目一般都是Maven项目,因此mybatis-3.x.x.jar包基本上不需要自己下载,官方文档倒是可以下载下来看看**。**

1.4 持久化

数据持久化

  • 持久化就是将程序的数据在持久状态和瞬时状态转化的过程
    • 瞬时状态:内存断电即失
    • 持久状态:数据库(jdbc)、IO文件持久化(相比较数据库浪费资源)
  • 生活中持久化例子:冷藏、罐头

为什么需要持久化?

  • 有一些对象,不能让它丢掉
  • 内存太贵了(外在原因)

1.5 持久层

Dao,Service, Controller

  • 完成持久化工作的代码块 Dao
  • 层界限十分明显

1.6 为什么需要Mybatis

  • 帮助程序员将数据存入到数据库
  • 方便
  • 传统的JDBC代码太复杂,简化、框架、自动化
  • 不用mybatis也可以,更容易上手。技术没有高低之分

优点

  1. 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件。易于学习,易于使用。通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
  2. 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。
  3. 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离提高了可维护性
  4. 提供映射标签,支持对象与数据库的orm字段关系映射。
  5. 提供对象关系映射标签,支持对象关系组建维护。
  6. .提供xml标签,支持编写动态sql。
  7. 最重要的一点:使用的人多!

未来: mybatis => spring | spring mvc | sring boot

1.7 和其它持久化层技术对比

  • JDBC
  1. SQL 夹杂在Java代码中耦合度高,导致硬编码内伤
  2. 维护不易且实际开发需求中 SQL 有变化,频繁修改的情况多见
  3. 代码冗长,开发效率低
  • Hibernate 和 JPA
  1. 操作简便,开发效率高
  2. 程序中的长难复杂 SQL 需要绕过框架
  3. 内部自动生产的 SQL,不容易做特殊优化
  4. 基于全映射的全自动框架,大量字段的 POJO 进行部分映射时比较困难。
  5. 反射操作太多,导致数据库性能下降
  • MyBatis
  1. 轻量级,性能出色
  2. SQL 和 Java 编码分开,功能边界清晰。Java代码专注业务、SQL语句专注数据
  3. 开发效率稍逊于HIbernate,但是完全能够接受

二、搭建MyBatis

开发环境

IDE:idea 2021.3
JDK:1.8
构建工具:maven 3.6.1
MySQL版本:MySQL 8.0.3
MyBatis版本:MyBatis 3.5.9

2. 第一个 mybatis 程序

思路:搭建环境 -> 导入MyBatis -> 编写代码 -> 测试

2.1 搭建环境

2.1.1 创建数据库

CREATE DATABASE `mybatis`
USE `mybatis`

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(20) NOT NULL,
`name` varchar(30) DEFAULT NULL,
`pwd` varchar(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `user`(`id`,`name`,`pwd`) VALUES 
(1,'张三','123456'),
(2,'李四','123456'),
(3,'王五','123890')

2.1.2 新建一个普通的maven项目

mybatis
命名:
mybatis

重新配置一下Maven
Windows系统:File -> Setting -> Build,Execution,Deployment -> Build Tool -> Maven
macOS: IntelliJ IDEA -> Preferences...-> Build,Execution,Deployment -> Build Tool -> Maven
应该是自己的路径,不是Bundled(Maven 3)
mybatis

2.1.3 删除src文件: 目的是创建一个项目是父项目,以后直接创建各个模块子项目

2.2 导入Maven依赖(MyBatis)

 <!--导入依赖-->
    <dependencies>
        <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.28</version>
        </dependency>
        <!--mybatis-->
        <!--https://mvnreposiyory.com/artifact/org.mybatis/mybatis-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.9</version>
        </dependency>
        <!--junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

确保依赖都导入成功:
右边Maven栏:必要时点刷新
mybatis
mybatis

2.3 编写代码

2.3.1 创建一个模块 new module

mybatis
点击Next:
mybatis
填写模块名,点击Next:
mybatis
这样创建的好处是:每一个模块单独拥有自己的pom.xml文件,管理不同的依赖
mybatis

2.3.2 编写mybatis的核心配置文件

mybatis
mybatis
连接mysql:
mybatis
填写必要的信息:
mybatis
选择Schemas
mybatis
可能需要下载必要的文件:
mybatis
连接测试:之后点击Apply,在点击OK
mybatis
测试连接成功:
mybatis
连接后可以显示:
mybatis
显示配置项:
mybatis
核心配置文件:mybatis-config.cml放到 resources文件中

<?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">  <!--约束,用的是.dtd文件-->
<!--!DOCTYPE后的configuration是下面的根标签-->
<!--核心配置文件-->
<configuration><!--根标签-->
  <!--配置连接数据库的环境-->
  <!--环境 environments下可以配置多个environment环境 环境名称是id="" environments default=使用哪个环境就写哪个环境的id-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>    <!--事务管理的类型-->
            <dataSource type="POOLED"> <!--数据源:连接数据的信息--> 
                <property name="driver" value="com.mysql.jdbc.Driver"/> 
              <!--驱动 /mybatis 是数据库名--> <!--编码--> <!--&amp;就是and  serverTimezone时区-->
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=true&amp;serverTimezone=GMT"/>
             <property name="username" value="root"/><!--用户名 写自己的-->
             <property name="password" value="*****"/><!--密码 写自己的-->
            </dataSource>
        </environment>
    </environments>
</configuration>

2.3.3 编写mybatis工具类

创建一些类
mybatis
创建Class文件
mybatis
填写Class类名
mybatis
编写工具类:MybatisUtils

  • sqlSession:代表Java程序和数据库之间的会话。(HttpSession是Java程序和浏览器之间的会话)
  • SqlSessionFactory:“生产”SqlSession的“工厂”
  • 工厂模式:如果创建某一个对象,使用的过程基本固定,那么我们就可以把创建者对象的相关代码封装到一个“工厂类”中,以后都使用这个工厂类来“生产”我们需要的对象。
package com.guo.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.InputStream;

//sqlSessionFactory --> sqlSession   加载资源  创建对象
public class MybatisUtils {

    private static SqlSessionFactory sqlSessionFactory;
    static {
        try {
            //使用mybatis第一步获取sqlSessionFactory对象
            String resource = "mybatis-config.xml";  //初始就加载 指定配置文件加载
            //读取配置文件 字节输出流
            InputStream inputStream = Resources.getResourceAsStream(resource); 
        
        //获取 sqlSessionFactoryBuilder
        // SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        
        //获取 sqlSessionFactory 
        // sqlSessionFactoryBuilder可以传入字节输入流或者字符输入流
//相当于SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 
        // sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
             
            //以上注释相当于下面
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //创建流
         
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    //既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例了。
    //SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。

    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();

    }

}

最终结构:
mybatis

2.3.4 编写代码

1. 实体类
package com.guo.pojo;

//实体类
public class User {

    private int id;
    private String name;
    private String pwd;

    //构造函数快捷键:command + N

    public User(){

    }

    public User(int id, String name, String pwd) {
        this.id = id;
        this.name = name;
        this.pwd = pwd;
    }

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    @Override
    public String toString() {
        return "User{"+
                "id=" + id +
                ", name='" + name + '\'' +
                ", pwd='" + pwd + '\'' +
                '}';
    }
    
}
2. Mapper接口,相当于DAO
package com.guo.dao;

import com.guo.pojo.User;

import java.util.List;

public interface UserMapper {
   //获取全部用户
   List<User> getUserList();

   
}
3. 接口实现类
  • 相关概念:ORM (Object Relations Mapping)对象关系映射。
  • 对象:Java的实体类对象
  • 关系:关系型数据库
  • 映射:二者之间的对应关系
Java概念数据库概念
属性字段/列
对象记录/行
  1. 映射文件的命名
    • 表所对应的实体类的类名+Mapper.xml
    • 例如:表user,映射的实体类为User,所对应的映射文件为UserMapper.xml
    • 因此,一个映射文件对应一个实体类 ,对应一张表操作
    • Mybatis映射文件用于编写SQL,访问以及操作表中的数据。
    • Mybatis映射文件存放的位置是src/maim/mappers目录下 (mapper就是原来的dao)
  2. Mybatis中可以面向接口操作数据,要保证两个一致:
    • a. mapper接口的全类名和映射文件的命名空间(namespace)保持一致
    • b. mapper接口中方法的方法名和映射文件中编写SQL的标签的id属性保持一致
  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"><!--约束,用的是.dtd文件-->
<!--!DOCTYPE后的mapper是下面的根标签-->
<!--引入映射文件-->
<!--namespace绑定一个对应的Dao/Mapper接口-->
<mapper namespace="com.guo.dao.UserMapper"> <!--绑定mapper接口-->
    <!--权限定名就是把类及类所在的包都写上-->
<!--select查询语句-->  
<!--重写方法,id对应方法名是getUserList,resultType返回一个结果(LIST结果集),resultMap是多个结果集合-->
    <select id="getUserList" resultType="com.guo.pojo.User" > 
    <!--这必须是 com.guo.pojo.User,因为是包名需要用.而不是路径不能用/-->
        <!--执行SQL-->
        select * from mybatis.user where name like #{value};
    </select>
</mapper>
  • 由原来的UserDaoImpl.java转换为一个Mapper配置文件UserMapper.xml 满足SqlSession的调用
  • 需要在配置里是设置一下SQL Dialects 保证下面使用Sql语句时可以出现表名
    • Windows系统:File -> Setting -> Languages & Frameworks -> SQL Dialects
    • macOS: IntelliJ IDEA -> Preferences...-> Languages & Frameworks -> SQL Dialects
      mybatis

2.4 测试 (junit测试)

2.4.1 建立对应的包

package com.guo.dao;

import com.guo.pojo.User;
import com.guo.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

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

public class UserMapperTest {
    @Test
    public void test(){
        //第一步,获得SQLSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        // 执行SQL 获取mapper接口对象 
   //当我们传给getMapper()一个类型的class对象,该方法会获取一个这个类型的实例化对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        //功能 
        //每当调用mapper接口中的方法,该方法会找到接口,接口找到相对应的映射文件,根据当前调用的方法找到当前映射文件里对应的SQL语句
        List<User> userList =  userMapper.getUserList(); //代理模式
        
        for (User user : userList){
            System.out.println(user);
        }
        //关闭sqlSession
        sqlSession.close();

    }

注意点:

2.4.2 可能报错,需要注意:

报错1:

org.apache.ibatis.binding.BindingException: Type interface com.guo.dao.UserMapper is not known to the MapperRegistry.
mybatis

核心配置文件mybatis-config.cml中注册mapper.


    <!--每一个Mapper.xml都需要在Mybatis核心配置文件中注册!-->
    <mappers>
        <mapper resource="com/guo/dao/UserMapper.xml"/> <!--写自己的-->
    </mappers>
    

正式的文件目录:
mybatis

mybatis-config.cml配置文件:全部代码

<?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"> <!--约束,用的是.dtd文件-->
<!--核心配置文件-->
<configuration><!--根标签-->
  <!--配置连接数据库的环境-->
  <!--
  环境 environments下可以配置多个environment环境,环境名称是id="" environments 
   default=使用哪个环境就写哪个环境的id
-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>    <!--事务管理的类型-->
            <dataSource type="POOLED"> <!--数据源:连接数据的信息-->  
                <property name="driver" value="com.mysql.jdbc.Driver"/> 
              <!--驱动 /mybatis 是数据库名--> <!--编码--> <!--&amp;就是and  serverTimezone时区-->
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=true&amp;serverTimezone=GMT"/>
             <property name="username" value="root"/><!--用户名 写自己的-->
             <property name="password" value="*****"/><!--密码 写自己的-->
            </dataSource>
        </environment>
    </environments>
  
    <!--引入映射文件-->
    <!--每一个Mapper.xml都需要在Mybatis核心配置文件中注册!-->
    <mappers>
        <mapper resource="mappers/UserMapper.xml"/> <!--写自己的-->
    </mappers>
    
</configuration>
报错2:初始化异常

The error may exist in com/guo/dao/UserMapper.xml 问题是target文件中不存在
mybatis
注意在主项目和子模块的pom.xml加代码,并重新刷新一遍maven

<!--maven由于它的约定大于配置,我们以后可能遇到我们自己写的配置文件,无法被导出或者生效的问题,解决方法:-->
    <!--在build中配置resources,来防止我们资源导出失败的问题!-->
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource> <!--重点在这里!-->
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>

当然你可以通过创建相同的包解决此类问题!

例如:接口SelectMapper.java对应实现类SelectMapper.xml,存放的包名相同:

接口SelectMapper.java存放在./src/main/java/com/**/mappers

对应实现类SelectMapper.xml存放在./src/main/resources/com/**/mappers

mybatis

如果不是第一次运行,需要clean一下 target文件
mybatis

2.5 运行

2.5.1 结果显示

mybatis

2.5.2 文件结构

mybatis
稍好一点:
mybatis

2.5.3 可能遇到的问题:

1.配置文件没有注册
2.绑定接口错误
3.方法名不对
4.返回类型不对
5.Maven导出资源问题

三、 特别注意(核心接口)

SqlSessionFactoryBuilder
这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了 。因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。

SqlSessionFactory
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式

SqlSession
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。

四、总结流程

按顺序写:
mybatis

五、 加入log4j 日志功能

5.1 加入依赖 pom.xml

导入后注意更新!

       <!--log4j日志-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

5.2 编写log4j.xml配置文件,在/src/main/resources/

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"><!--爆红没事-->
    <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
        <param name="Encoding" value="UTF-8" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n" />
        </layout>
    </appender>
    <logger name="java.sql"> <!--logger 输出日志的范围-->
        <level value="debug" /> <!--level 级别-->
    </logger>
    <logger name="org.apache.ibatis">
        <level value="info" />
    </logger>
    <root>
        <level value="debug" />
        <appender-ref ref="STDOUT" />
    </root>
</log4j:configuration>

5.3 日志的级别

  • 从上到下级别越来越低,但级别越高输出信息越少
  • 从上到下打印出来的内容越来越详细
  1. FATA(致命)
  2. ERROR(错误)
  3. WARN(警告)
  4. INFO(信息)
  5. DEBUG(测试)

mybatis

六、 MyBatis核心配置文件

6.1 environment

mybatis-config.xml核心配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--核心配置文件-->
<configuration>
    <!--
    环境 environments下可以配置多个environment环境 环境名称是id="" environments
    属性:
    default: 设置默认使用的环境的id
    -->
    <environments default="development">
        <!--
        environment:配置某个具体的环境
        属性:
           id:表示连接数据库环境的唯一标识,不能重复
        -->
        <environment id="development">
                <!--
                transactionManager: 设置事务管理方式
                属性:
                type:"JDBC/MANAGED"
                JDBC:当前环境中,执行SQL时,使用的是JDBC中原生的事务管理方式,事务的提交或回滚需要手动来处理。
                MANAGED:表示被管理,隶属Spring
                -->
            <transactionManager type="JDBC"/>  <!--事务管理的类型-->
            <!--
            dataSource: 配置数据源
                属性:
                type:设置数据源的类型
                type:"POOLED/UNPOOLED/JNDI"
                POOLED:表示使用数据库的连接池,缓存数据库连接
                UNPOOLED:表示不使用数据库的连接池
                JNDI:表示使用上下文中的数据源
                Spring管理数据源,SSM整合后,不需要再设置数据源了!!!
            -->
            <dataSource type="POOLED"><!--数据源:连接数据的信息-->
                <!--设置连接数据库的驱动--> <!--编码-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <!--设置连接数据库的连接地址-->
                <!-- /mybatis 是数据库名  &amp;就是and   serverTimezone时区-->
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=true&amp;serverTimezone=GMT"/>
                <!--设置连接数据库的用户名-->
                <property name="username" value="******"/><!--自定义-->
                <!--设置连接数据库的密码-->
                <property name="password" value="******"/><!--自定义-->
            </dataSource>
        </environment>
    </environments>

</configuration>

6.2 properties

这里我们的数据源:连接数据的信息是写死的,我们还可以把数据源信息单拿出来放到db.properties文件中:

6.2.1 创建db.properties文件

mybatis

6.2.2 编写db.properties文件

配置文件有很多且是键值对应的,为了防止重名,加一个跟文件名同名的前缀,例如文件名是db.properties,因此前缀就是db.

db.properties文件:

db.driver = com.mysql.jdbc.Driver
db.url =jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT
db.username = ******
db.password = ******

mybatis-config.xml文件中添加信息

    <!--引入外部properties配置文件-->
    <properties resource="db.properties"/>

mybatis-config.xml核心配置文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--核心配置文件-->
<configuration>
    <!--引入外部properties配置文件-->
    <properties resource="db.properties"/>

    <!--环境 environments下可以配置多个environment环境  环境名称是id="" environments default=使用哪个个环境就写哪个环境的id-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>    <!--事物管理-->
            <dataSource type="POOLED">
                <property name="driver" value="${db.driver}"/> <!--驱动--><!--编码--> <!--&amp;就是and  serverTimezone时区-->
                <property name="url" value="${db.url}"/>
                <property name="username" value="${db.username}"/>
                <property name="password" value="${db.password}"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

6.3 typeAliases

在接口实现类Mapper.xml中,我们需要写resultType="com.guo.pojo.***"包名,比如UserMapper.xml,每次写一个SQL语句都要重新写一个resuktType

    <select id="getUserList" resultType="com.guo.pojo.User" > 
    <!--这必须是 com.guo.pojo.User,因为是包名需要用.而不是路径不能用/-->
        <!--执行SQL-->
        select * from mybatis.user where name like #{value};
    </select>

MyBatis提供了类型别名的功能,解决了问题:
mybatis-config.xml核心配置文件中引入标签

<!--设置类型别名-->
<typeAliases>
        <!--
        typeAlias:设置某个具体的类型的别名
        属性:
        type:需要设置别名的类型的全类名
        alias:设置此类型的别名,且别名不区分大小写。若不设置此属性,该类型拥有默认的别名,即类名
        -->
        <!--<typeAlias type="com.atguigu.mybatis.bean.User"></typeAlias>-->
        <!--<typeAlias type="com.atguigu.mybatis.bean.User" alias="user">
        </typeAlias>-->
        <!--以包为单位,设置改包下所有的类型都拥有默认的别名,即类名且不区分大小写-->
        <package name="com.guo.pojo"/>
    </typeAliases>

但是核心mybatis-config.xml核心配置文件中引入标签需要符合顺序!
mybatis
mybatis-config.xml核心配置文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--核心配置文件-->
<configuration>
    <!--引入外部properties配置文件-->
    <properties resource="db.properties"/>

    <!--以包为单位,将包下所有类型设置默认的类型别名,即类名且不区分大小写-->
    <typeAliases>
        <package name="com.guo.pojo"/>
    </typeAliases>

    <!--环境 environments下可以配置多个environment环境  环境名称是id="" environments default=使用哪个个环境就写哪个环境的id-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>    <!--事物管理-->
            <dataSource type="POOLED">
                <property name="driver" value="${db.driver}"/> <!--驱动--><!--编码--> <!--&amp;就是and  serverTimezone时区-->
                <property name="url" value="${db.url}"/>
                <property name="username" value="${db.username}"/>
                <property name="password" value="${db.password}"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

6.4 `mappers``

    <!--每一个Mapper.xml都需要在Mybatis核心配置文件中注册!-->
    <mappers>
        <mapper resource="mappers/UserMapper.xml"/>
    </mappers>

但是每次写一个接口文件,就需要引用一个mappper标签,因此可以以为单位改量一下:

6.4.1 新建一个包

映射文件放在resources里,这里不能直接创建包,只能创建Directory
mybatis
resources文件下是一个个目录,因此里写的是路径,创建Directory时不能写com.guo.mappers要写com/guo/mappers
最后生成的目录结构:
mybatis

6.4.2 修改核心配置文件mybatis-configxml

核心配置文件mybatis-config.xml :

    <!--引入映射文件-->
    <mappers>
        <!--<mapper resource="mappers/UserMapper.xml"/>-->
        <!--
           以包为单位应如映射文件
           要求:
           1. mapper接口所在的包要和映射文件所在的包一致
           2. mapper接口要和映射文件的名字一样
        -->
        <package name="com.guo.mappers"/>
    </mappers>

6.5 总结

6.5.1 核心配置文件mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//MyBatis.org//DTD Config 3.0//EN"
        "http://MyBatis.org/dtd/MyBatis-3-config.dtd">
<configuration>
    <!--引入properties文件,此时就可以${属性名}的方式访问属性值-->
    <properties resource="jdbc.properties"/>
  
    <typeAliases>
        <!--
        typeAlias:设置某个具体的类型的别名
        属性:
        type:需要设置别名的类型的全类名
        alias:设置此类型的别名,且别名不区分大小写。若不设置此属性,该类型拥有默认的别名,即类名
        -->
        <!--<typeAlias type="com.guo.pojo.User"></typeAlias>-->
        <!--<typeAlias type="com.guo.pojo.User" alias="user">
        </typeAlias>-->
        <!--以包为单位,设置改包下所有的类型都拥有默认的别名,即类名且不区分大小写-->
        <package name="com.guo.pojo"/>
    </typeAliases>
    <!--
    environments:设置多个连接数据库的环境
    属性:
	    default:设置默认使用的环境的id
    -->
    <environments default="development">
        <!--
        environment:设置具体的连接数据库的环境信息
        属性:
	        id:设置环境的唯一标识,可通过environments标签中的default设置某一个环境的id,表示默认使用的环境
        -->
        <environment id="development">
            <!--
            transactionManager:设置事务管理方式
            属性:
	            type:设置事务管理方式,type="JDBC|MANAGED"
	            type="JDBC":设置当前环境的事务管理都必须手动处理
	            type="MANAGED":设置事务被管理,例如spring中的AOP
            -->
            <transactionManager type="JDBC"/>
            <!--
            dataSource:设置数据源
            属性:
	            type:设置数据源的类型,type="POOLED|UNPOOLED|JNDI"
	            type="POOLED":使用数据库连接池,即会将创建的连接进行缓存,下次使用可以从缓存中直接获取,不需要重新创建
	            type="UNPOOLED":不使用数据库连接池,即每次使用连接都需要重新创建
	            type="JNDI":调用上下文中的数据源
            -->
            <dataSource type="POOLED">
                <!--设置驱动类的全类名-->
                <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>
    <!--引入映射文件-->
    <mappers>
        <!-- <mapper resource="UserMapper.xml"/> -->
        <!--
        以包为单位,将包下所有的映射文件引入核心配置文件
        注意:
			1. 此方式必须保证mapper接口和mapper映射文件必须在相同的包下
			2. mapper接口要和mapper映射文件的名字一致
        -->
        <package name="com.guo.mappers"/>
    </mappers>
</configuration>

6.5.2 文件结构

mybatis

七、MyBatis 增删改查 CRUD

7. 新建项目(New Module)

7.1 新建一个模块

选中项目名右键点击New --> Module...
mybatis
点击 Next
mybatis
设置相应参数,设置完点击Finish
mybatis

7.2 增加并配置核心配置文件

选中resources文件,右键点击New --> File
mybatis

可自定义命名,但规范命名为mybatis-config.xml
mybatis

7.3 新建jdbc.properties文件

选中resources文件,右键点击New --> Resource Bundle
mybatis
写个名字,我这里写的是jdbc,之后里面的配置加jdbc.前缀
mybatis

jdbc.driver = com.mysql.jdbc.Driver
jdbc.url =jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT
jdbc.username = 
jdbc.password = 

7.4 创建类

选中src/main/java文件,右键点击New --> Package
mybatis
自定义命名包名:com.**.mapperscom.**.pojo 等等
mybatis

7.5 创建resources中的mapper

选中resources文件,右键点击New --> Directory
mybatis

写路径名要和之前写过的com.**.mappers对应,只不过这里写的是路径 com/***/mappers
mybatis

7.6 将核心配置文件作为模版mybatis-config.xml

点击IntelliJ IDEA --> Preferences…
mybatis

或者 点击右上角齿轮 -->Preferences…
mybatis
选择File and Code Templates设置
mybatis

7.7 创建映射文件并将其作为模版mybatis-mapper.xml

选中resources文件,右键点击New --> File
mybatis
设定名字,要与接口文件名字相同,例:ParameterMapper.xml
mybatis
选择File and Code Templates设置
mybatis

7.8 创建测试类

选中test/java文件,右键点击New --> Java Class
mybatis
设定名字,最好要与接口文件名字相同,例:ParameterMapperTest.java
mybatis
文件格式
mybatis

7.9 创建工具类Utils.java

mybatis

import java.io.InputStream;

public class SqlSessionUtils {

    public static SqlSession getSqlSession() {

        SqlSession sqlSession = null;

        String resource = "mybatis-config.xml";  //初始就加载
        try {
            InputStream inputStream = Resources.getResourceAsStream(resource);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            sqlSession =  sqlSessionFactory.openSession(true); //自动提交事物

        } catch (IOException e) {
            e.printStackTrace();
        }
        return sqlSession;
    }
}

八、 MyBatis获取参数值的两种方式(重点)

  • MyBatis获取参数值的两种方式:${}#{}
  • ${}的本质就是字符串拼接,#{}的本质就是占位符赋值(优先使用)
  • ${}使用字符串拼接的方式拼接SQL,若为字符串类型或日期类型的字段进行赋值时,需要手动加单引号;但是#{}使用占位符赋值的方式拼接SQL,此时为字符串类型或日期类型的字段进行赋值时,可以自动添加单引号。

8.1 单个字面量类型的参数

  • 若mapper接口中的方法参数为单个的字面量类型,此时可以使用${}#{}以任意的名称(最好见名识意)获取参数的值,注意${}需要手动加单引号
//ParameterMapper.java  接口class
    User getUserByUserName(String username);
<!--User getUserByUsername(String username);-->
<select id="getUserByUsername" resultType="User"> 
	 select * from mybatis.user where user.name = #{username} <!--这里参数名不重要,建议username-->
</select>
<!--User getUserByUsername(String username);-->
<select id="getUserByUsername" resultType="User">  
	select * from mybatis.user where user.name = '${username}'<!--这里参数名不重要,$因为是字符串拼接需要单引号-->  
</select>
// 测试类
    @Test
    public void testgetUserByUserName(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        ParameterMapper mapper = sqlSession.getMapper(ParameterMapper.class);
        User user = mapper.getUserByUserName("王五"); //需要的值
        System.out.println(user);
    }

8.2 多个字面量类型的参数

  • 若mapper接口中的方法参数为多个时,此时MyBatis会自动将这些参数放在一个map集合中
    • 以arg0,arg1…为键,以参数为值;
    • 以param1,param2…为键,以参数为值;
  • 因此只需要通过${}#{}访问map集合的键就可以获取相对应的值,注意${}需要手动加单引号。
  • 使用arg或者param都行,要注意的是,arg是从arg0开始的,param是从param1开始的
//ParameterMapper.java  接口class
//验证和登录功能
    User checkLogin(String username,String password);
<!--User checkLogin(String username,String password);-->
<select id="checkLogin" resultType="User">  
	select * from mybatis.user where user.name = #{arg0} and user.pwd = #{arg1}; 
</select>
<!--User checkLogin(String username,String password);-->
<select id="checkLogin" resultType="User">
	select * from mybatis.user where user.name = '${arg0}' and user.pwd = '${arg1}';
</select>
<!--参数只需要对应位置的值就好,不必纠结arg或者param,数字代表位置-->
<!--User checkLogin(String username,String password);-->
<select id="checkLogin" resultType="User">
	select * from mybatis.user where user.name = '${arg0}' and user.pwd = '${param1}';
</select>
// 测试类
  @Test
    public void testCheckLogin(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        ParameterMapper mapper = sqlSession.getMapper(ParameterMapper.class);
        User user = mapper.checkLogin("张三","123456");
        System.out.println(user);
    }

8.2.1 注意点:

mybatis

8.3 map集合类型的参数

  • 若mapper接口中的方法需要的参数为多个时,此时可以手动创建map集合,将这些数据放在map中只需要通过${}#{}访问map集合的键就可以获取相对应的值,注意${}需要手动加单引号
//ParameterMapper.java  接口class
//验证和登录功能
//键 String类型,值Object类型
    User checkLoginByMap(Map<String,Object> map);  
<!--User checkLoginByMap(Map<String,Object> map);-->
<select id="checkLoginByMap" resultType="User">
	select * from mybatis.user where user.name = #{username} and user.pwd = #{password};
</select>
// 测试类
    @Test
    public void testCheckLoginByMap(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        ParameterMapper mapper = sqlSession.getMapper(ParameterMapper.class);
        Map<String, Object> map = new HashMap<String,Object>();
      //这里键username要和ParameterMapper.xml中的 #{username}的名字对应上
        map.put("username","张三"); 
      //这里值password要和ParameterMapper.xml中的#{password}的名字对应上
        map.put("password","123456");
        User user = mapper.checkLoginByMap(map);
        System.out.println(user);
    }

8.4 实体类类型的参数

  • 若mapper接口中的方法参数为实体类对象时此时可以使用${}#{},通过访问实体类对象中的属性名获取属性值,注意${}需要手动加单引号。(使用最多的情况,浏览器传输过来的是实体类对象,通过${}#{}以属性的方式访问属性值)
//ParameterMapper.java  接口class
//添加用户信息功能
    int insertUser(User user);
<!--int insertUser(User user);-->
<insert id="insertUser">
      insert into mybatis.user values (#{id},#{name},#{pwd});
	<!--insert into t_user values(null,#{name},#{pwd});如果id设定为自增加,那就是null,name和pwd对应实体类中的属性名-->
</insert>
//测试类
    @Test
    public void testinsertUser(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        ParameterMapper mapper = sqlSession.getMapper(ParameterMapper.class);
        int result = mapper.insertUser(new User(7,"测试2","密码"));
        //如果id为自增列,则是null
        //int result = mapper.insertUser(new User(null,"测试2","密码"));
        System.out.println(result);
    }

8.4.1 注意点:

mybatis

8.5 使用@Param标识参数

  • 可以通过@Param注解标识mapper接口中的方法参数,此时,会将这些参数放在map集合中
    • @Param注解的value属性值为键,以参数为值;
    • 以param1,param2…为键,以参数为值;
  • 只需要通过${}#{}访问map集合的键就可以获取相对应的值,注意${}需要手动加单引号
//ParameterMapper.java  接口class
//验证和登录功能
//键 String类型,值Object类型
//@Param("username") 相当于 @Param(value = "username")
      User checkLoginByParam(@Param("username") String username, @Param("password") String password);
      
<!--User CheckLoginByParam(@Param("username") String username, @Param("password") String password);-->
    <select id="CheckLoginByParam" resultType="User">
         select * from mybatis.user where user.name = #{username} and user.pwd = #{password};
         <!--也可以使用下面这个-->
         <!-- select * from mybatis.user where user.name = #{param1} and user.pwd = #{param2};-->
    </select>
//测试类
    @Test
    public void testCheckLoginByParam(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        ParameterMapper mapper = sqlSession.getMapper(ParameterMapper.class);
        User user = mapper.checkLoginByParam("张三","123456");
        System.out.println(user);
    }

8.5.1 注意点:

mybatis

8.5.2 @param源码解析

打上断电:
mybatis
运行:
mybatis
运行debug时:

  • 红框从左到右依次是:
  • 下一步
  • 进入到某个方法中
  • 强制进入
  • 从某个方法中返回
  • 返回上一个断点
  • 跳转光标所在的行
    mybatis
    点击进入方法:底层用的是代理模式MapperProxy.java
    mybatis
    点击进入方法 进入到invoke方法中:
    mybatis
    再次点击进入方法
    command是某一个语句的标识,标识用命名空间.sql的id类名type指sql语句的类型
    mybatis
    方法先是一个switch语句,判断sql语句的类型:
    mybatis
    点击下一步 跳转到SELECT查询:判断返回值类型
    mybatis
    点击下一步
    mybatis
    convertArgsToSqlCommandParam 转换参数成sql语句的参数,mybatis获取参数的方法,将方法参数转换成sql语句的参数。
    mybatis
    点击进入方法
    mybatis
    再次点击进入方法
    mybatis
    点击command/alt+点击进入names,看看names是什么
    mybatis
    names存的内容:
    mybatis
    返回到ParamNameResolver.class看源码
    mybatis
    mybatis
    mybatis
    下图中的循环指:判断names中是否含parami的值,如果没有就往names中放键值对:键:parami,值:实际参数的值。这也解释了: Mybatis存储数据放在map集合中有两种情况,1. 以@Param注解的value属性值为键,以参数为值; 2. 以param1,param2…为键,以参数为值;
    mybatis
    经过循环:最后有的数据
    mybatis

8.6 总结

  • 建议综合整合成两种情况进行处理
    1. 实体类类型的参数
    2. 使用@Param标识参数

8.6.1 大致过程

mybatis

举例、增删改查

1. 查询 Select

根据以上所说,实体类工具类不需要变了!我们只需要变的是UserMapper.javaUserMapper.xmlUserMapperTest.java(测试类)

步骤

  1. 编写接口
  2. 编写对应的mapper中的sql语句
  3. 测试

注意点:
增删改查,需要提交事务!
UserMapper.xml文件可以放在resources文件夹中!
sql语句 insert 、update、delete 返回值默认情况下是int类型 :matched(匹配的)行数
如果要返回:changed(受影响的)行数只需在mysql数据连接url参数加useAffectedRows=true
例如:jdbc.url=jdbc:mysql://localhost:3306/ssm?useAffectedRows=true

  1. 编写接口
    UserMapper.java
   //根据ID查询用户
   User getUserById(int id);
  1. 编写对应的mapper中的sql语句
    UserMapper.xml

  2. namespace中的包名要和Dao/mapper 接口的包名一致!
    <mapper namespace="com.guo.dao.UserMapper"> <!--绑定-->

  3. select 选择,查询语句

    • id 就是对应的namespace中的方法名
    • resultType: sql语句执行的返回值
    • parameterType:参数类型
  <!--对应的接口-->
  <!--User getUserById(int id);-->
  <select id="getUserById" parameterType="int" resultType="com.guo.pojo.User">
        select * from mybatis.user where id = #{id}
  </select>
  1. 测试
    UserMapperTest.java(测试类)
    @Test
    public void getUserById(){
        SqlSession sqlSession = MybatisUtils.getSqlSession(); //不变

        UserMapper mapper = sqlSession.getMapper(UserMapper.class); //获得相应接口 具体对象 
        User user = mapper.getUserById(1);
        System.out.println(user);
        sqlSession.close(); // 不变
    }

mybatis

2. 增加 insert

UserMapper.java

//insert一个用户
   int addUser(User user);

UserMapper.xml

    <!--对象中的属性,可以直接取出来-->
    <!--int addUser(User user);-->
    <insert id="addUser" parameterType="com.guo.pojo.User" >
        insert into mybatis.user (id,name,pwd) values (#{id},#{name},#{pwd});
    </insert>

UserMapperTest.java(测试类)
增删改需要提交事物

    //增删改需要提交事物
    @Test
    public void addUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();//不变
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);//不变
        int res = mapper.addUser(new User(4,"赵六","121212"));
        if(res > 0){
            System.out.println("插入成功");
        }
        //提交事物
        sqlSession.commit();
        sqlSession.close(); //不变
    }

3. 修改 update

UserMapper.java

  //update修改用户
   int updateUser(User user);

UserMapper.xml

    <!--更新用户信息-->
    <!--void updateUser(User user);-->
    <update id="updateUser" parameterType="com.guo.pojo.User">
        update mybatis.user set name =#{name},pwd=#{pwd} where id = #{id};
    </update>

UserMapperTest.java(测试类)
增删改需要提交事物

    @Test
    public void updateUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();//不变

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);//不变
        mapper.updateUser(new User(4,"更改了","123123"));

        //提交事物
        sqlSession.commit();
        sqlSession.close();//不变
    }

4. 删除 delete

UserMapper.java

   //delete 删除一个用户
   int deleteUser(int id);

UserMapper.xml

    <!--删除一个用户-->
    <!--int deleteUser(int id);-->
    <delete id="deleteUser" parameterType="int">
         delete from mybatis.user where id = #{id};
    </delete>

UserMapperTest.java(测试类)
增删改需要提交事物

    @Test
    public void deleteUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();//不变
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);//不变
        mapper.deleteUser(4);

        //必须提交事物
        sqlSession.commit();
        sqlSession.close();//不变
    }

附:自动提交事物

修改MybatisUtils.javasqlSessionFactory.openSession();方法可以自动提交事物,默认不自动提交事物

package com.guo.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.InputStream;

//sqlSessionFactory --> sqlSession   加载资源  创建对象
public class MybatisUtils {

    private static SqlSessionFactory sqlSessionFactory;
    static {
        try {
            //使用mybatis第一步获取sqlSessionFactory对象
            //加载核心配置文件
            String resource = "mybatis-config.xml";  //初始就加载
            InputStream inputStream = Resources.getResourceAsStream(resource); //读取配置文件

            //获取 sqlSessionFactoryBuilder
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            //获取 sqlSessionFactory
            sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
            //相当于SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //创建流
            //以上2句相当于 sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //创建流

        }catch (IOException e){
            e.printStackTrace();
        }
    }

    //既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例了。
    //SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。

    public static SqlSession getSqlSession(){
        //获取 sqlSession
        //相当于 SqlSession sqlSession = sessionFactory.openSession();
        return sqlSessionFactory.openSession(true); //如果设置sqlSessionFactory.openSession(true);为true 可自动提交事物

    }

}

5. 分析错误

  1. 正确写法:
    <mapper namespace="com.guo.dao.UserMapper"> <!--绑定-->
    错误写法:
    <mapper namespace="com/guo/dao/UserMapper"> <!--绑定-->

  2. 标签不能匹配错:例如: insert 对应插入的方法

  3. mybatis-config.xml中:
    正确写法: resource是资源路径,绑定mapper需要使用路径,使用 /

   <!--每一个Mapper.xml都需要在Mybatis核心配置文件中注册!-->
    <mappers>
        <mapper resource="com/guo/dao/UserMapper.xml"/>
    </mappers>

错误写法:

   <!--每一个Mapper.xml都需要在Mybatis核心配置文件中注册!-->
    <mappers>
        <mapper resource="com.guo.dao.UserMapper.xml"/>
    </mappers>
  1. 程序数据库配置文件,必须符合规范!

  2. NullPointerException空指针异常 没有注册到资源!
    需要注意MybatisUtils.java中这两行的关系,是否能注册到资源!

   private static SqlSessionFactory sqlSessionFactory;
   sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //创建流
  1. 输出的target文件夹中的xml文件中存在中文乱码问题!

  2. maven资源没有导出问题!
    pom.xml中配置:

    <!--maven由于它的约定大于配置,我们以后可能遇到我们自己写的配置文件,无法被导出或者生效的问题,解决方法:-->
    <!--在build中配置resources,来防止我们资源导出失败的问题!-->
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>

程序报错分析:
mybatis

6. Map

遇到问题:当前类字段是三个,可以写成(id,name,pwd) values (#{id},#{name},#{pwd})形式。
UserMapper.xml

    <!--对象中的属性,可以直接取出来-->
    <insert id="addUser" parameterType="com.guo.pojo.User" >
        insert into mybatis.user (id,name,pwd) values (#{id},#{name},#{pwd});
    </insert>

如果实体类或者数据库表中字段过多,不太建议都写成以上这样,建议使用Map

UserMapper.java

//万能的Map 不需要传
   int addUser2(Map<String,Object> map);

UserMapper.xml

    <!--对象中的属性,map对象不需要一一对应,传递map的key-->
    <insert id="addUser2" parameterType="map">
        insert into mybatis.user(id,name,pwd) values (#{userid},#{userName },#{passWord});
    </insert>

UserMapperTest.java(测试类)

    @Test
    public void addUser2(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();//不变
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);//不变
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("userid",5);//随意制造参数
        map.put("userName","孙七");//
        map.put("passWord","12345");
        mapper.addUser2(map);
        //必须提交事物
        sqlSession.commit();
        sqlSession.close();//不变
    }

6.1 修改查询 (根据id查询用户)

UserMapper.java

//根据ID查询用户
//User getUserById(int id); 原来的
User getUserById2(Map<String,Object> map);//改成

UserMapper.xml

    <select id="getUserById2" parameterType="map" resultType="com.guo.pojo.User">
        select * from mybatis.user where id = #{helloid} and name = #{name};
    </select>

UserMapperTest.java(测试类)

      @Test
    public void getUserById2(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();//不变
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);//不变
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("helloId",1);//需要与`UserMapper.xml`对应上
        map.put("name","张三");
        User user = mapper.getUserById2(map);
        System.out.println(user);
        sqlSession.close();//不变
    }

总结:

  • Map传递参数,直接在sql中取出key即可!parameterType="map"
  • 对象传递参数,直接在sql中取对象的属性即可!parameterType="map"
  • 只有一个基本参数的情况下,可以直接在sql中取到!
    举个例子:

UserMapper.javagetUserById方法只传了一个值

   //根据ID查询用户
   User getUserById(int id);

UserMapper.xmlparameterType="int"可以不写了,多个参数用Map,或者注解

    <select id="getUserById" resultType="com.guo.pojo.User">
        select * from mybatis.user where id = #{id}
    </select>

6.2 模糊查询

  1. Java代码执的时候,传递通配符% %
    List<User> userList = mapper.getUserLike("%李%");
  2. 在sql拼接中使用通配符!(容易sql注入)
    select * from mybatis.user where name like "%" #{value} "%"

UserMapper.java

//模糊查询
   List<User> getUserLike(String value);

UserMapper.xmlparameterType="int"可以不写了,多个参数用Map,或者注解

    <select id="getUserLike" resultType="com.guo.pojo.User">
        select * from mybatis.user where name like #{value}
    </select>

不要写成:

    <select id="getUserLike" resultType="com.guo.pojo.User">
        select * from mybatis.user where name like "%"#{value}"%"
    </select>

能运行,但是容易造成sql语句拼接,产生sql注入

      select * from mybatis.user where id = ?
      select * from mybatis.user where id = 1 or 1 = 1

UserMapperTest.java(测试类)

    @Test
    public void getUserLike(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();//不变
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);//不变
        List<User> userList = mapper.getUserLike("%李%");
        for (User user : userList) {
            System.out.println(user);
        }
        sqlSession.close();//不变
    }

7. 配置解析

MyBatis配置文档:
mybatis

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

7.1 核心配置文件

mybatis-config.xml

  • configuration(配置)
    • properties(属性)
    • settings(设置)
    • typeAliases(类型别名)
    • typeHandlers(类型处理器)
    • objectFactory(对象工厂)
    • plugins(插件)
    • environments(环境配置)
      • environment(环境变量)
      • transactionManager(事务管理器)
      • dataSource(数据源)
    • databaseIdProvider(数据库厂商标识)
    • mappers(映射器)

7.2 环境配置(environments)

学会使用配置多套运行环境,不过要记住:尽管可以配置多个环境,但每个SqlSessionFactory实例只能选择一种环境

<environments default="test"><!--这里的default对应environment的id-->
  <environment id="development">
    <transactionManager type="JDBC"> <!--Mybatis默认的事务管理器就是JDBC-->
      <property name="..." value="..."/>
    </transactionManager>
    <dataSource type="POOLED"> <!--数据源是连接数据库:连接池:POOLED(用完可以回收)-->
      <property name="driver" value="${driver}"/>
      <property name="url" value="${url}"/>
      <property name="username" value="${username}"/>
      <property name="password" value="${password}"/>
    </dataSource>
  </environment>
 
   <environment id="test"> <!--使用的是test环境-->
    <transactionManager type="JDBC">
      <property name="..." value="..."/>
    </transactionManager>
    <dataSource type="POOLED">
      <property name="driver" value="${driver}"/>
      <property name="url" value="${url}"/>
      <property name="username" value="${username}"/>
      <property name="password" value="${password}"/>
    </dataSource>
  </environment>
</environments>

7.3 属性properties

我们可以通过properties属性来实现引用配置文件
这些属性都是课外不配置且可以动态替换的,既可以在典型的java属性文件中配置,亦可以通过properties元素的子元素来传递。【db.properties】

<!--官方文档-->
<properties resource="org/mybatis/example/config.properties">
  <property name="username" value="dev_user"/>
  <property name="password" value="F2Fa3!33TYyg"/>
</properties>

也可以编写一个配置文件db.properties

<!-- db.properties -->
driver = com.mysql.jdbc.Driver
url =jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT
username = 
password = 

核心配置文件中(mybatis-config.xml)引入,在xml中,所有的标签都可以规定其顺序
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>
    <!--引入外部配置文件--> <!--如果两个文件有同一个字段,优先使用外部配置文件-->
    <properties resource="db.properties">

    </properties>
    <!--环境 environments下可以配置多个environment环境  环境名称是id="" environments default=使用哪个个环境就写哪个环境的id-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>    <!--事物管理-->
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/> <!--驱动--><!--编码--> <!--&amp;就是and  serverTimezone时区-->
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

    <!--每一个Mapper.xml都需要在Mybatis核心配置文件中注册!-->
    <mappers>
        <mapper resource="com/guo/dao/UserMapper.xml"/>
    </mappers>
</configuration>

当然也可以这样配置:
db.properties

driver = com.mysql.jdbc.Driver
url =jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT
<?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>
    <!--引入外部配置文件--> <!--如果两个文件有同一个字段,优先使用外部配置文件-->
    <properties resource="db.properties">
        <property name="username" value=""/>
        <property name="pwd" value=""/>
    </properties>
 
    <!--环境 environments下可以配置多个environment环境  环境名称是id="" environments default=使用哪个个环境就写哪个环境的id-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>    <!--事物管理-->
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/> <!--驱动--><!--编码--> <!--&amp;就是and  serverTimezone时区-->
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${pwd}"/>
            </dataSource>
        </environment>
    </environments>

    <!--每一个Mapper.xml都需要在Mybatis核心配置文件中注册!-->
    <mappers>
        <mapper resource="com/guo/dao/UserMapper.xml"/>
    </mappers>

</configuration>

7.4 类型别名(typeAliases)

类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,存在的意义仅在于用来减少类完全限定名的冗余。

第一种,在实体类比较少的时候使用的方式,这种方式可以自定义:

    <!--可以给实体类起别名-->
    <typeAliases>
        <typeAlias alias="User" type="com.guo.pojo.User"/>
    </typeAliases>

第二种,如果实体类比较多,建议也可以这样写:
扫描实体类的包,它的默认别名就为这类的类名首字母

    <!--可以给实体类起别名-->
    <typeAliases>
         <package name="com.guo.pojo"/>
    </typeAliases>
    <select id="getUserList" resultType="user" > <!--这里user最好,也可以用User-->
        <!--执行SQL-->
        select * from mybatis.user;
    </select>

使用第二种方式,还可以在实体类上增加注解直接起名字
User.java实体类

import org.apache.ibatis.type.Alias;
@Alias("user")
public class User {
    ...
}

7.5 设置(setting)

mybatis官方文档(设置):

原因;原来很多公司用Oracle数据库,在其数据库中,字段lastName 常常会转化为
mybatis
LASTNAME,因此程序员默认开始使用_来分隔字段。现在加下面的参数last_Name转化为lastName
mybatis
mybatis

配置

<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>

7.6 插件及其他

mybatis插件:
插件举例:

  • MyBatis Generator Core
  • MyBatis Plus
  • 通用mapper
    mybatis
7.6.1 映射器(mappers)

MapperRegistry:注册绑定我们的Mapper文件
mybatis

方式一:【推荐使用】
mybatis-config.xml

    <!--每一个Mapper.xml都需要在Mybatis核心配置文件中注册!-->
    <mappers>
        <mapper resource="com/guo/dao/UserMapper.xml"/>
    </mappers>

方式二:使用class文件绑定注册
mybatis-config.xml

    <!--每一个Mapper.xml都需要在Mybatis核心配置文件中注册!-->
    <mappers>
        <mapper class="com.guo.dao.UserMapper"/>
    </mappers>

注意点:

  • 接口和它的Mapper配置文件必须同名!
  • 接口和它的的配置文件必须在同一个包下!

方式三:使用扫描包进行注入绑定
mybatis-config.xml

    <!--每一个Mapper.xml都需要在Mybatis核心配置文件中注册!-->
    <mappers>
       <package name="com.guo.dao"/>
    </mappers>

注意点:

  • 接口和它的Mapper配置文件必须同名!
  • 接口和它的的配置文件必须在同一个包下!

如果配置不对会报错:
mybatis

8. 生命周期和作用域

mybatis
mybatis

生命周期作用域是至关重要的,因为错误的使用会导致非常严重的并发问题

SqlSessionFactoryBuilder:

  • 一旦创建了 SqlSessionFactory,就不再需要它了
  • 作用域 :局部变量

SqlSessionFactory:

  • 可以理解为数据库连接池
  • SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
  • 多次重建 SqlSessionFactory 被视为一种代码“坏习惯”
  • 因此 SqlSessionFactory 的最佳作用域是应用作用域。全局变量
  • 最简单的就是使用单例模式或者静态单例模式。

SqlSession:

  • 连接到数据库连接池的一个请求。
  • SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
  • 用完之后需要关闭,否则资源被占用!

mybatis

九、MyBatis的各种查询功能

  1. 如果查询出的数据只有一条,可以通过
    • 实体类对象接收
    • List集合接收
    • Map集合接收,结果{password=123456, sex=男, id=1, age=23, username=admin}
  2. 如果查询出的数据有多条,一定不能用实体类对象接收,会抛异常TooManyResultsException,- - 可以通过
    • 实体类类型的LIst集合接收
    • Map类型的LIst集合接收
    • 在mapper接口的方法上添加@MapKey注解。此时就可以将每条数据转换的map集合作为值,以某个字段的值作为键,放在同一个map中

9.1 类型别名

Mybatis中设置了默认的类型别名,可以通用:
最常用:

  • java.lang.Integer -> intinteger
  • int -> _int_integer
  • Map -> map
  • String -> string

mybatis

9.2 查询一个实体类对象

9.2.1 接口


   /**
     * 根据id查询用户信息
     */

    User getUserById(@Param("id") Integer id);

9.2.2 实现类

    <!--User getUserById(@Param("id") Integer id);-->
<select id="getUserById" resultType="user">
    select  * from mybatis.user where id = #{id};
</select>

9.2.3 测试类

    @Test
    public void testgetUserById(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        SelectMapper mapper = sqlSession.getMapper(SelectMapper.class);
        System.out.println(mapper.getUserById(1));
    }

9.3 查询一个List集合

9.3.1 接口


   /**
     * 查询所有的用户信息
     */

    List<User> getAllUser();

9.3.2 实现类

    <!--List<User> getAllUser();-->
    <select id="getAllUser" resultType="user">
        select * from mybatis.user;
    </select>

9.3.3 测试类

    @Test
    public void testgetAllUser(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        SelectMapper mapper = sqlSession.getMapper(SelectMapper.class);
        System.out.println(mapper.getAllUser());
    }

9.4 查询单个数据

9.4.1 接口

    /**
     * 查询用户信息的总记录数
     */

    Integer getCount();

9.4.2 实现类

    <!-- Integer getCount();-->
    <select id="getCount" resultType="java.lang.Integer"> 
    <!--resultType="java.lang.Integer"、Integer、int、Int 类型别名-->
        select count(*) from mybatis.user; <!--null有时候,不算在count()字段时的结果中。-->
    </select>

9.4.3 测试类

    @Test
    public void testgetCount(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        SelectMapper mapper = sqlSession.getMapper(SelectMapper.class);
        System.out.println(mapper.getCount());
    }

9.5 查询一条数据为map集合

9.5.1 接口

 /**
     * 根据id查询用户信息为一个map集合
     */

    Map<String, Object> getUserByIdToMap(@Param("id") Integer id);

9.5.2 实现类

    <!--Map<String, Object> getUserByIdToMap(@Param("id") Integer id);-->
    <select id="getUserByIdToMap" resultType="map">
        select * from mybatis.user where user.id = #{id};
    </select>

9.5.3 测试类

    @Test
    public void testGetUserByIdToMap(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        SelectMapper mapper = sqlSession.getMapper(SelectMapper.class);
        System.out.println(mapper.getUserByIdToMap(1)); //map是无序的
    }

9.6 查询多条数据为map集合

9.6.1 方法一

9.6.1.2 接口
/**  
 * 查询所有用户信息为map集合  
 * @return  
 * 将表中的数据以map集合的方式查询,一条数据对应一个map;若有多条数据,就会产生多个map集合,此时可以将这些map放在一个list集合中获取  
 */  
		List<Map<String, Object>> getAllUserToMap();
9.6.1.3 实现类
<!--Map<String, Object> getAllUserToMap();-->  
<select id="getAllUserToMap" resultType="map">  
	select * from t_user  
</select>
<!--
[{name=张三, id=1, pwd=123456}, {name=李四, id=2, pwd=123456}, {name=王五, id=3, pwd=123890}, {name=赵4, id=4, pwd=123123}, {name=孙七, id=5, pwd=12345}, {name=测试, id=6, pwd=123}, {name=测试2, id=7, pwd=密码}]
-->

9.6.2 方法二

9.6.2.1 接口
/**
 * 查询所有用户信息为map集合
 * @return
 * 将表中的数据以map集合的方式查询,一条数据对应一个map;若有多条数据,就会产生多个map集合,并且最终要以一个map的方式返回数据,此时需要通过@MapKey注解设置map集合的键,值是每条数据所对应的map集合
 */
		@MapKey("id")
		Map<String, Object> getAllUserToMap();
9.6.2.2 实现类
<!--Map<String, Object> getAllUserToMap();-->
    <select id="getAllUserToMap" resultType="map">
        select  * from mybatis.user;
    </select>
<!--
{1={name=张三, id=1, pwd=123456}, 2={name=李四, id=2, pwd=123456}, 3={name=王五, id=3, pwd=123890}, 4={name=赵4, id=4, pwd=123123}, 5={name=孙七, id=5, pwd=12345}, 6={name=测试, id=6, pwd=123}, 7={name=测试2, id=7, pwd=密码}}
-->

9.6.3 测试类

    @Test
    public void testGetAllUserToMap(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        SelectMapper mapper = sqlSession.getMapper(SelectMapper.class);
        System.out.println(mapper.getAllUserToMap()); //map是无序的
    }

十、 特殊SQL的执行

10.1 模糊查询

10.1.1 接口

    /**
     * 根据用户名模糊查询用户信息
     */

    List<User> getUserByLike(@Param("username") String username);

10.1.2 实现类

10.1.2.1 第一种
    <!--List<User> getUserByLike(@Param("username") String username);-->
    <select id="getUserByLike" resultType="User">
        select * from mybatis.user where user.name like '%${username}%';
    </select>
    <!--
    [User{id=2, name='李四', pwd='123456'}]
    -->
10.1.2.2 第二种
    <!--List<User> getUserByLike(@Param("username") String username);-->
    <select id="getUserByLike" resultType="User">
        select * from mybatis.user where user.name like concat('%',#{username},'%');
    </select>
      <!--
    [User{id=2, name='李四', pwd='123456'}]
    -->
10.1.2.3 第三种
    <!--List<User> getUserByLike(@Param("username") String username);-->
    <select id="getUserByLike" resultType="User">
        select * from mybatis.user where user.name like "%"#{username}"%";
    </select>

10.1.3 测试类

    @Test
    public void testGetUserByLike(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        SQLMapper mapper = sqlSession.getMapper(SQLMapper.class);
        List<User> list = mapper.getUserByLike("李");
        System.out.println(list);
    }

10.2 批量删除

  • 只能使用${},如果使用#{},则解析后的sql语句为delete from t_user where id in ('1,2,3'),这样是将1,2,3看做是一个整体,只有id1,2,3的数据会被删除。正确的语句应该是delete from t_user where id in (1,2,3),或者delete from t_user where id in ('1','2','3')

10.2.1 接口

   /**
     * 批量删除
     */

    int deleteMore(@Param("ids") String ids);

10.2.2 实现类

    <!--int deleteMore(@Param("ids") String ids);-->
    <delete id="deleteMore" >
        delete  from mybatis.user where id in (${ids});
    </delete>

10.2.3 测试类

    @Test
    public void testDeleteMore(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        SQLMapper mapper = sqlSession.getMapper(SQLMapper.class);
        int result = mapper.deleteMore("6,7");
        System.out.println(result);
    }

10.3 动态设置表名

  • 多张表共同存储数据
  • 只能使用${},因为表名不能加单引号

10.3.1 接口

   /**
     * 查询指定表中的数据
     */
    List<User> getUserByTableName(@Param("tableName") String tableName);

10.3.2 实现类

    <!--List<User> getUserByTableName(@Param("tableName") String tableName);-->
    <select id="getUserByTableName" resultType="user">
        select * from ${tableName}
    </select>

10.3.3 测试类

        @Test
    public void testGetUserByTableName(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        SQLMapper mapper = sqlSession.getMapper(SQLMapper.class);
        List<User>  list = mapper.getUserByTableName("mybatis.user");
        System.out.println(list);
    }

10.4 添加功能获取自增主键

  • 使用场景
    • t_clazz(clazz_id,clazz_name)
    • t_student(student_id,student_name,clazz_id)
      1. 添加班级信息
      2. 获取新添加的班级的id
      3. 为班级分配学生,即将某学的班级id修改为新添加的班级的id
  • 在mapper.xml中设置两个属性
    • useGeneratedKeys:设置使用自增的主键()
    • keyProperty:因为增删改有统一的返回值是受影响的行数,因此只能将获取的自增的主键放在传输的参数user对象的某个属性中

10.4.1 接口

   /**
     * 添加用户信息
     */
    void insertUser(User user);

10.4.2 实现类

<!--void insertUser(User user);-->
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
	insert into t_user values (null,#{username},#{password})
</insert>

10.4.3 测试类

//测试类
@Test
public void insertUser() {
	SqlSession sqlSession = SqlSessionUtils.getSqlSession();
	SQLMapper mapper = sqlSession.getMapper(SQLMapper.class);
	User user = new User(null, "ton", "123");
	mapper.insertUser(user);
	System.out.println(user);
	//输出:user{id=10, username='ton', password='123'},自增主键存放到了user的id属性中
}

十一、自定义映射resultMap

11.1 建表

员工表:
mybatis
部门表:
mybatis

11.2 resultMap处理字段和属性的映射关系

  • resultMap:设置自定义映射
    • 属性:
      • id:表示自定义映射的唯一标识,不能重复
      • type:查询的数据要映射的实体类的类型
    • 子标签:
      • id:设置主键的映射关系
      • result:设置普通字段的映射关系
    • 子标签属性:
      • property:设置映射关系中实体类中的属性名
      • column:设置映射关系中表中的字段名
  • 若字段名和实体类中的属性名不一致,则可以通过resultMap设置自定义映射,即使字段名和属性名一致的属性也要映射,也就是全部属性都要列出来
<!--resultMap改写getAllEmp方法-->
    <resultMap id="empResultMap" type="Emp">
        <id property="eid" column="eid"></id>
        <result property="empName" column="emp_name"></result>
        <result property="age" column="age"></result>
        <result property="sex" column="sex"></result>
        <result property="email" column="email"></result>
    </resultMap>
    <select id="getAllEmp" resultMap="empResultMap">
        select * from mybatis.t_emp
    </select>
  • 若字段名和实体类中的属性名不一致,但是字段名符合数据库的规则(使用_),实体类中的属性名符合Java的规则(使用驼峰)。此时也可通过以下两种方式处理字段名和实体类中的属性的映射关系
  1. 可以通过为字段起别名的方式,保证和实体类中的属性名保持一致
    EmpMapper.xml实现类
    <!--List<Emp> getAllEmp();-->
    <select id="getAllEmp" resultType="Emp">
        select eid, emp_name empName, age, sex, email, did from mybatis.t_emp;
    </select>
  1. 可以在MyBatis的核心配置文件中的setting标签中,设置一个全局配置信息mapUnderscoreToCamelCase,可以在查询表中数据时,自动将_类型的字段名转换为驼峰,例如:字段名user_name,设置了<setting name="mapUnderscoreToCamelCase" value="true"/>,此时字段名就会转换为userName。核心配置文件详解
  <!--设置MyBatis的全局配置--> 
    <settings>
        <!--将下划线自动映射为驼峰,emp_name:empName-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

EmpMapper.xml实现类

    <select id="getAllEmp" resultType="Emp">
        select * from mybatis.t_emp
    </select>

测试类:

    @Test
    public void testGetAllEmp(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        List<Emp> list = mapper.getAllEmp();
        list.forEach(emp -> System.out.println(emp));
    }

11.3 多对一映射处理

11.3.1 查询员工信息以及员工所对应的部门信息

11.3.1.1 实体类
public class Emp {

        private Integer eid;
        private String empName;
        private Integer age;
        private String sex;
        private String email;
        private Dept dept; //注意!!

    public Integer getEid() {
        return eid;
    }

    public void setEid(Integer eid) {
        this.eid = eid;
    }

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    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 String getEmail() {
        return email;
    }

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

    public Dept getDept() {
        return dept;
    }

    public void setDept(Dept dept) {
        this.dept = dept;
    }

    @Override
    public String toString() {
        return "Emp{" +
                "eid=" + eid +
                ", empName='" + empName + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                ", email='" + email + '\'' +
                ", dept=" + dept +
                '}';
    }
public class Dept {
    private Integer did;
    private String deptName;

    public Dept(Integer did, String deptName) {
        this.did = did;
        this.deptName = deptName;
    }

    public Dept() {
    }

    public Integer getDid() {
        return did;
    }

    public void setDid(Integer did) {
        this.did = did;
    }

    public String getDeptName() {
        return deptName;
    }

    public void setDeptName(String deptName) {
        this.deptName = deptName;
    }

    @Override
    public String toString() {
        return "Dept{" +
                "did=" + did +
                ", deptName='" + deptName + '\'' +
                '}';
    }

}
11.3.1.2 接口
   /**
     * 查询员工所对应的部门信息
     */
    Emp getEmpAndDept(@Param("eid") Integer eid);
11.3.1.3 级联方式处理映射关系
   <!--处理多对一映射关系方式一 级联属性赋值-->
    <resultMap id="empAndDeptResultMapOne" type="Emp">
        <id property="eid" column="eid"></id>
        <result property="empName" column="emp_name"></result>
        <result property="age" column="age"></result>
        <result property="sex" column="sex"></result>
        <result property="email" column="email"></result>
        <result property="dept.did" column="did"></result>
        <result property="dept.deptName" column="dept_name"></result>
    </resultMap>

    <!--Emp getEmpAndDept(@Param("eid") Integer eid);-->
    <select id="getEmpAndDept" resultMap="empAndDeptResultMapOne">
        select * from mybatis.t_emp left join mybatis.t_dept on mybatis.t_emp.did = mybatis.t_dept.did where mybatis.t_emp.eid = #{eid};
    </select>
<!--
Emp{eid=1, empName='张三', age=30, sex='男', email='123@qq.com', dept=Dept{did=1, deptName='A'}}
-->
11.3.1.4 使用association处理映射关系
  • association:处理多对一的映射关系
  • property:需要处理多对的映射关系的属性名
  • javaType:该属性的类型
    <resultMap id="empAndDeptResultMapTwo" type="Emp">
        <id property="eid" column="eid"></id>
        <result property="empName" column="emp_name"></result>
        <result property="age" column="age"></result>
        <result property="sex" column="sex"></result>
        <result property="email" column="email"></result>
        <!--association property="dept" 多对一处理的属性 javaType="Dept" 属性的类型  -->
        <association property="dept" javaType="Dept">  <!--获得了Dept对象赋值给dept属性-->
            <!--Dept 实体类的属性和查询出来数据库表字段-->
            <id property="did" column="did"></id>
            <result property="deptName" column="dept_name"></result>
        </association>
    </resultMap>

    <!--Emp getEmpAndDept(@Param("eid") Integer eid);-->
    <select id="getEmpAndDept" resultMap="empAndDeptResultMapTwo">
        select * from mybatis.t_emp left join mybatis.t_dept on mybatis.t_emp.did = mybatis.t_dept.did where mybatis.t_emp.eid = #{eid};
    </select>
11.3.1.5 测试类
    @Test
    public void testGetEmpAndDept(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        Emp emp = mapper.getEmpAndDept(1);//自定义
        System.out.println(emp);
    }
11.3.1.6 分部查询
  1. 第一步:查询员工信息
  • select:设置分布查询的sql的唯一标识(namespace.SQLIdmapper接口的全类名.方法名
  • column:设置分步查询的条件
  1. 第二步:查询部门信息
11.3.1.6.1 第一步:查询员工信息

EmpMapper.java接口

   /**
     * 通过分步查询,员工及所对应的部门信息
     * 分步查询第一步:查询员工信息
     */
    Emp getEmpAndDeptByStepOne(@Param("eid") Integer eid);

EmpMapper.xml实现类

<!--处理多对一映射关系方式三:-->
    <resultMap id="empAndDeptByStepResultMap" type="Emp">
        <id property="eid" column="eid"></id>
        <result property="empName" column="emp_name"></result>
        <result property="age" column="age"></result>
        <result property="sex" column="sex"></result>
        <result property="email" column="email"></result>
        <!--select:命名空间+sql语句的id-->
        <!--
        select:设置分步查询的sql的唯一标识 <namespace.SQLId或mapper接口的全类名.方法名>
        column:设置分步查询的条件
        -->
        <association property="dept"
                     select="com.guo.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo"
                     column="did"></association>
    </resultMap>
    <!--Emp getEmpAndDeptByStepOne(@Param("eid") Integer eid);-->
    <select id="getEmpAndDeptByStepOne" resultMap="empAndDeptByStepResultMap">
        select * from mybatis.t_emp where mybatis.t_emp.eid = #{eid};
    </select>
11.3.1.6.2 第二步:查询部门信息

DeptMapper.java接口

    /**
     * 通过分步查询,员工及所对应的部门信息
     * 分步查询第二步:通过did查询员工所对应的部门
     */
    Dept getEmpAndDeptByStepTwo(@Param("did") Integer did);

DeptMapper.xml实现类

    <!--Dept getEmpAndDeptByStepTwo(@Param("did") Integer did);-->
    <select id="getEmpAndDeptByStepTwo" resultType="Dept">
        select * from mybatis.t_dept where  mybatis.t_dept.did = #{did};
    </select>
11.3.1.6.3 测试类
    @Test
    public void testGetEmpAndDeptByStep(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        Emp emp = mapper.getEmpAndDeptByStepOne(1);//自定义
        System.out.println(emp);
    }

mybatis

11.4 一对多映射处理

11.4.1 查询员工信息以及员工所对应的部门信息

  • collection
    • collection:用来处理一对多的映射关系
    • property: 一对多的映射关系的属性
    • ofType:表示该属性对饮的集合中存储的数据的类型
11.4.1.1 实体类
import java.util.List;

public class Dept {
    private Integer did;
    private String deptName;
    private List<Emp> emps;

    public Dept(Integer did, String deptName, List<Emp> emps) {
        this.did = did;
        this.deptName = deptName;
        this.emps = emps;
    }
    
    public Dept() {
    }

    public Integer getDid() {
        return did;
    }

    public void setDid(Integer did) {
        this.did = did;
    }

    public String getDeptName() {
        return deptName;
    }

    public void setDeptName(String deptName) {
        this.deptName = deptName;
    }

    public List<Emp> getEmps() {
        return emps;
    }

    public void setEmps(List<Emp> emps) {
        this.emps = emps;
    }

    @Override
    public String toString() {
        return "Dept{" +
                "did=" + did +
                ", deptName='" + deptName + '\'' +
                ", emps=" + emps +
                '}';
    }
}
11.4.1.2 接口

DeptMapper.java

   /**
     * 获取部门以及部门中所有的员工信息
     * 部门是主表DeptAndEmp
     */
    Dept getDeptAndEmp(@Param("did")Integer did);
11.4.1.3 实现类

DeptMapper.xml

    <resultMap id="deptAndEmpResultMap" type="Dept">
        <id property="did" column="did"></id>
        <result property="deptName" column="dept_name"></result>
        <!--
        1.目的:设置字段和属性的映射关系
        2.通过ofType获取集合里存储的数据的类型
        3.根据这类型获取相应的属性(mybatis内部操作)
        4.映射关系建立后,创建该类型的对象(此处是emps)
        5.最后把每一个对象放在集合(emps)中
        -->
        <collection property="emps" ofType="Emp">
            <id property="eid" column="eid"></id>
            <result property="empName" column="emp_name"></result>
            <result property="age" column="age"></result>
            <result property="sex" column="sex"></result>
            <result property="email" column="email"></result>
        </collection>
    </resultMap>
    <!--Dept getDeptAndEmp(@Param("did")Integer did);-->
    <select id="getDeptAndEmp" resultMap="deptAndEmpResultMap">
        select * from mybatis.t_dept left join mybatis.t_emp on mybatis.t_dept.did = mybatis.t_emp.did where t_dept.did = #{did};
    </select>
11.4.1.4 测试类
    @Test
    public void testGetDeptAndEmp(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
        Dept dept = mapper.getDeptAndEmp(1);
        System.out.println(dept);

    }

结果:

Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
DEBUG 05-17 13:32:57,654 ==>  Preparing: select * from mybatis.t_dept left join mybatis.t_emp on mybatis.t_dept.did = mybatis.t_emp.did where t_dept.did = ?; (BaseJdbcLogger.java:137) 
DEBUG 05-17 13:32:57,790 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137) 
DEBUG 05-17 13:32:57,919 <==      Total: 2 (BaseJdbcLogger.java:137) 
Dept{did=1, deptName='A', emps=[Emp{eid=1, empName='张三', age=30, sex='', email='123@qq.com', dept=null}, Emp{eid=4, empName='赵六', age=28, sex='', email='123@qq.com', dept=null}]}

Process finished with exit code 0

11.4.2 分步查询

11.4.2.1 第一步:查询部门信息
11.4.2.1.1 接口
   /**
     * 通过分步查询查询部门以及部门中所有的员工信息
     * 分步查询第一步:查询部门信息
     */
    Dept getDeptAndEmpByStepOne(@Param("did")Integer did);
11.4.2.1.2 实现类
    <resultMap id="deptAndEmpByResultMap" type="Dept">
        <id property="did" column="did"></id>
        <result property="deptName" column="dept_name"></result>
        <collection property="emps"
                    select="com.guo.mybatis.mapper.EmpMapper.getDeptAndEmpByStepTwo"
                    column="did"></collection>
    </resultMap>
    <!-- Dept getDeptAndEmpByStepOne(@Param("did")Integer did);-->
    <select id="getDeptAndEmpByStepOne" resultMap="deptAndEmpByResultMap">
        select * from mybatis.t_dept where mybatis.t_dept.did = #{did};
    </select>
11.4.2.2 第二步: 根据部门id查询部门中的所有员工
11.4.2.2.1 接口
   /**
     * 通过分步查询查询部门以及部门中所有的员工信息
     * 分步查询第二步:根据did查询员工信息
     */
    List<Emp> getDeptAndEmpByStepTwo(@Param("eid") Integer eid);
11.4.2.2.2 实现类
   <!--List<Emp> getDeptAndEmpByStepTwo(@Param("did") Integer did);-->
    <select id="getDeptAndEmpByStepTwo" resultType="Emp">
        select * from mybatis.t_emp where mybatis.t_emp.did = #{did};
    </select>
11.4.2.3 测试类
    @Test
    public void testGetDeptAndEmpByStep(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
        Dept dept = mapper.getDeptAndEmpByStepOne(1);
        System.out.println(dept);

    }

结果:

Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
DEBUG 05-17 14:01:38,649 ==>  Preparing: select * from mybatis.t_dept where mybatis.t_dept.did = ?; (BaseJdbcLogger.java:137) 
DEBUG 05-17 14:01:38,820 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137) 
DEBUG 05-17 14:01:38,991 <==      Total: 1 (BaseJdbcLogger.java:137) 
DEBUG 05-17 14:01:38,996 ==>  Preparing: select * from mybatis.t_emp where mybatis.t_emp.did = ?; (BaseJdbcLogger.java:137) 
DEBUG 05-17 14:01:38,997 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137) 
DEBUG 05-17 14:01:39,001 <==      Total: 2 (BaseJdbcLogger.java:137) 
Dept{did=1, deptName='A', emps=[Emp{eid=1, empName='张三', age=30, sex='', email='123@qq.com', dept=null}, Emp{eid=4, empName='赵六', age=28, sex='', email='123@qq.com', dept=null}]}

Process finished with exit code 0
11.4.2.4 测试类延迟加载
    @Test
    public void testGetDeptAndEmpByStep(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
        Dept dept = mapper.getDeptAndEmpByStepOne(1);
        System.out.println(dept.getDeptName());
    }

mybatis
手动设置延迟加载:fetchType="eager" 在开启延迟加载的情况下,设置立即加载

<resultMap id="deptAndEmpByResultMap" type="Dept">
        <id property="did" column="did"></id>
        <result property="deptName" column="dept_name"></result>
        <collection property="emps"
                    select="com.guo.mybatis.mapper.EmpMapper.getDeptAndEmpByStepTwo"
                    column="did"
                    fetchType="eager"></collection>
    </resultMap>
    <!-- Dept getDeptAndEmpByStepOne(@Param("did")Integer did);-->
    <select id="getDeptAndEmpByStepOne" resultMap="deptAndEmpByResultMap">
        select * from mybatis.t_dept where mybatis.t_dept.did = #{did};
    </select>

mybatis

11.5 延迟加载

  • 分步查询的优点:可以实现延迟加载,但是必须在核心配置文件中设置全局配置信息:
    • lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载,默认为false(需要设置lazyLoadingEnabled: true,aggressiveLazyLoading为false)
    • aggressiveLazyLoading:当开启时,任何方法的调用都会加载该对象的所有属性。 否则,每个属性会按需加载。默认为false
  • 此时就可以实现按需加载,获取的数据是什么,就只会执行相应的sql。此时可通过association和collection中的fetchType属性设置当前的分步查询是否使用延迟加载,fetchType=“lazy(延迟加载)|eager(立即加载)”

11.5.1 开启延迟加载

   <!--设置MyBatis的全局配置-->
    <settings>
        <!--开启延迟加载-->
        <setting name="lazyLoadingEnabled" value="true"/>
    </settings>

ResultMapTest.java测试类

   @Test
    public void testGetEmpAndDeptByStep(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        Emp emp = mapper.getEmpAndDeptByStepOne(1);
        System.out.println(emp.getEmpName());
    }

未开启延迟加载前:两条SQL语句都运行了
mybatis
开启延迟加载后:只运行获取emp的SQL语句
mybatsi
ResultMapTest.java测试类2

    @Test
    public void testGetEmpAndDeptByStep(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        Emp emp = mapper.getEmpAndDeptByStepOne(1);
        System.out.println(emp.getEmpName());
        System.out.println("---------------------------");
        System.out.println(emp.getDept());
    }

未开启延迟加载前:两条SQL语句都运行了
mybatis
开启延迟加载后:需要用到查询dept的时候才会调用相应的SQL语句
mybatis

11.5.2 开启延迟加载,配置手动设置fetchType

fetchType当开启了全局的延迟加载之后,可以通过该属性手动控制延迟加载的效果
fetchType=“lazy|eager”lazy表示延迟加载,eager表示立即加载

    <resultMap id="empAndDeptByStepResultMap" type="Emp">
        <id property="eid" column="eid"></id>
        <result property="empName" column="emp_name"></result>
        <result property="age" column="age"></result>
        <result property="sex" column="sex"></result>
        <result property="email" column="email"></result>
        <association property="dept"
select="com.guo.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo"
                     column="did"
                     fetchType="eager"></association>
    </resultMap>

mybatis

十二、动态SQL

  • Mybatis框架的动态SQL技术是一种根据特定条件动态拼装SQL语句的功能,它存在的意义是为了解决拼接SQL语句字符串时的痛点问题
//实体类
public class Emp {

        private Integer eid;
        private String empName;
        private Integer age;
        private String sex;
        private String email;
        private Dept dept;

    public Integer getEid() {
        return eid;
    }

    public void setEid(Integer eid) {
        this.eid = eid;
    }

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    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 String getEmail() {
        return email;
    }

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

    public Dept getDept() {
        return dept;
    }

    public void setDept(Dept dept) {
        this.dept = dept;
    }

    @Override
    public String toString() {
        return "Emp{" +
                "eid=" + eid +
                ", empName='" + empName + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                ", email='" + email + '\'' +
                ", dept=" + dept +
                '}';
    }

    public Emp(Integer eid, String empName, Integer age, String sex, String email, Dept dept) {
        this.eid = eid;
        this.empName = empName;
        this.age = age;
        this.sex = sex;
        this.email = email;
        this.dept = dept;
    }

    public Emp(Integer eid, String empName, Integer age, String sex, String email) {
        this.eid = eid;
        this.empName = empName;
        this.age = age;
        this.sex = sex;
        this.email = email;
    }

    public Emp() {
    }
}

12.1 if

  • if标签可通过test属性(即传递过来的数据)的表达式进行判断,若表达式的结果为true,则标签中的内容会执行;反之标签中的内容不会执行
  • where后面添加一个恒成立条件1=1
    • 这个恒成立条件并不会影响查询的结果
    • 这个1=1可以用来拼接and语句,例如:当empNamenull
      • 如果不加上恒成立条件,则SQL语句为select * from t_emp where and age = ? and sex = ? and email = ?,此时where会与and连用,SQL语句会报错
      • 如果加上一个恒成立条件,则SQL语句为select * from t_emp where 1= 1 and age = ? and sex = ? and email = ?,此时不报错

12.1.1 接口

  /**
     * 多条件查询
     */
    List<Emp> getEmpByCondition(Emp emp);

12.1.2 实现类

 <!-- List<Emp> getEmpByCondition(Emp emp);-->
    <select id="getEmpByCondition" resultType="Emp">
        select * from mybatis.t_emp where 1=1
        <if test="empName != null and empName !=''">
            and emp_name = #{empName}
        </if>
        <if test="age != null and age !=''">
            and age = #{age}
        </if>
        <if test="sex != null and sex !=''">
            and sex = #{sex}
        </if>
        <if test="email != null and email !=''">
            and email = #{email}
        </if>
    </select>

12.1.3 测试类

    @Test
    public void TestGetEmpByCondition(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class);
        List<Emp> list = mapper.getEmpByCondition(new Emp(null,"张三",30,"男","123@qq.com"));
        System.out.println(list);
    }

12.2 where

  • whereif一般结合使用:
    • where标签中的if条件都不满足,则where标签没有任何功能,即不会添加where关键字
    • where标签中的if条件满足,则where标签会自动添加where关键字,并将条件最前方多余的and/or去掉

12.2.1 实现类

    <!-- List<Emp> getEmpByCondition(Emp emp);-->
    <select id="getEmpByCondition" resultType="Emp">
        select * from mybatis.t_emp
        <where>
            <if test="empName != null and empName !=''">
                emp_name = #{empName}
            </if>
            <if test="age != null and age !=''">
                and age = #{age}
            </if>
            <if test="sex != null and sex !=''">
                and sex = #{sex}
            </if>
            <if test="email != null and email !=''">
                and email = #{email}
            </if>
        </where>
    </select>

12.2.3 注意:where标签不能去掉条件后多余的and/or

错误写法!

  <select id="getEmpByCondition" resultType="Emp">
        select * from mybatis.t_emp
        <where>
            <if test="empName != null and empName !=''">
                emp_name = #{empName} and 
            </if>
            <if test="age != null and age !=''">
                age = #{age} and 
            </if>
            <if test="sex != null and sex !=''">
                sex = #{sex} and 
            </if>
            <if test="email != null and email !=''">
                email = #{email}
            </if>
        </where>
    </select>

12.3 trim

  • trim用于去掉或添加标签中的内容
  • 常用属性
  • prefix:在trim标签中的内容的前面添加指定内容
  • suffix:在trim标签中的内容的后面添加指定内容
  • prefixOverrides:在trim标签中的内容的前面去掉指定内容
  • suffixOverrides:在trim标签中的内容的后面去掉指定内容
  • trim中的标签都不满足条件,则trim标签没有任何效果,也就是只剩下select * from t_emp
<!-- List<Emp> getEmpByCondition(Emp emp);-->
    <select id="getEmpByCondition" resultType="Emp">
        select * from mybatis.t_emp
        <trim prefix="where" suffixOverrides="and|or">
            <if test="empName != null and empName !=''">
                emp_name = #{empName} and
            </if>
            <if test="age != null and age !=''">
                age = #{age} and
            </if>
            <if test="sex != null and sex !=''">
                sex = #{sex} or
            </if>
            <if test="email != null and email !=''">
                email = #{email}
            </if>
        </trim>
    </select>

12.3.1 测试类

    @Test
    public void TestGetEmpByCondition(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class);
        List<Emp> list = mapper.getEmpByCondition(new Emp(null,"张三",null,"",""));
        System.out.println(list);
    }

12.4 choose、when、otherwise

  • choosewhenotherwise相当于if...else if..else
  • when至少要有一个,otherwise至多只有一个

12.4.1 接口

  /**
     * 测试 choose when otherwise
     */
    List<Emp> getEmpByChoose(Emp emp);

12.4.2 实现类

    <!--List<Emp> getEmpByChoose(Emp emp);-->
    <select id="getEmpByChoose" resultType="Emp">
        select * from mybatis.t_emp
        <where>
            <choose>
                <when test="empName != null and empName != ''">
                    emp_name = #{empName}
                </when>
                <when test="age != null and age != ''">
                    age = #{age}
                </when>
                <when test="sex != null and sex != ''">
                    sex = #{sex}
                </when>
                <when test="email != null and email != ''">
                    email = #{email}
                </when>
                <otherwise>
                    did = 1 <!--如果以上条件都不满足,就搜索did=1的条件-->
                </otherwise>
            </choose>
        </where>
    </select>

12.4.3 测试类

    @Test
    public void TestGetEmpByChoose(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class);
        List<Emp> list = mapper.getEmpByChoose(new Emp(null,"张三",23,"男","123@qq.com"));
        System.out.println(list);
    }
  • 相当于if a else if b else if c else d,只会执行其中一个
    mybatis

12.5 foreach

  • 属性:
    • collection:设置要循环的数组或集合
    • item:表示集合或数组中的每一个数据
    • separator:设置循环体之间的分隔符,分隔符前后默认有一个空格,如设置:separator=",",结果是:,
    • open:设置foreach标签中的内容的开始符
    • close:设置foreach标签中的内容的结束符
12.5.1.1 批量删除 接口
    /**
     * 通过数组实现批量删除
     */
    int deleteMoreByArray(@Param("eids") Integer[] eids);
12.5.1.2 批量删除 实现类
12.5.1.2.1 第一种写法
      <!--int deleteMoreByArray(@Param("eids") Integer[] eids);-->
    <delete id="deleteMoreByArray">
        delete  from mybatis.t_emp where eid in
        <foreach collection="eids" item="eid" separator="," open="(" close=")">
            #{eid}
        </foreach>
    </delete>
12.5.1.2.2 第二种写法
  <!--int deleteMoreByArray(@Param("eids") Integer[] eids);-->
    <delete id="deleteMoreByArray">
        delete  from mybatis.t_emp where
        <foreach collection="eids" item="eid" separator="or" > <!--分隔符默认加空格,separator="or"-->
            eid = #{eid}
        </foreach>
    </delete>
12.5.1.3 批量删除 测试类
      @Test
    public void TestDeleteMoreByArray(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class);
        int result = mapper.deleteMoreByArray(new Integer[]{6,7,8});
        System.out.println(result);
    }
12.5.2.1 批量增加 接口
   /**
     * 通过list集合实现批量添加
     */
    int insertMoreByList(@Param("emps")List<Emp> emps);
12.5.2.2 批量增加 实现类
         <!--int insertMoreByList(@Param("emps")List<Emp> emps);-->
    <insert id="insertMoreByList">
        insert into mybatis.t_emp values
        <foreach collection="emps" item="emp" separator=",">
            (null,#{emp.empName},#{emp.age},#{emp.sex},#{emp.email},null)
        </foreach>
    </insert>
12.5.2.4 批量增加 测试类
       @Test
    public void TestInsertMoreByList(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class);
        Emp emp1 = new Emp(null,"a",1,"男","123@321.com",null);
        Emp emp2 = new Emp(null,"b",2,"男","123@321.com",null);
        Emp emp3 = new Emp(null,"c",3,"男","123@321.com",null);
        List<Emp> emps = Arrays.asList(emp1, emp2, emp3);
        int result = mapper.insertMoreByList(emps);
        System.out.println(result);
    }

结果:
mybatis

插入前原表:
mybatis
插入后表:
mybatis

12.6 SQL片段

  • sql片段,可以记录一段公共sql片段,在使用的地方通过include标签进行引入
  • 声明sql片段:<sql>标签
<sql id="empColumns">eid,emp_name,age,sex,email</sql>

引用sql片段:<include>标签

<select id="getEmpByCondition" resultType="Emp">
        select <include refid="empColumns"></include> from mybatis.t_emp
        <trim prefix="where" suffixOverrides="and|or">
            <if test="empName != null and empName !=''">
                emp_name = #{empName} and
            </if>
            <if test="age != null and age !=''">
                age = #{age} and
            </if>
            <if test="sex != null and sex !=''">
                sex = #{sex} or
            </if>
            <if test="email != null and email !=''">
                email = #{email}
            </if>
        </trim>
    </select>

十三、MyBatis的缓存

13.1 MyBatis的一级缓存 (默认开启)

  • 一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问
  • 使一级缓存失效的四种情况:
    1. 不同的SqlSession对应不同的一级缓存
    2. 同一个SqlSession但是查询条件不同
    3. 同一个SqlSession两次查询期间执行了任何一次增删改操作。(增删改操作改变了数据,清空了缓存)
    4. 同一个SqlSession两次查询期间手动清空了缓存
  • 举例:
    一级缓存的范围:
//接口
Emp getEmpByEid(@Param("eid") Integer eid);
<!--实现类-->
    <!--Emp getEmpByEid(@Param("eid") Integer eid);-->
    <select id="getEmpByEid" resultType="Emp">
        select * from mybatis.t_emp where mybatis.t_emp.eid = #{eid};
    </select>
//测试类
    @Test
    public void testCache(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        CacheMapper mapper = sqlSession.getMapper(CacheMapper.class);
        Emp emp = mapper.getEmpByEid(1);
        System.out.println(emp);
        SqlSession sqlSession2 = SqlSessionUtils.getSqlSession();
        CacheMapper mapper1 = sqlSession2.getMapper(CacheMapper.class);
        Emp emp2 = mapper1.getEmpByEid(1);
        System.out.println(emp2);
    }

mybatis
mybatis

  • 一级缓存失败情况:
//接口
void insertEmp(Emp emp);
    <!--void insertEmp(Emp emp);-->
    <insert id="insertEmp">
        insert into mybatis.t_emp values(null,#{empName},#{age},#{sex},#{email},null);
    </insert>
//测试类
@Test
    public void testCacheInsertEmp(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        CacheMapper mapper = sqlSession.getMapper(CacheMapper.class);
        Emp emp = mapper.getEmpByEid(1);
        System.out.println(emp);
        mapper.insertEmp(new Emp(null,"abc",23,"男","123@qq.com"));
        Emp emp2 = mapper.getEmpByEid(1);
        System.out.println(emp2);
    }

mybatis

  • 手动清空缓存
//测试类
    @Test
    public void testCacheInsertEmp(){
        SqlSession sqlSession = SqlSessionUtils.getSqlSession();
        CacheMapper mapper = sqlSession.getMapper(CacheMapper.class);
        Emp emp = mapper.getEmpByEid(1);
        System.out.println(emp);
        sqlSession.clearCache();//清空缓存
        Emp emp2 = mapper.getEmpByEid(1);
        System.out.println(emp2);
    }

mybatis

13.2 MyBatis的二级缓存

  • 二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取

  • 二级缓存开启的条件

    1. 在核心配置文件中,设置全局配置<setting>标签中的属性cacheEnabled=“true”默认true,不需要设置
    2. 在映射文件(xxxMapper.xml)中设置标签<cache/>,该标签里可以设置二级缓存的配置信息。
    3. 二级缓存必须在SqlSession关闭或提交之后有效
      当没有关闭或提交SqlSession时,数据会先被存储在一级缓存中,当关闭或提交SqlSession后,数据才会被保存到二级缓存中。
    4. 查询的数据所转换的实体类类型必须实现序列化的接口
      public class Emp implements Serializable{}
  • 使二级缓存失效的情况:两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效。

  • 手动清空缓存:因为是sqlSession.clearCache();//清空缓存Sqlsession级别所以是一级缓存。

  • 测试:

//测试类
   /**
     * 测试二级缓存,工具类不能用
     */
    @Test
    public void testTwoCache(){
        //工具类不可用,只能重新写
        try{
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

            SqlSession sqlSession1 = sqlSessionFactory.openSession(true);
            CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
            System.out.println(mapper1.getEmpByEid(1));

            SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
            CacheMapper mapper2 = sqlSession2.getMapper(CacheMapper.class);
            System.out.println(mapper2.getEmpByEid(1));
        } catch (IOException e){
            e.printStackTrace();
        }
    }

mybatis

//测试类
   /**
     * 测试二级缓存,工具类不能用
     */
@Test
    public void testTwoCache(){
        //工具类不可用,只能重新写
        try{
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
            SqlSession sqlSession1 = sqlSessionFactory.openSession(true);
            CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);
            System.out.println(mapper1.getEmpByEid(1));
            sqlSession1.close();
            SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
            CacheMapper mapper2 = sqlSession2.getMapper(CacheMapper.class);
            System.out.println(mapper2.getEmpByEid(1));
            sqlSession2.close();
        } catch (IOException e){
            e.printStackTrace();
        }
    }

mybatis

13.2.1 二级缓存的相关配置

  • mapper配置文件中添加的<cache/>标签可以设置一些属性
  • eviction属性:缓存回收策略
    • LRU(Least Recently Used) – 最近最少使用的:移除最长时间不被使用的对象。
    • FIFO(First in First out) – 先进先出:按对象进入缓存的顺序来移除它们。
    • SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
    • WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
    • 默认的是 LRU
  • flushInterval属性:刷新间隔,单位毫秒
    • 默认情况是不设置,也就是没有刷新间隔,缓存仅仅**调用语句(增删改)**时刷新
  • size属性:引用数目,正整数
    • 代表缓存最多可以存储多少个对象,太大容易导致内存溢出
  • readOnly属性:只读,true/false
    • true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。例子:查询出来的信息保存在二级缓存中,这些信息不能被改,否则与数据库不相同了!
    • false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false

13.3 MyBatis缓存查询的顺序 (二级缓存开启的情况下,一级缓存已经开启)

  • 一级缓存只对应一个SqlSession,二级缓存对应多个SqlSession因此:
  • 先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用
  • 如果二级缓存没有命中,再查询一级缓存
  • 如果一级缓存也没有命中,则查询数据库
  • SqlSession关闭之后,一级缓存中的数据会写入二级缓存

13.4 整合第三方缓存EHCache代替MyBatis的二级缓存(了解)

13.4.1 添加依赖

<!-- Mybatis EHCache整合包 -->
<dependency>
	<groupId>org.mybatis.caches</groupId>
	<artifactId>mybatis-ehcache</artifactId>
	<version>1.2.1</version>
</dependency>
<!-- slf4j日志门面的一个具体实现 日志门面:只提供规定接口-->
<dependency>
	<groupId>ch.qos.logback</groupId>
	<artifactId>logback-classic</artifactId>
	<version>1.2.3</version>
</dependency>

mybatis

13.4.2 各个jar包的功能

jar包名称作用
mybatis-ehcacheMybatis和EHCache的整合包
ehcacheEHCache核心包
slf4j-apiSLF4J日志门面包
logback-classic支持SLF4J门面接口的一个具体实现,里面放实现类

13.4.3 创建EHCache的配置文件ehcache.xml

  • 名字必须叫ehcache.xml
<?xml version="1.0" encoding="utf-8" ?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
    <!-- 磁盘保存路径 自己定义-->
    <diskStore path="D:\**\ehcache"/>
    <defaultCache
            maxElementsInMemory="1000"
            maxElementsOnDisk="10000000"
            eternal="false"
            overflowToDisk="true"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
    </defaultCache>
</ehcache>

13.4.4 EHCache配置文件说明

属性名是否必须作用
maxElementsInMemory在内存中缓存的element的最大数目
maxElementsOnDisk在磁盘上缓存的element的最大数目,若是0表示无穷大
eternal设定缓存的elements是否永远不过期。 如果为true,则缓存的数据始终有效, 如果为false那么还要根据timeToIdleSeconds、timeToLiveSeconds判断
overflowToDisk设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上
timeToIdleSeconds当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时, 这些数据便会删除,默认值是0,也就是可闲置时间无穷大
timeToLiveSeconds缓存element的有效生命期,默认是0.,也就是element存活时间无穷大
diskSpoolBufferSizeMBDiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区
diskPersistent在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false
diskExpiryThreadIntervalSeconds磁盘缓存的清理线程运行间隔,默认是120秒。每个120s, 相应的线程会进行一次EhCache中数据的清理工作
memoryStoreEvictionPolicy当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。 默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出

13.4.5 设置二级缓存的类型

  • xxxMapper.xml文件中设置二级缓存类型
    <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

13.4.6 加入logback日志

  • 存在SLF4J时,作为简易日志的log4j将失效,此时我们需要借助SLF4J的具体实现logback来打印日志。创建logback的配置文件logback.xml名字固定,不可改变!
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
    <!-- 指定日志输出的位置 -->
    <appender name="STDOUT"
              class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <!-- 日志输出的格式 -->
            <!-- 按照顺序分别是:时间、日志级别、线程名称、打印日志的类、日志主体内容、换行 -->
            <pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern>
        </encoder>
    </appender>
    <!-- 设置全局日志级别。日志级别按顺序分别是:DEBUG、INFO、WARN、ERROR -->
    <!-- 指定任何一个日志级别都只打印当前级别和后面级别的日志。 -->
    <root level="DEBUG">
        <!-- 指定打印日志的appender,这里通过“STDOUT”引用了前面配置的appender -->
        <appender-ref ref="STDOUT" />
    </root>
    <!-- 根据特殊需求指定局部日志级别 自己定义-->
    <logger name="com.**.**.mapper" level="DEBUG"/>
</configuration>

此时可以重新运行一下测试类,输出的日志相比MyBatis的日志肯定会多一些,个人感觉会有点乱!

十四、MyBatis的逆向工程 MyBatis Generator (MBG)

  • 正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表。Hibernate是支持正向工程的
  • 逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成如下资源:
    • Java实体类
    • Mapper接口
    • Mapper映射文件

14.1 创建逆向工程的步骤

14.1.1 创建项目

mybatis
mybatis

14.1.2 添加依赖和插件 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">
    <parent>
        <artifactId>Mybatis_Study</artifactId>
        <groupId>com.mybatis</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.guo.mybatis</groupId>
    <artifactId>mybatis-MBG</artifactId>
    <packaging>jar</packaging>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!-- MyBatis核心依赖包 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.9</version>
        </dependency>
        <!-- junit测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!-- MySQL驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.28</version>
        </dependency>
        <!-- log4j日志 -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
    </dependencies>
    <!-- 控制Maven在构建过程中相关配置 -->
    <build>
        <!-- 构建过程中用到的插件 -->
        <plugins>
            <!-- 具体插件,逆向工程的操作是以构建过程中插件形式出现的 -->
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.0</version>
                <!-- 插件的依赖 -->
                <dependencies>
                    <!-- 逆向工程的核心依赖 -->
                    <dependency>
                        <groupId>org.mybatis.generator</groupId>
                        <artifactId>mybatis-generator-core</artifactId>
                        <version>1.3.2</version>
                    </dependency>
                    <!-- 数据库连接池 -->
                    <dependency>
                        <groupId>com.mchange</groupId>
                        <artifactId>c3p0</artifactId>
                        <version>0.9.2</version>
                    </dependency>
                    <!-- MySQL驱动 -->
                    <dependency>
                        <groupId>mysql</groupId>
                        <artifactId>mysql-connector-java</artifactId>
                        <version>8.0.28</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>

</project>

mybatis

14.1.3 创建MyBatis的核心配置文件 mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <properties resource="jdbc.properties"/>

    <typeAliases>
        <package name="com.guo.mybatis.pojo"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <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>

    <mappers>
        <package name="com.guo.mybatis.mapper"/>
    </mappers>
</configuration>

14.1.4 创建MyBatis的核心配置文件

文件名必须是:generatorConfig.xml

macOS系统路径是:./src/main/java

windows系统应该是:.\src\main\java

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
    <!--
    targetRuntime: 执行生成的逆向工程的版本
    MyBatis3Simple: 生成基本的CRUD(清新简洁版)只有增、删、改、查询所有数据、根据id查询一条数据
    MyBatis3: 生成带条件的CRUD(奢华尊享版)
    -->
    <context id="DB2Tables" targetRuntime="MyBatis3Simple">
        <!-- 数据库的连接信息 自己设置 -->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/mybatis"
                        userId="****"
                        password="****">
        </jdbcConnection>
        <!-- javaBean 实体类的生成策略-->
        <javaModelGenerator targetPackage="com.guo.mybatis.pojo" targetProject="./src/main/java">
            <!--是否对应子包,写的是com.guo.mybatis.pojo对应的就是含内包,如果这里设定是false,那com.guo.mybatis.pojo就是一个包的名字-->
            <property name="enableSubPackages" value="true" />
            <property name="trimStrings" value="true" /><!--去掉字符串前后的空格:字段名转换成属性,去掉空格-->
        </javaModelGenerator>
        <!-- SQL映射文件的生成策略 -->
        <sqlMapGenerator targetPackage="com.guo.mybatis.mapper"
                         targetProject="./src/main/resources">
            <property name="enableSubPackages" value="true" />
        </sqlMapGenerator>
        <!-- Mapper接口的生成策略 与上面SQL映射文件的生成对应 -->
        <javaClientGenerator type="XMLMAPPER"
                             targetPackage="com.guo.mybatis.mapper" targetProject="./src/main/java">
            <property name="enableSubPackages" value="true" />
        </javaClientGenerator>
        <!-- 逆向分析的表 -->
        <!-- tableName设置为*号,可以对应所有表,此时不写domainObjectName -->
        <!-- domainObjectName属性指定生成出来的实体类的类名 -->
        <table tableName="t_emp" domainObjectName="Emp"/>
        <table tableName="t_dept" domainObjectName="Dept"/>
    </context>
</generatorConfiguration>
  • 如果出现报错:Exception getting JDBC Driver,可能是pom.xml中,数据库驱动配置错误
  • dependency中的驱动
  • 两者的驱动版本应该相同
        <!-- MySQL驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.28</version>
        </dependency>
  • mybatis-generator-maven-plugin插件中的驱动
<!-- 控制Maven在构建过程中相关配置 -->
    <build>
        <!-- 构建过程中用到的插件 -->
        <plugins>
            <!-- 具体插件,逆向工程的操作是以构建过程中插件形式出现的 -->
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.0</version>
                <!-- 插件的依赖 -->
                <dependencies>
                    <!-- 逆向工程的核心依赖 -->
                    <dependency>
                        <groupId>org.mybatis.generator</groupId>
                        <artifactId>mybatis-generator-core</artifactId>
                        <version>1.3.2</version>
                    </dependency>
                    <!-- 数据库连接池 -->
                    <dependency>
                        <groupId>com.mchange</groupId>
                        <artifactId>c3p0</artifactId>
                        <version>0.9.2</version>
                    </dependency>
                    <!-- MySQL驱动 -->
                    <dependency>
                        <groupId>mysql</groupId>
                        <artifactId>mysql-connector-java</artifactId>
                        <version>8.0.28</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>

运行插件前:
mybatis
运行插件后
mybatis
注意新生成实体类中:没有有参构造方法,没有午餐构造方法,以及toString()方法,需要手动生成
自动生成驼峰结构的属性:
mybatis
精简版只生成5个方法:
mybatis

14.1.5 创建MyBatis的核心配置文件 详细版

  • 将上述MyBatis的核心配置文件设置修改:<context id="DB2Tables" targetRuntime="MyBatis3">
    mybatis
    注意新生成实体类中:没有有参构造方法,没有午餐构造方法,以及toString()方法,需要手动生成
    mybatis

普通添加和选择性添加,结果上相同,但方法有所不同:
mybatis
普通更新操作和选择性更新操作有很大不同,需要特别注意!
mybatis

14.1.6 创建测试类 QBC查询 (Query By Criteria 根据条件查询)

  • selectByExample:按条件查询,需要传入一个example对象或者null;如果传入一个null,则表示没有条件,也就是查询所有数据
  • example.createCriteria().xxx:创建条件对象,通过andXXX方法为SQL添加查询添加,每个条件之间是and关系
  • example.or().xxx:将之前添加的条件通过or拼接其他条件
    mybatis
    @Test
    public void testMBG(){
        try{
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
            SqlSession sqlSession = sqlSessionFactory.openSession(true);
            EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);

           //查询所有数据
           List<Emp> list =  mapper.selectByExample(null);
           list.forEach(emp -> System.out.println(emp));

        }catch (IOException e){
            e.printStackTrace();
        }
    }

mybatis
加上条件查询

  @Test
    public void testMBG(){
        try{
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
            SqlSession sqlSession = sqlSessionFactory.openSession(true);
            EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);

            //根据条件查询 QBC Query By Criteria 根据条件查询
            EmpExample example = new EmpExample();
            example.createCriteria().andEmpNameEqualTo("张三");
            List<Emp> list =  mapper.selectByExample(example);
            list.forEach(emp -> System.out.println(emp));
        }catch (IOException e){
            e.printStackTrace();
        }
    }

mybatis
可以继续叠加方法:

    @Test
    public void testMBG(){
        try{
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
            SqlSession sqlSession = sqlSessionFactory.openSession(true);
            EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);

            //根据条件查询 QBC Query By Criteria 根据条件查询
            EmpExample example = new EmpExample();
            example.createCriteria().andEmpNameEqualTo("张三").andAgeGreaterThanOrEqualTo(20);
            List<Emp> list =  mapper.selectByExample(example);
            list.forEach(emp -> System.out.println(emp));
        }catch (IOException e){
            e.printStackTrace();
        }
    }

mybatis
加上or

    @Test
    public void testMBG(){
        try{
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
            SqlSession sqlSession = sqlSessionFactory.openSession(true);
            EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);

            //根据条件查询 QBC Query By Criteria 根据条件查询
            EmpExample example = new EmpExample();
            example.createCriteria().andEmpNameEqualTo("张三").andAgeGreaterThanOrEqualTo(20);//名字是:张三年龄大于等于20
            example.or().andDidIsNotNull();//or设置  did值不为空
            List<Emp> list =  mapper.selectByExample(example);
            list.forEach(emp -> System.out.println(emp));
        }catch (IOException e){
            e.printStackTrace();
        }
    }

mybatis

14.1.7 创建测试类 普通修改updateByPrimaryKey

原表
mybatis

    @Test
    public void testMBGUpdate(){
        try{
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
            SqlSession sqlSession = sqlSessionFactory.openSession(true);
            EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
            mapper.updateByPrimaryKey(new Emp(12,"abc改",22,null,"456@qq.com",3));
        }catch (IOException e){
            e.printStackTrace();
        }
    }

运行后:
mybatis
性别变成了:null
mybatis

14.1.8 创建测试类 选择性普通修改updateByPrimaryKeySelective

原表
请添加图片描述

    @Test
    public void testMBGUpdateSelect(){
        try{
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
            SqlSession sqlSession = sqlSessionFactory.openSession(true);
            EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
            mapper.updateByPrimaryKeySelective(new Emp(12,"abc改",22,null,"456@qq.com",3));
        }catch (IOException e){
            e.printStackTrace();
        }
    }

运行后:
mybatis

表中性别没有变:
mybatis

十五、分页功能

15.1 分页插件使用步骤

15.1.1 添加依赖

    <!-- 分页查询 https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.2.0</version>
        </dependency>

15.1.2 配置分页插件

  • 在MyBatis的核心配置文件mybatis-config.xml中配置插件plugins
  • 核心配置文件中的标签必须按照固定的顺序(有的标签可以不写,但顺序一定不能乱):
    properties、settings、typeAliases、typeHandlers、objectFactory、objectWrapperFactory、reflectorFactory、plugins、environments、databaseIdProvider、mappers
<plugins>
	<!--设置分页插件-->
	<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>

15.2 分页插件的使用

15.2.1 开启分页功能

  • 在查询功能之前使用PageHelper.startPage(int pageNum, int pageSize)开启分页功能
    • pageNum:当前页的页码
    • pageSize:每页显示的条数
    @Test
    public void testPageHelper() throws IOException {
        try{
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
            SqlSession sqlSession = sqlSessionFactory.openSession(true);
            EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
            //访问第一页,每页四条数据
            PageHelper.startPage(2,4);//pageNum = 1,pageSize = 4
            List<Emp> list = mapper.selectByExample(null);//查询所有数据
            list.forEach(emp -> System.out.println(emp));
        }catch (IOException  e){
            e.printStackTrace();
        }

    }

mybatis

mybatis

15.2.2 分页相关数据

15.2.2.1 方法一:直接输出

PageHelper其实是一个Page<Object>类型

@Test
    public void testPageHelper() throws IOException {
        try{
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
            SqlSession sqlSession = sqlSessionFactory.openSession(true);
            EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
            //访问第一页,每页四条数据
            Page<Object> page = PageHelper.startPage(2,4);//pageNum = 1,pageSize = 4
            List<Emp> list = mapper.selectByExample(null);//查询所有数据
            //list.forEach(emp -> System.out.println(emp));
            System.out.println(page);
        }catch (IOException  e){
            e.printStackTrace();
        }

输出结果:

Page{count=true, pageNum=2, pageSize=4, startRow=4, endRow=8, total=9, pages=3, reasonable=false, pageSizeZero=false}[Emp{eid=5, empName='田七', age=23, sex='男', email='123@qq.com', did=2}, Emp{eid=6, empName='a', age=1, sex='男', email='123@321.com', did=null}, Emp{eid=7, empName='b', age=2, sex='男', email='123@321.com', did=null}, Emp{eid=8, empName='c', age=3, sex='男', email='123@321.com', did=null}]

count=true, pageNum=2: 当前页页码
pageSize=4:每页显示条数
startRow=4:从那行开始
endRow=8: 到底几行结束
total=9 :一共多少条数据
pages=3: 一共多少页

数据:

[Emp{eid=5, empName='田七', age=23, sex='男', email='123@qq.com', did=2}, Emp{eid=6, empName='a', age=1, sex='男', email='123@321.com', did=null}, Emp{eid=7, empName='b', age=2, sex='男', email='123@321.com', did=null}, Emp{eid=8, empName='c', age=3, sex='男', email='123@321.com', did=null}]
15.2.2.2 方法二:使用PageInfo
  • 在查询获取list集合之后,使用PageInfo<T> pageInfo = new PageInfo<>(List<T> list, intnavigatePages)获取分页相关数据
    • list:分页之后的数据
    • navigatePages:导航分页的页码数
    @Test
    public void testPageHelper() throws IOException {
        try{
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
            SqlSession sqlSession = sqlSessionFactory.openSession(true);
            EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
            //访问第一页,每页四条数据
            PageHelper.startPage(2,4);
            List<Emp> list = mapper.selectByExample(null);//查询所有数据
            //获取分页数据,设置导航分页时用    不建议写偶数,一般为基数
            PageInfo<Emp> page = new PageInfo<>(list,3);
            System.out.println(page);
        }catch (IOException  e){
            e.printStackTrace();
        }
PageInfo{
# pageNum=2:页码 ,pageSize=4:每页有多少条数据,size=4:当前页码真实的数量(最后一页不一定是满的),startRow=5:从第几行开始,endRow=8:从第几行结束, total=9:总条数,pages=3:页数
pageNum=2, pageSize=4, size=4, startRow=5, endRow=8, total=9, pages=3, 

# 分页相关数据
list=Page{count=true, pageNum=2, pageSize=4, startRow=4, endRow=8, total=9, pages=3, reasonable=false, pageSizeZero=false}[Emp{eid=5, empName='田七', age=23, sex='男', email='123@qq.com', did=2}, Emp{eid=6, empName='a', age=1, sex='男', email='123@321.com', did=null}, Emp{eid=7, empName='b', age=2, sex='男', email='123@321.com', did=null}, Emp{eid=8, empName='c', age=3, sex='男', email='123@321.com', did=null}], 

# prePage=1:上一页页码,nextPage=3:下一页页码,isFirstPage=false:是否是第一页,isLastPage=false:是否是最后一页,hasPreviousPage=true:是否有上一页,hasNextPage=true:是否有下一页,navigatePages=3:方法设置的3( PageInfo<Emp> page = new PageInfo<>(list,3);),navigatepageNums=[1, 2, 3]:导航分页页码,navigateFirstPage=1:导航分页从第几页开始,navigateLastPage=3:导航分页从第几页结束
prePage=1, nextPage=3, isFirstPage=false, isLastPage=false, hasPreviousPage=true, hasNextPage=true, navigatePages=3, navigateFirstPage=1, navigateLastPage=3, navigatepageNums=[1, 2, 3]}
  • 在页面中配置导航栏超链接时,可以通过访问PageInfo预对象。当isFirstPage=falsehasPreviousPage=true才需要配置首页上一页,即:不是第一页有上一页时才显示首页上一页。当isLastPage=falsehasNextPage=true才需要配置尾页下一页,即:不是尾页有下一页时才显示尾页下一页。设置导航分页,将navigatepageNums数组进行循环,设置超链接进行跳转。
15.2.2.3 分页数据相关属性
  • pageNum:当前页的页码
  • pageSize:每页显示的条数
  • size:当前页显示的真实条数
  • total:总记录数
  • pages:总页数
  • prePage:上一页的页码
  • nextPage:下一页的页码
  • isFirstPage/isLastPage:是否为第一页/最后一页
  • hasPreviousPage/hasNextPage:是否存在上一页/下一页
  • navigatePages:导航分页的页码数
  • navigatepageNums:导航分页的页码,[1,2,3,4,5]
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值