MyBatis零基础入门笔记

文章目录

  • 一、MyBatis
    • 1.1 什么是MyBatis
    • MyBatis简介
    • 1.2 持久化
        • 持久层
    • 1.3 为什么需要Mybatis
  • 二、MyBatis第一个程序
      • 1、搭建实验数据库
      • 2、导入MyBatis相关 jar 包
      • 3、编写MyBatis核心配置文件
      • 4、编写MyBatisUtils工具类
      • 5、创建实体类
      • 6、编写Mapper接口类
      • 7、编写Mapper.xml配置文件
      • 8、编写测试类
      • **可能出现问题说明:Maven静态资源过滤问题**
  • 三、CRUD操作及配置解析
      • 1、在UserMapper.java中添加对应方法
      • 2、在UserMapper.xml中添加对应语句
      • 3、在UserMapperTest.java测试类中测试
      • 思路二:使用万能的Map
      • 4、做模糊查询
  • 四、配置解析
      • 核心配置文件 (mybatis-config.xml)
      • 1、环境配置(environments)
      • 2、属性(properties)
      • 3、类型别名(typeAliases)
      • 4、设置(settings)
      • 5、映射器(mappers)
      • 6、生命周期和作用域
        • SqlSessionFactoryBuilder
        • SqlSessionFactory
        • SqlSession
  • 五、日志
      • 1、日志工厂
      • 2、STDOUT_LOGGING(标准日志输出)
      • 3、LOG4J
  • 六、ResultMap(结果集映射)
      • 1、查询为null问题
        • 解决方案:
      • 2、环境配置
        • **SQL**
        • 引入Maven依赖
        • **MyBatisUtils工具类**
        • **db.properties**
        • **log4j.properties**
        • **mybatis-config.xml**
      • 3、多对一:
        • ①实体类:
        • ②编写实体类对应的Mapper接口 【两个】:
        • ③编写Mapper接口对应的 mapper.xml配置文件 【两个】:
        • ④测试
      • 4、一对多:
        • ①实体类:
        • ②编写实体类对应的Mapper接口 【两个】:
        • ③编写Mapper接口对应的 mapper.xml配置文件 【两个】:
        • ④测试
      • 总结
  • 七、分页
      • **1、使用Limit实现分页**
      • 2、PageHelper分页插件实现
  • 八、注解开发
      • 1、Mybatis详细的执行流程
      • 2、注解增删改查
  • 九、Lombok
      • 1、常用注解
      • 2、使用
      • 3、Lombok的优缺点
  • 十、动态SQL
      • 1、环境配置:
        • SQL
        • IDutils工具类
        • 实体类
        • 编写Mapper接口及xml文件
        • mybatis核心配置文件
        • 插入初始数据
    • 2、if 语句
    • 3、Where
    • 4、Set
    • 5、choose语句
    • 6、SQL片段
    • 7、Foreach
    • 8、bind
  • 十一、缓存
      • 1、简介
      • 2、Mybatis缓存
    • 一级缓存
      • 一级缓存失效的四种情况
    • 二级缓存
      • 二级缓存结论:
    • EhCache
  • 补充:
    • **逆向工程 ( MBG )**

一、MyBatis

环境说明:

  • jdk 8 +
  • MySQL 5.7.19
  • maven-3.6.1
  • IDEA

学习前需要掌握:

  • JDBC
  • MySQL
  • Java 基础
  • Maven
  • Junit

1.1 什么是MyBatis

  • MyBatis 是一款优秀的持久层框架
  • MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集的过程
  • MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 实体类 【Plain Old Java Objects,普通的 Java对象】映射成数据库中的记录。
  • MyBatis 本是apache的一个开源项目ibatis, 2010年这个项目由apache 迁移到了google code,并且改名为MyBatis 。
  • 2013年11月迁移到Github .
  • Mybatis官方文档 : http://www.mybatis.org/mybatis-3/zh/index.html
  • GitHub : https://github.com/mybatis/mybatis-3

MyBatis简介

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

  • MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。

  • MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录.

1.2 持久化

持久化是将程序数据在持久状态和瞬时状态间转换的机制。

  • 即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。持久化的主要应用是将内存中的对象存储在数据库中,或者存储在磁盘文件中、XML数据文件中等等。
  • JDBC就是一种持久化机制。文件IO也是一种持久化机制。
  • 在生活中 : 将鲜肉冷藏,吃的时候再解冻的方法也是。将水果做成罐头的方法也是。

为什么需要持久化服务呢?那是由于内存本身的缺陷引起的

  • 内存断电后数据会丢失,但有一些对象是无论如何都不能丢失的,比如银行账号等,遗憾的是,人们还无法保证内存永不掉电。
  • 内存过于昂贵,与硬盘、光盘等外存相比,内存的价格要高2~3个数量级,而且维持成本也高,至少需要一直供电吧。所以即使对象不需要永久保存,也会因为内存的容量限制不能一直呆在内存中,需要持久化来缓存到外存。
持久层

什么是持久层?

  • 完成持久化工作的代码块 . ----> dao层 【DAO (Data Access Object) 数据访问对象】
  • 大多数情况下特别是企业级应用,数据持久化往往也就意味着将内存中的数据保存到磁盘上加以固化,而持久化的实现过程则大多通过各种关系数据库来完成。
  • 不过这里有一个字需要特别强调,也就是所谓的“层”。对于应用系统而言,数据持久功能大多是必不可少的组成部分。也就是说,我们的系统中,已经天然的具备了“持久层”概念?也许是,但也许实际情况并非如此。之所以要独立出一个“持久层”的概念,而不是“持久模块”,“持久单元”,也就意味着,我们的系统架构中,应该有一个相对独立的逻辑层面,专注于数据持久化逻辑的实现.
  • 与系统其他部分相对而言,这个层面应该具有一个较为清晰和严格的逻辑边界。【说白了就是用来操作数据库存在的!】

1.3 为什么需要Mybatis

  • Mybatis就是帮助程序猿将数据存入数据库中 , 和从数据库中取数据 .

  • 传统的jdbc操作 , 有很多重复代码块 .比如 : 数据取出时的封装 , 数据库的建立连接等等… , 通过框架可以减少重复代码,提高开发效率 .

  • MyBatis 是一个半自动化的ORM框架 (Object Relationship Mapping) -->对象关系映射

  • 所有的事情,不用Mybatis依旧可以做到,只是用了它,所有实现会更加简单!技术没有高低之分,只有使用这个技术的人有高低之别

  • MyBatis的优点

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

二、MyBatis第一个程序

思路流程:搭建环境–>导入Mybatis—>编写代码—>测试

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

image-20220112155546450

代码演示

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,'张三','abcdef'),(3,'李四','987654');

2、导入MyBatis相关 jar 包

  • GitHub上找
  • MyBatis-Study.pom

<groupId>com.shroudgroupId>
<artifactId>MyBatis-StudyartifactId>
<packaging>pompackaging>
<version>1.0-SNAPSHOTversion>
<modules>
    <module>mybatis-01module>
modules>


<dependencies>
    
    <dependency>
        <groupId>mysqlgroupId>
        <artifactId>mysql-connector-javaartifactId>
        <version>5.1.47version>
    dependency>
    
    <dependency>
        <groupId>org.mybatisgroupId>
        <artifactId>mybatisartifactId>
        <version>3.5.2version>
    dependency>
    
    <dependency>
        <groupId>junitgroupId>
        <artifactId>junitartifactId>
        <version>4.12version>
    dependency>
dependencies>


<build>
    <resources>
        <resource>
            <directory>src/main/javadirectory>
            <includes>
                <include>**/*.propertiesinclude>
                <include>**/*.xmlinclude>
            includes>
            <filtering>falsefiltering>
        resource>
        <resource>
            <directory>src/main/resourcesdirectory>
            <includes>
                <include>**/*.propertiesinclude>
                <include>**/*.xmlinclude>
            includes>
            <filtering>falsefiltering>
        resource>
    resources>
build>

  • mybatis-01.pom
  • 防止资源导出失败,子父工程都加上
<parent>
    <artifactId>MyBatis-StudyartifactId>
    <groupId>com.shroudgroupId>
    <version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>

<artifactId>mybatis-01artifactId>

<build>
    <resources>
        <resource>
            <directory>src/main/javadirectory>
            <includes>
                <include>**/*.propertiesinclude>
                <include>**/*.xmlinclude>
            includes>
            <filtering>falsefiltering>
        resource>
        <resource>
            <directory>src/main/resourcesdirectory>
            <includes>
                <include>**/*.propertiesinclude>
                <include>**/*.xmlinclude>
            includes>
            <filtering>falsefiltering>
        resource>
    resources>
build>

3、编写MyBatis核心配置文件

  • 查看帮助文档
  • 注意!:每一个Mapper.xml都需要在Mybatis核心配置文件中注册!
    • ​ 使用resource绑定mapper,需要使用路径!
  • mybatis-config.xml

DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?
                useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="101699"/>
            dataSource>
        environment>
    environments>

    
    <mappers>
        <mapper resource="com/kuang/dao/UserMapper.xml"/>
    mappers>

configuration>

4、编写MyBatisUtils工具类

  • 查看帮助文档
  • 1、根据全局配置文件,利用 SqlSessionFactoryBuilder创建SqlSessionFactory
  • 2、使用SqlSessionFactory获取sqlSession对象。一个 SqlSession对象代表和数据库的一次会话
  • MyBatisUtils.java
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);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    //获取SqlSession连接
    //既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
    // SqlSession 提供了在数据库执行 SQL 命令所需的所有方法
    public static SqlSession getSqlSession(){
        //openSession():创建一个非自动提交功能的SqlSession,需要手动提交
        //openSession(true):创建一个有自动提交功能的SqlSession
        return sqlSessionFactory.openSession();
    }
}

5、创建实体类

  • User.java
public class User {
   
   private int id;  //id
   private String name;   //姓名
   private String pwd;   //密码
   
   //构造,有参,无参
   //set/get
   //toString()
}

6、编写Mapper接口类

  • UserMapper.java
import com.kuang.pojo.User;
import java.util.List;

public interface UserMapper {
    List<User> selectUser();
}

7、编写Mapper.xml配置文件

  • namespace 十分重要,不能写错!
  • 配置文件中namespace中的名称为对应Mapper接口或者Dao接口的完整包名,必须一致!
  • UserMapper.xml

DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">


<mapper namespace="com.kuang.dao.UserMapper">
    
    <select id="selectUser" resultType="com.kuang.pojo.User">
        select * from mybatis.user
    select>
mapper>

8、编写测试类

  • Junit 包测试
  • SqlSession 的实例不是线程安全的,因此是不能 被共享的。
  • SqlSession每次使用完成后需要正确关闭,这个 关闭操作是必须的
  • SqlSession可以直接调用方法的id进行数据库操作,但是我们一般还是推荐使用SqlSession获取到Dao接口的代理类,执行代理对象的方法,可以更安全的进行类型检查操作
public class UserMapperTest {
    @Test
    public void test(){

        //第一步:获得SqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        try {
            //方法一:getMapper得到接口
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            List<User> userList = mapper.selectUser();
            //方法二(不建议):
      		//List users = session.selectList("com.kuang.mapper.UserMapper.selectUser");

            for (User user:userList){
                System.out.println(user);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //关闭sqlSession
            sqlSession.close();
        }
    }
}

可能出现问题说明:Maven静态资源过滤问题

  • 添加在pom文件里

<build>
    <resources>
        <resource>
            <directory>src/main/javadirectory>
            <includes>
                <include>**/*.propertiesinclude>
                <include>**/*.xmlinclude>
            includes>
            <filtering>falsefiltering>
        resource>
        <resource>
            <directory>src/main/resourcesdirectory>
            <includes>
                <include>**/*.propertiesinclude>
                <include>**/*.xmlinclude>
            includes>
            <filtering>falsefiltering>
        resource>
    resources>
build>

三、CRUD操作及配置解析

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

前面配置与上面一致,只需要操作UserMapper.java、UserMapper.xml、测试类UserMapperTest.java

需求:1.查询全部用户、2.根据id查询用户、3.插入一个用户、4.修改用户、5.根据Id删除用户

1、在UserMapper.java中添加对应方法

import com.kuang.pojo.User;
import java.util.List;

public interface UserMapper {
    //查询全部用户
    List<User> selectUser();

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

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

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

    //根据Id删除用户
    int deleteUser(int id);
}

2、在UserMapper.xml中添加对应语句


        DOCTYPE mapper
                PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                "http://mybatis.org/dtd/mybatis-3-mapper.dtd">


<mapper namespace="com.kuang.dao.UserMapper">

    
    <select id="selectUser" resultType="com.kuang.pojo.User">
       select * from mybatis.user;
    select>

    
    
    <select id="getUserById" parameterType="int" resultType="com.kuang.pojo.User">
        select * from mybatis.user where id = #{id};
    select>

    
    <insert id="addUser" parameterType="com.kuang.pojo.User">
        insert into mybatis.user (id, name, pwd) values (#{id},#{name},#{pwd});
    insert>

    
    <update id="updateUser" parameterType="com.kuang.pojo.User">
        update mybatis.user set name=#{name},pwd=#{pwd} where id=#{id};
    update>

    
    <delete id="deleteUser" parameterType="int">
        delete from mybatis.user where id=#{id};
    delete>

mapper>

3、在UserMapperTest.java测试类中测试

注意点:增、删、改操作需要提交事务!

public class UserMapperTest {

    @Test
    public void test(){
        //第一步:获得SqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();

        try {
            //方法一:getMapper得到接口
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            List<User> userList = mapper.selectUser();

            for (User user:userList){
                System.out.println(user);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //关闭sqlSession
            sqlSession.close();
        }
    }

    @Test
    public void getUserById(){
        // 1.获取sqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        // 2.获取需要的mapper接口的代理对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        // 3.调用对应的方法执行操作
        User userById = mapper.getUserById(1);
        System.out.println(userById);
        // 4.关闭sqlSession
        sqlSession.close();
    }

    @Test
    public void addUser(){
        // 1.获取sqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        // 2.获取需要的mapper接口的代理对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        // 3.调用对应的方法执行操作
        int res = mapper.addUser(new User(4, "哈哈", "12345"));
        if (res>0){
            System.out.println("插入成功!");
        }
        // 4.提交事务
        sqlSession.commit();
        // 5.关闭sqlSession
        sqlSession.close();
    }

    @Test
    public void updateUser(){
        // 1.获取sqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        // 2.获取需要的mapper接口的代理对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        // 3.调用对应的方法执行操作
        mapper.updateUser(new User(4,"呵呵","54321"));
        // 4.提交事务
        sqlSession.commit();
        // 5.关闭sqlSession
        sqlSession.close();
    }

    @Test
    public void deleteUser(){
        // 1.获取sqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        // 2.获取需要的mapper接口的代理对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        // 3.调用对应的方法执行操作
        mapper.deleteUser(4);
        // 4.提交事务
        sqlSession.commit();
        // 5.关闭sqlSession
        sqlSession.close();
    }
}

思路二:使用万能的Map

1、在接口方法中,参数直接传递Map;(UserMapper.java)

//使用Map完成添加
int addUserMap(Map<String,Object> map);

//使用Map根据Id查询用户
User getUserByIdMap(Map<String,Object> map);

2、编写sql语句的时候,需要传递参数类型,参数类型为map;(UserMapper.xml)


<insert id="addUserMap" parameterType="map">
    insert into user (id,pwd) values (#{userid},#{password});
insert>


<select id="getUserByIdMap" parameterType="map" resultType="com.kuang.pojo.User">
    select * from user where id=#{id};
select>

3、在使用方法的时候,Map的 key 为 sql中取的值即可,没有顺序要求!(UserMapper.xml)

​ 这里的参数值必须一致:

​ insert into user (id,pwd) values (#{userid},#{password});

​ map.put(“userid”, 5); map.put(“password”, “22133”);

​ 这里的参数值必须一致:

​ select * from user where id=#{id};

	 map.put("id", 1);
@Test
public void addUserMap(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    Map<String, Object> map = new HashMap<String, Object>();

    map.put("userid", 5);
    map.put("password", "22133");
    mapper.addUserMap(map);

    sqlSession.commit();
    sqlSession.close();
}

@Test
public void getUserByIdMap(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    Map<String, Object> map = new HashMap<String, Object>();

    map.put("id", 1);
    User userByIdMap = mapper.getUserByIdMap(map);
    System.out.println(userByIdMap);

    sqlSession.close();
}

总结:如果参数过多,我们可以考虑直接使用Map实现,如果参数比较少,直接传递参数即可

4、做模糊查询

①在UserMapper.java中添加对应方法

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

②在UserMapper.xml中添加对应语句

<!--模糊查询-->
<select id="getUserLike" resultType="com.kuang.pojo.User">
    select * from user where name like #{value};
    select * from user where name like CONCAT(#{name},'%');
</select>
  1. ​ java代码执行(测试)的时候,传递通配符 %

    ​ select * from user where name like #{value};

    ​ List userList = mapper.getUserLike(“李%”);

  2. ​ 在sql语句拼接使用通配符 %

    ​ select * from user where name like CONCAT(#{name},‘%’);

    ​ List userList = mapper.getUserLike(“李”);

@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();
}

四、配置解析

核心配置文件 (mybatis-config.xml)

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

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

注意元素节点的顺序!顺序不对会报错!

1、环境配置(environments)

MyBatis 可以配置成适应多种环境

不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。(default=“环境id”)

学会使用配置多套运行环境

MyBatis 默认的事务管理器就是JDBC,连接池:POOLED


<environments default="test">
    
    <environment id="development">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/mybatis?
            useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
            <property name="username" value="root"/>
            <property name="password" value="101699"/>
        dataSource>
    environment>
    
    
    <environment id="oracle环境">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="Driver: com.ibm.db2.jdbc.app.DB2Driver"/>
            <property name="url" value="jdbc:db2://localhost:5000/orcl"/>
            <property name="username" value="scott"/>
            <property name="password" value="ccat"/>
        dataSource>
    environment>
environments>

配置MyBatis的多套运行环境,将SQL映射到多个不同的数据库上,必须指定其中一个为默认运行环境(通过default指定)

  • 子元素节点:environment

  • transactionManager - [ 事务管理器 ]

  • ​ 提供两种事务管理器:JDBC 、MANAGED

  • 
    <transactionManager type="[ JDBC | MANAGED ]"/>
    
  • dataSource -[ 数据源 ]

  • ​ dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。

  • ​ 数据源是必须配置的。

  • ​ 有三种内建的数据源类型:UNPOOLED、POOLED、JNDI

  • <dataSource type="POOLED">	--> type="[	UNPOOLED | POOLED | JNDI]"
    
  • ​ unpooled:这个数据源的实现只是每次被请求时打开和关闭连接。

  • pooled:这个数据源的实现利用“池”的概念将 JDBC 连接对象组织起来 , 使得并发 Web 应用快速响应请求的流行处理方式。

  • ​ jndi:这个数据源的实现是为了能在如 Spring 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。

  • ​ 数据源也有很多第三方的实现,比如dbcp,c3p0,druid等等…

2、属性(properties)

我们可以通过properties属性来实现引用配置文件

这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。(db.properties->放在mian/resource目录下)

第一步:在资源目录下新建一个.properties文件 (db.properties)

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8
username=root
password=101699

第二步:在核心配置文件中引入 (mybatis-config.xml)

  • 可以直接引入外部文件
  • 可以在其中增加一些属性配置()
  • 如果两个文件有同一个字段,优先使用外部配置文件的!

<properties resource="db.properties">
    
properties>

第三步:使用

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

3、类型别名(typeAliases)

  • 类型别名可为 Java 类型设置一个缩写名字。存在的意义仅在于用来降低冗余的全限定类名书写
  • MyBatis中的别名是不区分大小写的

<typeAliases>
    <typeAlias type="com.kuang.pojo.User" alias="User"/>
typeAliases>
  • 当这样配置时,User可以用在任何使用com.kuang.pojo.User的地方

  • 也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean
    每一个在包 com.kuang.pojo 中的 Java Bean,在没有注解的情况下,

    会使用 Bean 的首字母小写的非限定类名来作为它的别名,比如 com.kuang.pojo.User 的别名为 user


<typeAliases>
    <package name="com.kuang.pojo"/>
typeAliases>
  • 若有注解,则别名为其注解值。通常用来解决不同包中相同类名的别名冲突(需要与指定包名一起使用)
@Alias("Shroud")
public class User {
    ...
}

在实体类比较少的时候,使用第一种方式

如果实体类十分多,建议使用第二种方式

第一种可以DIY别名;第二种则不行,如果非要改,需要在实体类上增加注解

使用类型别名之后,就不用类的全限定名称了,

在UserMapper.xml中,resultType就可以使用别名名称(resultType=“Shroud”)


DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.kuang.dao.UserMapper">
    
    
    <select id="selectUser" resultType="Shroud">
        select * from user;
    select>
    
mapper>

下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,

resultType=

​ 对于引用数据类型,都是将大写字母转小写,比如 HashMap 对应的别名是 ‘hashmap’

​ 基本数据类型考虑到重复的问题,会在其前面加上 ‘下划线’,比如 byte 对应的别名是 '下划线byte

注意,为了应对原始类型的命名重复,采取了特殊的命名风格。

别名 映射的类型
_byte byte
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
object Object
map Map
hashmap HashMap
list List
arraylist ArrayList
collection Collection
iterator Iterator

4、设置(settings)

设置名 描述 有效值 默认值
cacheEnabled (缓存开启关闭) 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 true | false true
lazyLoadingEnabled (懒加载) 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 true | false false
aggressiveLazyLoading 开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载(参考 lazyLoadTriggerMethods)。 true | false false (在 3.4.1 及之前的版本中默认为 true)
mapUnderscoreToCamelCase 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 true | false false
logImpl(日志实现) 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING 未设置
<settings>
    
    <setting name="mapUnderscoreToCamelCase" value="true"/>
settings>

5、映射器(mappers)

注意!!!每一个Mapper.xml都需要在Mybatis核心配置文件中注册!

方式一


<mappers>
    <mapper resource="com/kuang/dao/UserMapper.xml"/>
mappers>

方式二



<mappers>
    <mapper class="com.kuang.dao.UserMapper"/>
mappers>

方式三



<mappers>
    <package name="com.kuang.dao"/>
mappers>

方式二&方式三的注意点

  • 接口和对应的Mapper配置文件必须同名
  • 接口和对应的Mapper配置文件编译后必须在同一个文件夹下
    1. 可以把接口和对应的Mapper创建在同一个包下
    2. 可以在resources文件加中创建和接口相同层数并且名称相同的包,然后创建接口对应的Mapper

Mapper文件(UserMapper.xml)


DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">


<mapper namespace="com.kuang.dao.UserMapper">
    
    <select id="selectUser" resultType="Shroud">
        select * from user;
    select>
mapper>
  • namespace中文意思:命名空间,作用如下:

    • namespace的命名必须跟某个接口同名
    • 接口中的方法与映射文件中sql语句id应该一一对应
    1. namespace和子元素的id联合保证唯一 , 区别不同的mapper
    2. 绑定DAO/Mapper接口
    3. namespace命名规则 : 包名+类名(com.kuang.dao.UserMapper)

6、生命周期和作用域

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

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

SqlSessionFactoryBuilder
  • 一旦创建了 SqlSessionFactory,就不再需要它了。
  • 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(局部变量)
SqlSessionFactory
  • 可以想象为:数据库连接池

  • SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。

  • 因此 SqlSessionFactory 的最佳作用域是应用作用域(Application)-类似保证全局只有一份变量 (全局变量)

  • 最简单的就是使用单例模式或者静态单例模式。

SqlSession
  • 想象为连接到连接池的一个请求

  • SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域

  • 用完之后需要马上关闭,否则会导致资源被占用!

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

这里的Mapper,就代表一个具体的业务!

五、日志

<dependency>
    <groupId>log4jgroupId>
    <artifactId>log4jartifactId>
    <version>1.2.17version>
dependency>

1、日志工厂

如果一个数据库操作出现了异常,我们需要排错。日志就是最好的助手!!!

曾经:sout,Debug

现在:日志工厂!

logImpl 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING 未设置

●SLF4J

LOG4J 【掌握】

●LOG4J2

●JDK_LOGGING

●COMMONS_LOGGING

STDOUT_LOGGING 【掌握】

●NO_LOGGING

配置在mybatis-config.xml中


<settings>
    <setting name="logImpl" value="LOG4J"/>
settings>

在MyBatis中具体使用那个日志实现,在设置中设定!

2、STDOUT_LOGGING(标准日志输出)

在mybatis核心配置文件中,配置我们的日志(mybatis-3-config.xml)


<settings>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
settings>

当执行测试方法,控制台输出:


Opening JDBC Connection
Created connection 1885996206.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@706a04ae]
==>  Preparing: select * from user where id=?; 
==> Parameters: 2(Integer)
<==    Columns: id, name, pwd
<==        Row: 2, 张三, abcdef
<==      Total: 1
User{id=2, name='张三', pwd='abcdef'}
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@706a04ae]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@706a04ae]
Returned connection 1885996206 to pool.

3、LOG4J

什么是log4j?

  • Log4j是Apache的一个开放源代码项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件

  • 我们也可以控制每一条日志的输出格式。

  • 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。

  • 通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

1. 在Maven里先导入log4j的jar包 (pom.xml)

<dependencies>
    <dependency>
        <groupId>log4jgroupId>
        <artifactId>log4jartifactId>
        <version>1.2.17version>
    dependency>
dependencies>

2.创建log4j.properties(resources/log4j.properties)

#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file

#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n

#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/kuang.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n

#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

3.配置log4j为日志的实现(mybatis-config.xml)


<settings>
    <setting name="logImpl" value="LOG4J"/>
settings>

4.Log4j的使用!直接运行测试查询

  • 使用Log4j 输出日志
  • 可以看到还生成了一个日志的文件 【需要修改file的日志级别】
  • log4j.appender.file.File=./log/kuang.log

测试之后,会生成一个.log文件,里面存放具体日志信息

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

简单使用:

​ 1.在要使用Log4j的类中,导入包 import org.apache.log4j.Logger;

​ 2.日志对象,参数为当前类的class

static Logger logger = Logger.getLogger(UserMapperTest.class);

​ 3.设置日志级别

logger.info("info:进入了testLog4j");
logger.debug("debug:进入了testLog4j");
logger.error("error:进入了testLog4j");

六、ResultMap(结果集映射)

1、查询为null问题

要解决的问题:实体类属性和数据库列名不一致如何映射

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

环境:新建一个项目,将之前的项目拷贝过来

1、查看之前的数据库的字段名

MyBatis零基础入门笔记_第1张图片

2、Java中的实体类设计(User.java)

public class User {

    private int id; //id
    private String name; //姓名
    private String password; //密码和数据库不一样!
   
   //构造
   //set/get
   //toString()
}

3、接口(UserMapper.java)

public interface UserMapper {

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

}

4、mapper映射文件(UserMapper.xml)


<select id="getUserById" resultType="User">
select * from user where id=#{id};
select>

5、测试

@Test
public void getUserById(){
    // 1.获取sqlSession对象
    SqlSession sqlSession = MyBatisUtils.getSession();
    // 2.获取需要的mapper接口的代理对象
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    // 3.调用对应的方法执行操作
    User userById = mapper.getUserById(1);
    System.out.println(userById);
    // 4.关闭sqlSession
    sqlSession.close();
}

结果:

  • User{id=1, name=‘狂神’, password=‘null’}
  • 查询出来发现 password 为空 . 说明出现了查询为null问题!

分析:

  • select * from user where id = #{id} 可以看做

    select id,name,pwd from user where id = #{id}

  • mybatis会根据这些查询的列名(会将列名转化为小写,数据库不区分大小写) , 去对应的实体类中查找相应列名的set方法设值 , 由于找不到setPwd() , 所以password返回null ; 【自动映射】

解决方案:

方案一:为列名指定别名 , 别名和java实体类的属性名一致 .

<select id="getUserById" resultType="com.kuang.pojo.User">
    select id,name,pwd as password from user where id=#{id};
select>

方案二:使用结果集映射->ResultMap 【推荐】


<mapper namespace="com.kuang.dao.UserMapper">

    <resultMap id="UserMap" type="com.kuang.pojo.User">
        
        <id property="id" column="id"/>
        
        
        <result property="name" column="name"/>
        <result property="password" column="pwd"/>
    resultMap>

    
    <select id="getUserById" resultMap="UserMap">
        select * from user where id=#{id};
    select>
    
mapper>
<resultMap id="UserMap" type="com.kuang.pojo.User">
    resultMap: 自定义结果集映射规则
        id: 唯一标识, 方便被引用
        type: 映射到的实体类的全类名

<id property="id" column="id"/>
    id: 映射实体类中与主键对应的属性
        column: 主键列的列名
        property: 主键列对应的属性

<select id="getUserById" resultMap="UserMap">
    如果javaBen属性名和列名不一致, 不应该再使用resultType配置返回值类型
    而是使用resultMap引用自定义的结果集映射规则

2、环境配置

接下来演示一个部门对应多个员工,一个员工对应一个部门的关系映射。

SQL
CREATE TABLE `teacher` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT INTO teacher(`id`, `name`) VALUES (1, '秦老师');

CREATE TABLE `student` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
`tid` INT(10) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fktid` (`tid`),
CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8


INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小红', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小张', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1');
引入Maven依赖
<dependencies>
    <dependency>
        <groupId>org.projectlombokgroupId>
        <artifactId>lombokartifactId>
        <version>1.18.10version>
    dependency>

    <dependency>
        <groupId>log4jgroupId>
        <artifactId>log4jartifactId>
        <version>1.2.17version>
    dependency>
dependencies>


<build>
    <resources>
        <resource>
            <directory>src/main/javadirectory>
            <includes>
                <include>**/*.propertiesinclude>
                <include>**/*.xmlinclude>
            includes>
            <filtering>falsefiltering>
        resource>
        <resource>
            <directory>src/main/resourcesdirectory>
            <includes>
                <include>**/*.propertiesinclude>
                <include>**/*.xmlinclude>
            includes>
            <filtering>falsefiltering>
        resource>
    resources>
build>
MyBatisUtils工具类
//sqlSessionFactory --> sqlSession
public class MybatisUtils {

    private static SqlSessionFactory sqlSessionFactory;

    static {
        try {
            // 获取sqlSessionFactory对象
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 获取sqlSession连接
    // 既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
    // SqlSession 提供了在数据库执行 SQL 命令所需的所有方法
    public static SqlSession getSqlSession(){
        //openSession():创建一个非自动提交功能的SqlSession,需要手动提交
        //openSession(true):创建一个有自动提交功能的SqlSession
        return sqlSessionFactory.openSession(true);
    }
}
db.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8
username=root
password=101699
log4j.properties
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file

#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n

#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/kuang.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n

#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
mybatis-config.xml

DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

    
    <properties resource="db.properties"/>

    
    <settings>
        <setting name="logImpl" value="LOG4J"/>
    settings>

    
    <typeAliases>
        <package name="com.kuang.pojo"/>
        
        
    typeAliases>

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

    
    <mappers>
        <package name="com.kuang.dao"/>
        
        
    mappers>

configuration>

3、多个学生,对应一个老师

对于学生这边而言,关联,多个学生,关联一个老师【多对一】

对于老师而言,集合,一个老师,有很学生【一对多】

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

3、多对一:

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

多对一的理解:

  • 多个学生对应一个老师
  • 如果对于学生这边,就是一个多对一的现象,即从学生这边关联一个老师!
①实体类:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {

    private int id;
    private String name;

    // 多个学生可以是同一个老师,即多对一
    private Teacher teacher;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {

    private int id;
    private String name;

}
②编写实体类对应的Mapper接口 【两个】:

无论有没有需求,都应该写上,以备后来之需!

public interface StudentMapper {
    
    //按结果嵌套处理
    // 获取所有学生及对应老师的信息
    public List<Student> getStudents2();

    //按查询嵌套处理:
    // 获取所有学生及对应老师的信息
    public List<Student> getStudent();
}
public interface TeacherMapper {

}
③编写Mapper接口对应的 mapper.xml配置文件 【两个】:

DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.kuang.dao.StudentMapper">

    
    <select id="getStudents2" resultMap="StudentTeacher2">
        select s.id sid,s.name sname,t.name tname
        from student s,teacher t
        where s.tid=t.id
    select>

    <resultMap id="StudentTeacher2" type="student">
        <id property="id" column="sid"/>
        <result property="name" column="sname"/>
        
        <association property="teacher" column="teacher">
            <result property="name" column="tname"/>
        association>
    resultMap>


    
    <select id="getStudent" resultMap="StudentTeacher">
        select * from student
    select>

    <resultMap id="StudentTeacher" type="student">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        
        <association property="teacher" column="tid" javaType="teacher" select="getTeacher"/>
    resultMap>

    <select id="getTeacher" resultType="teacher">
        select * from teacher where id=#{id}
    select>
    
mapper>

DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.kuang.dao.TeacherMapper">
mapper>
④测试
@Test
public void getStudent2(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    List<Student> student = mapper.getStudents2();
    for (Student students : student) {
        System.out.println(students);
    }
    sqlSession.close();
}

@Test
public void getStudent(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    List<Student> student = mapper.getStudent();
    for (Student students : student) {
        System.out.println(students);
    }
    sqlSession.close();
}

4、一对多:

一对多的理解:

  • 一个老师拥有多个学生
  • 如果对于老师这边,就是一个一对多的现象,即从一个老师下面拥有一群学生(集合)!
①实体类:
@Data
public class Teacher {

    private int id;
    private String name;

    //一个老师拥有多个学生
    private List<Student> students;
}
@Data
public class Student {

    private int id;
    private String name;
    private int tid;

}
②编写实体类对应的Mapper接口 【两个】:

无论有没有需求,都应该写上,以备后来之需!

public interface TeacherMapper {

    //获取指定老师下的所有学生及老师的信息
    Teacher getTeacher(@Param("tid") int id);

    Teacher getTeacher2(@Param("tid") int id);
}
public interface StudentMapper {

}
③编写Mapper接口对应的 mapper.xml配置文件 【两个】:

DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.kuang.dao.TeacherMapper">

    
    
    <select id="getTeacher" resultMap="TeacherStudent">
        select s.id sid,s.name sname,t.name tname,t.id tid
        from student s,teacher t
        where t.id=s.tid and t.id=#{tid}
    select>

    <resultMap id="TeacherStudent" type="teacher">
        <id property="id" column="tid"/>
        <result property="name" column="tname"/>
        <collection property="students" ofType="student">
            <id property="id" column="sid"/>
            <result property="name" column="sname"/>
            <result property="tid" column="tid"/>
        collection>
    resultMap>


    
    
    <select id="getTeacher2" resultMap="TeacherStudent2">
        select * from teacher where id=#{tid}
    select>

    <resultMap id="TeacherStudent2" type="teacher">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        
        
        <collection property="students" javaType="ArrayList" ofType="student" select="getStudentByTeacherId" column="id"/>
    resultMap>

    <select id="getStudentByTeacherId" resultType="student">
        select * from student where tid=#{tid}
    select>

mapper>

DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.kuang.dao.StudentMapper">
    
mapper>
④测试
@Test
public void getTeacher(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
    Teacher teacher = mapper.getTeacher(1);
    System.out.println(teacher);
    sqlSession.close();
}

@Test
public void getTeacher2() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
    Teacher teacher = mapper.getTeacher2(1);
    System.out.println(teacher);
    sqlSession.close();
}

//Teacher(id=1, name=秦老师,
// students=[Student(id=1, name=小明, tid=1),
// Student(id=2, name=小红, tid=1),
// Student(id=3, name=小张, tid=1),
// Student(id=4, name=小李, tid=1),
// Student(id=5, name=小王, tid=1)])

总结

  • 联合查询的方式只会向数据库发送一次SQL语句
  • 嵌套查询的方式在查询时会向数据库发送多次SQL语句

  • resultMap:自定义某个javaBean的封装规则

  • resultMap的属性

    • id:唯一标识
    • type:自定义规则的java类型
  • resultMap包含的标签

    • result:指定其余键的封装规则
    • id:指定主键的封装规则, id定义主键底层会有优化
      • column:指定数据库的列
      • property:指定对应的javaBean属性

  • association是用于一对一和多对一;而collection是用于一对多的关系

  • association:定义关联对象的封装规则

  • collection:定义关联集合类型的属性的封装规则

    • property:当前封装的对象的属性
    • javaType:指定实体类中属性的类型
    • ofType:指定映射到List或集合中的pojo类型,泛型中的约束类型
    • select:表明当前属性是调用select指定的方法查出的结果
    • column: 指定将哪一列的值传给select
// 传递多列的值
column="{key1=column1, key2=column2}"
	key: select指定的查询的#{key}中的key
	colnmn: 列名
      • 流程:使用select指定的方法(传入column指定的列的参数值)查出对象, 并封装给property
    • fetchType:在全局配置中设置了延迟加载的情况下可以将联合属性修改为立即加载
      • lazy: 延迟, 默认
      • eager: 立即
嵌套查询的懒加载
        全局懒加载
修改 MyBatis 的核心配置文件

<settings>
<setting name="logImpl" value="LOG4J"/>

<setting name="aggressiveLazyLoading" value="false"/>

<setting name="lazyLoadingEnabled" value="true"/>
settings>
        局部懒加载

<resultMap id="employee" type="org.hong.pojo.Employee">
<id property="id" column="id">id>
<result property="name" column="name">result>

<association property="dept"
        column="did"
        javaType="org.hong.pojo.Dept"
        fetchType="lazy"
        select="org.hong.mapper.DeptMapper.get">association>
resultMap>

七、分页

1、使用Limit实现分页

#语法
SELECT * FROM table LIMIT stratIndex,pageSize

SELECT * FROM table LIMIT 5,10; // 检索记录行 6-15  

#如果只给定一个参数,它表示返回最大的记录行数目:   
SELECT * FROM table LIMIT 5; //检索前 5 个记录行  

#换句话说,LIMIT n 等价于 LIMIT 0,n。 

SELECT * FROM table LIMIT stratIndex,pageSize
#公式:Page = 1;  //第几页
#	  pageSize = 2; //每页显示几个
SELECT * FROM table LIMIT (Page-1)*pageSize), pageSize

步骤:

1、Mapper接口,参数为map

//选择全部用户实现分页
List<User> selectUser(Map<String,Integer> map);

2、修改Mapper文件


<select id="getUserByLimit" parameterType="map" resultType="com.kuang.pojo.User">
    select * from user limit #{startIndex},#{pageSize}
select>

3、在测试类中传入参数测试

  • 推断:起始位置 = (当前页面 - 1 ) * 每页显示个数
//分页查询 , 两个参数startIndex , pageSize
@Test
public void testSelectUser() {
   // 1.获取sqlSession对象
   SqlSession session = MybatisUtils.getSession();
   // 2.获取需要的mapper接口的代理对象
   UserMapper mapper = session.getMapper(UserMapper.class);
   int currentPage = 1;  //第几页
   int pageSize = 2;  //每页显示几个
   Map<String,Integer> map = new HashMap<String,Integer>();
   map.put("startIndex",(currentPage-1)*pageSize);
   map.put("pageSize",pageSize);
   // 3.调用对应的方法执行操作
   List<User> users = mapper.selectUser(map);
   for (User user: users){
       System.out.println(user);
  }
  // 4.关闭sqlSession
  session.close();
}

2、PageHelper分页插件实现

略,工作时候具体使用

八、注解开发

  • sql 类型主要分成 :

    • @select ()
    • @update ()
    • @Insert ()
    • @delete ()

**注意:**利用注解开发就不需要mapper.xml映射文件了 .

简单的sql语句使用注解,复杂的sql语句和结果集映射使用xml配置文件

小结:
所有的增删改操作都需要提交事务
接口所有的普通参数,尽量都写上@Param参数,尤其是多个参数时,必须写上!
有时候根据业务的需求,可以考虑使用map传递参数!
为了规范操作,在SQL的配置文件中,我们尽量将Parameter参数和resultType都写上!

1、我们在我们的接口中添加注解

//查询全部用户
@Select("select id,name,pwd password from user")
public List<User> getAllUser();

2、在mybatis的核心配置文件中注入



<mappers>
    <mapper class="com.kuang.dao.UserMapper"/>
mappers>

3、进行测试

@Test
public void testGetAllUser() {
   SqlSession session = MybatisUtils.getSession();
   //本质上利用了jvm的动态代理机制
   UserMapper mapper = session.getMapper(UserMapper.class);

   List<User> users = mapper.getAllUser();
   for (User user : users){
       System.out.println(user);
  }

   session.close();
}

4、利用Debug查看本质

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

5、本质:反射机制实现;底层:利用了JVM的动态代理机制

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

1、Mybatis详细的执行流程

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

2、注解增删改查

改造MybatisUtils工具类的.openSession(true);实现自动提交

public class MybatisUtils {

    private static SqlSessionFactory sqlSessionFactory;

    static {
        try {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //获取sqlSession连接
    public static SqlSession getSqlSession(){
        //openSession():创建一个非自动提交功能的SqlSession,需要手动提交
        //openSession(true):创建一个有自动提交功能的SqlSession
        return sqlSessionFactoryopenSession(true);
    }
}

【注意】确保实体类和数据库字段对应

**② **编写接口方法注解(UserMapper.java)

//方法存在多个参数,所有的参数前面必须加上 @Param("id")注解

@Select("select * from user where id=#{id}")
User getUserById(@Param("id") int id);

@Insert("insert into user(id,name,pwd) values(#{id},#{name},#{pwd})")
int addUser(User user);

@Update("update user set name=#{name},pwd=#{pwd} where id=#{id}")
int updateUser(User user);

@Delete("delete from user where id=#{uid}")
int deleteUser(@Param("uid") int id);

进行测试

    @Test
    public void getUserById(){
        // 1.获取SqlSession
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        // 2.获取需要的mapper接口的代理对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        // 3.调用对应的方法执行操作
        User userById = mapper.getUserById(2);
        System.out.println(userById);
        // 4.关闭sqlSession
        sqlSession.close();
    }
    
    @Test
    public void addUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        mapper.addUser(new User(6, "谭章竦", "101699"));
        sqlSession.close();
    }

    @Test
    public void updateUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        mapper.updateUser(new User(5,"罗丹江","112399"));
        sqlSession.close();
    }

    @Test
    public void deleteUser(){
        // 1.获取sqlSession对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        // 2.获取需要的mapper接口的代理对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        // 3.调用对应的方法执行操作
        mapper.deleteUser(6);
        // 4.关闭sqlSession
        sqlSession.close();
    }

【注意点:1、增删改一定记得对事务的处理 2、必须将接口注册绑定到我们的核心配置文件中】

关于@Param

@Param注解用于给方法参数起一个名字。以下是总结的使用原则:

  • 在方法只接受一个参数的情况下,可以不使用@Param。
  • 在方法接受多个参数的情况下,建议一定要使用@Param注解给参数命名。
  • 如果参数是 JavaBean , 则不能使用@Param。
  • 不使用@Param注解时,参数只能有一个,并且是Javabean。

#与$的区别

  • #{} 的作用主要是替换预编译语句(PrepareStatement)中的占位符? 【推荐使用】

    INSERT INTO user (name) VALUES (#{name});
    INSERT INTO user (name) VALUES (?);
    
  • ${} 的作用是直接进行字符串替换

    INSERT INTO user (name) VALUES ('${name}');
    INSERT INTO user (name) VALUES ('kuangshen');
    

使用注解和配置文件协同开发,才是MyBatis的最佳实践!

九、Lombok

1、常用注解

下面介绍一下常用的几个注解:

@Setter 注解在类或字段,注解在类时为所有字段生成setter方法,注解在字段上时只为该字段生成setter方法。
@Getter 使用方法同上,区别在于生成的是getter方法。
@ToString 注解在类,添加toString方法。
@EqualsAndHashCode 注解在类,生成hashCode和equals方法。
@NoArgsConstructor 注解在类,生成无参的构造方法。
@RequiredArgsConstructor 注解在类,为类中需要特殊处理的字段生成构造方法,比如final和被@NonNull注解的字段。
@AllArgsConstructor 注解在类,生成包含类中所有字段的构造方法。
@Data 注解在类,生成setter/getter、equals、canEqual、hashCode、toString方法,如为final属性,则不会为该属性生成setter方法。
@Slf4j 注解在类,生成log变量,严格意义来说是常量。private static final Logger log = LoggerFactory.getLogger(UserController.class);

2、使用

第一步:下载Lombok插件

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

第二步:导入Lombok依赖jar包

<dependencies>
    <dependency>
        <groupId>org.projectlombokgroupId>
        <artifactId>lombokartifactId>
        <version>1.18.10version>
    dependency>
dependencies>

第三步:在实体类上加注解即可!

@Data
@AllArgsConstructor
@NoArgsConstructor

3、Lombok的优缺点

优点:

能通过注解的形式自动生成构造器、getter/setter、equals、hashcode、toString等方法,提高了一定的开发效率
让代码变得简洁,不用过多的去关注相应的方法
属性做修改时,也简化了维护为这些属性所生成的getter/setter方法等

缺点:

不支持多种参数构造器的重载
虽然省去了手动创建getter/setter方法的麻烦,但大大降低了源代码的可读性和完整性,降低了阅读源代码的舒适度

十、动态SQL

什么是动态SQL:动态SQL指的是根据不同的查询条件 , 生成不同的Sql语句.

所谓的动态SQL,本质还是SQL语句,只是我们可以在SQL层面,去执行一个逻辑代码

官网描述:
动态 SQL 元素和 JSTL 或基于类似 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多元素需要花时间了解。MyBatis 3 大大精简了元素种类,现在只需学习原来一半的元素便可。MyBatis 采用功能强大的基于 OGNL 的表达式来淘汰其它大部分元素。


  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach

我们之前写的 SQL 语句都比较简单,如果有比较复杂的业务,我们需要写复杂的 SQL 语句,往往需要拼接,而拼接 SQL ,稍微不注意,由于引号,空格等缺失可能都会导致错误。

那么怎么去解决这个问题呢?这就要使用 mybatis 动态SQL,通过 if, choose, when, otherwise, trim, where, set, foreach等标签,可组合成非常灵活的SQL语句,从而在提高 SQL 语句的准确性的同时,也大大提高了开发人员的效率。

1、环境配置:

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

SQL
CREATE TABLE `blog` (
`id` varchar(50) NOT NULL COMMENT '博客id',
`title` varchar(100) NOT NULL COMMENT '博客标题',
`author` varchar(30) NOT NULL COMMENT '博客作者',
`create_time` datetime NOT NULL COMMENT '创建时间',
`views` int(30) NOT NULL COMMENT '浏览量'
) ENGINE=InnoDB DEFAULT CHARSET=utf8
IDutils工具类
@SuppressWarnings("all") //抑制警告
public class IDutils {

    public static String getId(){
        return UUID.randomUUID().toString().replaceAll("-", "");
    }

    @Test
    public void test(){
        System.out.println(IDutils.getId());
        System.out.println(IDutils.getId());
        System.out.println(IDutils.getId());
    }
}
实体类
@Data
public class Blog {

    private String id;
    private String title;
    private String author;
    private Date createTime;
    private int views;
}
编写Mapper接口及xml文件
public interface BlogMapper {
}





mybatis核心配置文件

DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

    
    <properties resource="db.properties"/>

    
    <settings>
        <setting name="logImpl" value="LOG4J"/>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    settings>

    
    <typeAliases>
        <package name="com.kuang.pojo"/>
    typeAliases>

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

    
    <mappers>
        <package name="com.kuang.dao"/>
        
    mappers>

configuration>
插入初始数据

编写接口

//新增一个博客
int addBlog(Blog blog);

sql配置文件

<mapper namespace="com.kuang.dao.BlogMapper">

    <insert id="addBlog" parameterType="blog">
        insert into blog (id,title,author,create_time,views)
        values (#{id},#{title},#{author},#{createTime},#{views});
    insert>
mapper>

初始化博客方法

@Test
public void addInitBlog(){
    SqlSession session = MybatisUtils.getSqlSession();
    BlogMapper mapper = session.getMapper(BlogMapper.class);

    Blog blog = new Blog();
    blog.setId(IDutils.getId());
    blog.setTitle("Mybatis如此简单");
    blog.setAuthor("狂神说");
    blog.setCreateTime(new Date());
    blog.setViews(9999);

    mapper.addBlog(blog);

    blog.setId(IDutils.getId());
    blog.setTitle("Java如此简单");
    mapper.addBlog(blog);

    blog.setId(IDutils.getId());
    blog.setTitle("Spring如此简单");
    mapper.addBlog(blog);

    blog.setId(IDutils.getId());
    blog.setTitle("微服务如此简单");
    mapper.addBlog(blog);

    session.close();
}

初始化数据完毕!

2、if 语句

需求:根据作者名字和博客名字来查询博客!如果作者名字为空,那么只根据博客名字查询,反之,则根据作者名来查询

1、编写接口类

//查询博客
List<Blog> queryBlogIF(Map map);

2、编写SQL语句


<select id="queryBlogIf" parameterType="map" resultType="blog">
  select * from blog where
   <if test="title != null">
      title = #{title}
   if>
   <if test="author != null">
      and author = #{author}
   if>
select>

3、测试

@Test
public void testQueryBlogIf(){
   SqlSession session = MybatisUtils.getSession();
   BlogMapper mapper = session.getMapper(BlogMapper.class);

   HashMap map = new HashMap();
   map.put("title","Mybatis如此简单");
   map.put("author","狂神说");
   List blogs = mapper.queryBlogIf(map);

   System.out.println(blogs);

   session.close();
}

这样写我们可以看到,如果 author 等于 null,那么查询语句为 select * from user where title=#{title},但是如果title为空呢?那么查询语句为 select * from user where and author=#{author},这是错误的 SQL 语句,如何解决呢?请看下面的 where 语句!

3、Where

修改上面的SQL语句;

<select id="queryBlogIF" parameterType="map" resultType="blog">
    select * from blog
    <where>
    <if test="title != null">
        title = #{title}
    if>
    <if test="author != null">
        and author = #{author}
    if>
    where>
select>

这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个‘where’。此外,如果标签返回的内容是以AND 或OR 开头的,则它会剔除掉。

4、Set

同理,上面的对于查询 SQL 语句包含 where 关键字,如果在进行更新操作的时候,含有 set 关键词,我们怎么处理呢?

1、编写接口方法

//更新博客
int updateBlog(Map map);

2、编写sql语句


<update id="updateBlog" parameterType="map">
    update blog
    <set>
        <if test="title != null">
            title = #{title},
        if>
        <if test="author != null">
            author = #{author}
        if>
    set>
    where id = #{id}
update>

3、测试

@Test
public void updateBlog(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

    HashMap<String, String> map = new HashMap<String, String>();
    map.put("title", "动态SQL");
    map.put("author", "秦疆");
    map.put("id", "51f7a3765c154dbaa0a55b35d38e66e2");
    mapper.updateBlog(map);

    sqlSession.close();
}

5、choose语句

有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句

1、编写接口方法

List<Blog> queryBlogChoose(Map map);

2、sql配置文件

<select id="queryBlogChoose" parameterType="map" resultType="blog">
    select * from blog
    <where>
        <choose>
            <when test="title != null">
                title = #{title}
            when>
            <when test="author != null">
                and author = #{author}
            when>
            <otherwise>
                and views = #{views}
            otherwise>
        choose>
    where>
select>

3、测试类

@Test
public void queryBlogChoose(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

    HashMap<String, Object> map = new HashMap<String, Object>();
    map.put("title","Java如此简单");
    map.put("author","狂神说");
    map.put("views",9999);
    List<Blog> blogs = mapper.queryBlogChoose(map);

    System.out.println(blogs);

    sqlSession.close();
}

6、SQL片段

有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。

提取SQL片段:


<sql id="if-title-author">
    <if test="title != null">
        title = #{title}
    if>
    <if test="author != null">
        and author = #{author}
    if>
sql>

引用SQL片段:

<select id="queryBlogIF" parameterType="map" resultType="blog">
    select * from blog
    <where>
        
        <include refid="if-title-author">include>
        
    where>
select>

注意:

①、最好基于 单表来定义 sql 片段,提高片段的可重用性

②、在 sql 片段中不要包括 where

7、Foreach

将数据库中前三个数据的id修改为1,2,3;

需求:我们需要查询 blog 表中 id 分别为1,2,3的博客信息

1、编写接口

//查询第1-2-3号记录的博客
List<Blog> queryBlogForeach(Map map);

2、编写SQL语句



<select id="queryBlogForeach" parameterType="map" resultType="blog">
    select * from blog
    <where>
        <foreach collection="ids" item="id" open="and (" separator="or" close=")" >
            id=#{id}
        foreach>
    where>
select>

3、测试

@Test
public void queryBlogForeach(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

    HashMap map = new HashMap();
    List<Integer> ids = new ArrayList<Integer>();
    ids.add(1);
    ids.add(2);
    ids.add(3);

    map.put("ids",ids);
    List<Blog> blogs = mapper.queryBlogForeach(map);
    for (Blog blog : blogs) {
        System.out.println(blog);
    }

    sqlSession.close();
}

8、bind

问题:
我们进行模糊查询时,每次给属性赋值都加上了 %%,显示的加上通配符,这样并不是很好,应该让 MyBatis 为我们加上通配符,想要完成这个功能需要使用 bind 元素。

元素允许你在 OGNL 表达式以外创建一个变量,并将其绑定到当前的上下文。通常用来拼接模糊查询
Mapper 接口和测试不变,对 where 演示的方法映射进行改造!!!

方法映射

<select id="getByBlog" resultType="org.hong.pojo.Blog">
    select * from blog
    <where>
        <if test="title != null and title.trim() != ''">
            <!-- bind -->
            <bind name="newTitle" value="'%' + title + '%'"/>
            and title like #{newTitle}
        </if>
        <if test="author != null and author.trim() != ''">
            <bind name="newAuthor" value="'%' + author + '%'"/>
            and author like #{newAuthor}
        </if>
    </where>
</select>

测试

@Test
public void testBind(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
	Blog blog = new Blog();
	// 直接赋值了一个y, 没有使用通配符
	blog.setTitle("y");
	List<Blog> list = mapper.getByBlog(blog);
	list.forEach(System.out :: println);

	sqlSession.commit();
	sqlSession.close();
}

小结:其实动态 sql 语句的编写往往就是一个拼接的问题,为了保证拼接准确,我们最好首先要写原生的 sql 语句出来,然后在通过 mybatis 动态sql 对照着改,防止出错。多在实践中使用才是熟练掌握它的技巧。

十一、缓存

1、简介

1、什么是缓存 [ Cache ]?

  • 存在内存中的临时数据。
  • 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。

2、为什么使用缓存?

  • 减少和数据库的交互次数,减少系统开销,提高系统效率。

3、什么样的数据能使用缓存?

  • 经常查询并且不经常改变的数据。

2、Mybatis缓存

  • MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。

  • MyBatis系统中默认定义了两级缓存:一级缓存二级缓存

    • 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
    • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
    • 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

一级缓存

一级缓存也叫本地缓存:

  • 与数据库同一次会话期间查询到的数据会放在本地缓存中。
  • 以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;

测试:

1、在mybatis中加入日志,方便测试结果

2、编写接口方法

//根据id查询用户
User queryUserById(@Param("id") int id);

3、接口对应的Mapper文件

<select id="queryUserById" resultType="user">
  select * from user where id = #{id}
select>

4、测试

@Test
public void testQueryUserById(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    User user1 = mapper.queryUserById(1);
    System.out.println(user1);

    System.out.println("==========================");

    User user2 = mapper.queryUserById(1);
    System.out.println(user2);

    System.out.println(user1==user2);
    sqlSession.close();
}

5、结果分析

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

一级缓存失效的四种情况

一级缓存是SqlSession级别的缓存,是一直开启的,我们关闭不了它;

一级缓存失效情况:没有使用到当前的一级缓存,效果就是,还需要再向数据库中发起一次查询请求!

  • 不同的SqlSession对应不同的一级缓存

  • 同一个SqlSession但是查询条件不同

  • 一个SqlSession两次查询期间执行了任何一次增删改操作

  • 同一个SqlSession两次查询期间手动清空了缓存

1、sqlSession不同

@Test
public void testQueryUserById(){
   SqlSession session = MybatisUtils.getSession();
   SqlSession session2 = MybatisUtils.getSession();
   UserMapper mapper = session.getMapper(UserMapper.class);
   UserMapper mapper2 = session2.getMapper(UserMapper.class);

   User user = mapper.queryUserById(1);
   System.out.println(user);
   User user2 = mapper2.queryUserById(1);
   System.out.println(user2);
   System.out.println(user==user2);

   session.close();
   session2.close();
}

观察结果:发现发送了两条SQL语句!

结论:每个sqlSession中的缓存相互独立

2、sqlSession相同,查询条件不同

@Test
public void testQueryUserById(){
   SqlSession session = MybatisUtils.getSession();
   UserMapper mapper = session.getMapper(UserMapper.class);
   UserMapper mapper2 = session.getMapper(UserMapper.class);

   User user = mapper.queryUserById(1);
   System.out.println(user);
   User user2 = mapper2.queryUserById(2);
   System.out.println(user2);
   System.out.println(user==user2);

   session.close();
}

观察结果:发现发送了两条SQL语句!很正常的理解

结论:当前缓存中,不存在这个数据

3、sqlSession相同,两次查询之间执行了增删改操作!

增加方法

//修改用户
int updateUser(Map map);

编写SQL

<update id="updateUser" parameterType="map">
  update user set name = #{name} where id = #{id}
update>

测试

@Test
public void testQueryUserById(){
   SqlSession session = MybatisUtils.getSession();
   UserMapper mapper = session.getMapper(UserMapper.class);

   User user = mapper.queryUserById(1);
   System.out.println(user);

   HashMap map = new HashMap();
   map.put("name","kuangshen");
   map.put("id",4);
   mapper.updateUser(map);

   User user2 = mapper.queryUserById(1);
   System.out.println(user2);

   System.out.println(user==user2);

   session.close();
}

观察结果:查询在中间执行了增删改操作后,重新执行了

结论:因为增删改操作可能会对当前数据产生影响

4、sqlSession相同,手动清除一级缓存

@Test
public void testQueryUserById(){
   SqlSession session = MybatisUtils.getSession();
   UserMapper mapper = session.getMapper(UserMapper.class);

   User user = mapper.queryUserById(1);
   System.out.println(user);

   session.clearCache();//手动清除缓存

   User user2 = mapper.queryUserById(1);
   System.out.println(user2);

   System.out.println(user==user2);

   session.close();
}

一级缓存就是一个map

二级缓存

  • 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
  • 基于namespace级别的缓存,一个名称空间,对应一个二级缓存;
  • 工作机制
    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
    • 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
    • 新的会话查询信息,就可以从二级缓存中获取内容;
    • 不同的mapper查出的数据会放在自己对应的缓存(map)中;

使用步骤

1、开启全局缓存 【mybatis-config.xml】

<setting name="cacheEnabled" value="true"/>

2、去每个mapper.xml中配置使用二级缓存;【xxxMapper.xml】

<cache/>

也可以自定义
<cache
 eviction="FIFO"
 flushInterval="60000"
 size="512"
 readOnly="true"/>
这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。

3、代码测试

接口

//根据id查询用户
User queryUserById(@Param("id") int id);

编写SQL

<mapper namespace="com.kuang.dao.UserMapper">
    
    <cache/>
    <select id="queryUserById" resultType="user">
        select * from user where id=#{id}
    select>

mapper>
  • 问题:我们需要将实体类序列化!否则报错!

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User implements Serializable {
        private int id;
        private String name;
        private String pwd;
    }
    
  • 测试代码

@Test
public void testQueryUserById(){
   SqlSession session = MybatisUtils.getSession();
   SqlSession session2 = MybatisUtils.getSession();

   UserMapper mapper = session.getMapper(UserMapper.class);
   UserMapper mapper2 = session2.getMapper(UserMapper.class);

   User user = mapper.queryUserById(1);
   System.out.println(user);
   session.close();

   User user2 = mapper2.queryUserById(1);
   System.out.println(user2);
   System.out.println(user==user2);

   session2.close();
}

结果:

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

二级缓存结论:

  • 只要开启了二级缓存,我们在同一个Mapper中的查询,可以在二级缓存中拿到数据
  • 查出的数据都会被默认先放在一级缓存中
  • 只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中
  • 如果两个mapper的namespace相同,即使是两个mapper,那么这两个mapper中执行sql查询到的数据也将存在相同的二级缓存区域中

缓存顺序:1.先看二级缓存中有没有

​ 2.再看一级缓存中有没有

​ 3.都没有,查询数据库

MyBatis零基础入门笔记_第2张图片

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

EhCache

MyBatis零基础入门笔记_第3张图片

第三方缓存实现–EhCache: 查看百度百科

Ehcache是一种广泛使用的java分布式缓存,用于通用缓存;

要在应用程序中使用Ehcache,需要引入依赖的jar包


<dependency>
   <groupId>org.mybatis.cachesgroupId>
   <artifactId>mybatis-ehcacheartifactId>
   <version>1.1.0version>
dependency>

在mapper.xml中使用对应的缓存即可

<mapper namespace = “org.acme.FooMapper” >
   <cache type = “org.mybatis.caches.ehcache.EhcacheCache” />
mapper>

编写ehcache.xml文件,如果在加载时未找到/ehcache.xml资源或出现问题,则将使用默认配置。


<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
        updateCheck="false">
   
   <diskStore path="./tmpdir/Tmp_EhCache"/>
   
   <defaultCache
           eternal="false"
           maxElementsInMemory="10000"
           overflowToDisk="false"
           diskPersistent="false"
           timeToIdleSeconds="1800"
           timeToLiveSeconds="259200"
           memoryStoreEvictionPolicy="LRU"/>

   <cache
           name="cloud_user"
           eternal="false"
           maxElementsInMemory="5000"
           overflowToDisk="false"
           diskPersistent="false"
           timeToIdleSeconds="1800"
           timeToLiveSeconds="1800"
           memoryStoreEvictionPolicy="LRU"/>
   
   

ehcache>

补充:

逆向工程 ( MBG )

逆向工程 ( MBG )MyBatis Generator 简称 MBG ,是一个专门为 MyBatis 框架使用者定制的代码生成器,可以快速的根据表生成对应的映射文件,接口,以及 bean 类。支持基本的增删改查,以及 QBC 风格的条件查询。但是表连接、存储过程等这些复杂 sql 的定义需要我们手工编写

Maven:

<dependency>
    <groupId>org.mybatis.generatorgroupId>
    <artifactId>mybatis-generator-coreartifactId>
    <version>1.4.0version>
dependency>generationConfig.xml

generationConfig.xml

<?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
            MyBatis3: 豪华版的CRUD, 支持QBC风格
     -->
    <context id="mybatisGenerator" targetRuntime="MyBatis3">
        <commentGenerator>
            <!-- 是否去除自动生成的注释 true:是 : false:-->
            <property name="suppressAllComments" value="true" />
        </commentGenerator>
        <!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/mybatis?userSSL=true&useUnicode=true&characterEncoding=UTF-8"
                        userId="root"
                        password="1234">
        </jdbcConnection>

        <!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和
            NUMERIC 类型解析为java.math.BigDecimal -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>

        <!-- targetProject:生成POJO类的位置 -->
        <javaModelGenerator targetPackage="org.hong.pojo"
                            targetProject=".\src\main\java">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
            <!-- 从数据库返回的值被清理前后的空格 -->
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <!-- targetProject:mapper映射文件生成的位置 -->
        <sqlMapGenerator targetPackage="org.hong.mapper"
                         targetProject=".\src\main\resources">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
        </sqlMapGenerator>
        <!-- targetPackage:mapper接口生成的位置 -->
        <javaClientGenerator type="XMLMAPPER"
                             targetPackage="org.hong.mapper"
                             targetProject=".\src\main\java">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
        </javaClientGenerator>
        <!-- 指定数据库表 -->
        <table tableName="blog" domainObjectName="Blog"></table>
    </context>
</generatorConfiguration>

Test

public class MyBatisTest {
    // 运行这个单元测试, 自动生成
    @Test
    public void testMbg() throws IOException, XMLParserException, InvalidConfigurationException, SQLException, InterruptedException {
        List<String> warnings = new ArrayList<String>();
        boolean overwrite = true;
        File configFile = new File("IDEA使用绝对路径/generationConfig.xml");
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(configFile);
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
        myBatisGenerator.generate(null);
    }
    
    @Test
    public void testMyBatis3(){
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        try {
            BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
            // 1.查询所有
            List<Blog> blogs = mapper.selectByExample(null);
            // 2.查询博客标题中带字母s的, 作者名字中带大字的
            // blogExample对象封装查询条件
            BlogExample blogExample = new BlogExample();
            // 3.创建Criteria, 这个Criteria就是拼装查询条件
            BlogExample.Criteria criteria = blogExample.createCriteria();
            // andXXXYYY表示添加and条件, XXX代表字段名, YYY代表条件(like,is...)
            criteria.andTitleLike("%s%");
            // 4.添加另外一组添加, 再次创建创建Criteria
            BlogExample.Criteria criteria2 = blogExample.createCriteria();
            // 设置Criteria的条件
            criteria2.andAuthorLike("%大%");
            // 5.调用or()表示这组添加与其他Criteria的关系
            blogExample.or(criteria2);
            blogs = mapper.selectByExample(blogExample);
            for (Blog blog : blogs) {
                System.out.println(blog);
            }
        } finally {
            sqlSession.close();
        }
    }
}

运行 Test 会自动生成 mapper 和 pojo,注意:移动文件后记得改配置文件的 parameterType、type 等属性。
MERIC 类型解析为java.math.BigDecimal -->


    
    
        
        
        
        
    
    
    
        
        
    
    
    
        
        
    
    
    
```

Test

public class MyBatisTest {
    // 运行这个单元测试, 自动生成
    @Test
    public void testMbg() throws IOException, XMLParserException, InvalidConfigurationException, SQLException, InterruptedException {
        List<String> warnings = new ArrayList<String>();
        boolean overwrite = true;
        File configFile = new File("IDEA使用绝对路径/generationConfig.xml");
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(configFile);
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
        myBatisGenerator.generate(null);
    }
    
    @Test
    public void testMyBatis3(){
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        try {
            BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
            // 1.查询所有
            List<Blog> blogs = mapper.selectByExample(null);
            // 2.查询博客标题中带字母s的, 作者名字中带大字的
            // blogExample对象封装查询条件
            BlogExample blogExample = new BlogExample();
            // 3.创建Criteria, 这个Criteria就是拼装查询条件
            BlogExample.Criteria criteria = blogExample.createCriteria();
            // andXXXYYY表示添加and条件, XXX代表字段名, YYY代表条件(like,is...)
            criteria.andTitleLike("%s%");
            // 4.添加另外一组添加, 再次创建创建Criteria
            BlogExample.Criteria criteria2 = blogExample.createCriteria();
            // 设置Criteria的条件
            criteria2.andAuthorLike("%大%");
            // 5.调用or()表示这组添加与其他Criteria的关系
            blogExample.or(criteria2);
            blogs = mapper.selectByExample(blogExample);
            for (Blog blog : blogs) {
                System.out.println(blog);
            }
        } finally {
            sqlSession.close();
        }
    }
}

运行 Test 会自动生成 mapper 和 pojo,注意:移动文件后记得改配置文件的 parameterType、type 等属性。

你可能感兴趣的:(SSM框架,mybatis,maven,java,intellij-idea,开发语言)