Mybatis-狂神笔记

Typora笔记下载地址:
链接:https://pan.baidu.com/s/1zw_aOYjlljlnEMdRqGYj8g
提取码:zqw7
1.简介

官方网站:动态 SQL_MyBatis中文网

1. 环境搭建

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

2. 什么是Mybatis

  • MyBatis 是一款优秀的持久层框架

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

  • MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 实体类 【Plain Old Java Objects,普通的 Java对象】映射成数据库中的记录。

3. 持久化

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

  • JDBC就是一种持久化机制。文件IO也是一种持久化机制。

4. 持久层

  • 完成持久化工作的代码块 . ----> dao层 【DAO (Data Access Object) 数据访问对象】

  • 大多数情况下特别是企业级应用,数据持久化往往也就意味着将内存中的数据保存到磁盘上加以固化,而持久化的实现过程则大多通过各种关系数据库来完成。

    1. 为什么需要Mybatis
    • Mybatis就是帮助程序猿将数据存入数据库中 , 和从数据库中取数据 .

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

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

5.程序搭建

  1. 导入对应的依赖

     <!--    mybatis-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</version>
        </dependency>
    
        <!--    junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
    
    
    1. 编写MyBatis核心配置文件

      
      <?xml version="1.0" encoding="UTF-8" ?>
      <!DOCTYPE configuration
              PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
              "http://mybatis.org/dtd/mybatis-3-config.dtd">
      <configuration>
          <environments default="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=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=GMT%2B8"/>
                      <property name="username" value="root"/>
                      <property name="password" value="123456"/>
                  </dataSource>
              </environment>
          </environments>
      <mappers>
          <mapper resource="com/csf/dao/UserImpl.xml"></mapper>
      </mappers>
      </configuration>
      
    2. 编写MyBatis工具类

    3. package com.csf.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;
      
      /**
       * @ClassName MybatisUtils
       * @Author 陈世枫
       * @Date 2022/2/24 18:28
       * @Version 1.0
       **/
      public class MybatisUtils  {
          static private SqlSessionFactory sqlSessionFactory=null;
          static {
              try {
                  String resource = "mybatis-config.xml";
                  InputStream inputStream = Resources.getResourceAsStream(resource);
                  sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
              } catch (IOException e) {
                  e.printStackTrace();
              }
      
          }
          public static SqlSession getSqlSession(){
              return sqlSessionFactory.openSession();
          }
      
      }
      
      
      1. 创建实体类

      2. 创建接口

      3. 编写接口的配置文件

        <?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">
        <!--跟的是具体接口-->
        <mapper namespace="com.csf.dao.UserDao">
        <!--    id是接口中的方法名 resultType是返回集合中的泛型类-->
            <select id="getUserList" resultType="com.csf.pojo.User">
        --         跟着sql语句
                    select * from user
          </select>
        </mapper>
        

6.可能遇到的异常

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

  • 这中异常是未在mybatis-config.xml中配置mappers

    
    <mappers>
        <!--根据具体的接口-->
        <mapper resource="com/csf/dao/UserImpl.xml"></mapper>
    </mappers>
    

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

  • 默认除了resources包下的xml、properties会加载到target里,要把这个过滤器关闭,在pom.xml中关闭过滤器

        <build>
            <!--在进行模块化开发打jar包时,maven会将非java文件过滤掉,
            xml,properties配置文件等,但是这些文件又是必需的,
            使用此配置可以在打包时将不会过滤这些必需的配置文件。
            -->
            <resources>
                <resource>
                    <directory>src/main/java</directory>
                    <includes>
                        <include>**/*.properties</include>
                        <include>**/*.xml</include>
                    </includes>
                    <filtering>false</filtering>
                </resource>
                <resource>
                    <directory>src/main/resources</directory>
                    <includes>
                        <include>**/*.properties</include>
                        <include>**/*.xml</include>
                    </includes>
                    <filtering>false</filtering>
                </resource>
            </resources>
            <!--配置打包时不过滤非java文件结束 -->
        </build>
    
    

2、CRUD

1、在接口方法中定义各方法
  //查询单个user
   User getUser(int id);
   //增
   int addUser(User user);
   //删
   int delUser(int id);
   //改
   int updateUser(User user);

1、【增】编写mapper中的语句

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

2、【改】

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

    </update>

3、【删】

    <delete id="delUser" parameterType="int">
        delete from user where id = #{id}
    </delete>

4、【查】

    <select id="getUser" parameterType="int" resultType="com.csf.pojo.User">
        select * from user where id = #{id}
    </select>

注意点:

​ CRU要提交事务

		sqlSession.commit();

8、万能Map

接口中方法的定义:

 List<User> getUserLike(Map<String,Object> map);

xml配置:

   <select id="getUserLike" parameterType="map" resultType="com.csf.pojo.User">
        select * from user where name like "%"#{userName}"%"
    </select>

  <insert id="addUserLike" parameterType="map">
        insert into user values (#{userid},#{userName},#{userpwd})
    </insert>

测试:

    //模糊查询
    @Test
    public void test6(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserDao mapper = sqlSession.getMapper(UserDao.class);
        Map<String,Object> map=new HashMap<String, Object>();
        map.put("userName","陈");
        List<User> userLike = mapper.getUserLike(map);
        for (User user : userLike) {
            System.out.println(user);
        }
    }
//Map插入
    @Test
    public void test7(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserDao mapper = sqlSession.getMapper(UserDao.class);
        Map<String,Object> map=new HashMap<String, Object>();
        map.put("userName","陈晨");
        map.put("userid",5);
        map.put("userpwd","666666");
       mapper.addUserLike(map);
       sqlSession.commit();
    }

Map传递参数,直接在sql中取出key即可【parameterType=“map”】

对象传递参数,直接在sql中取对象的属性即可!【parameterType=“Object”】

只有一个基本类型参数的情况下,可以直接在sql中取到!

多个参数用Map,或者注解!

3、配置解析

1、核心配置文件

MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:

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

2、环境配置

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

<environments default="development">
  <environment id="development">
    <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 id="text">
    <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>

注意一些关键点:

  • 默认使用的环境 ID(比如:default=“development”)。
  • 每个 environment 元素定义的环境 ID(比如:id=“development”)。
  • 事务管理器的配置(比如:type=“JDBC”)。默认JDBC,还有MANAGED
  • 数据源的配置(比如:type=“POOLED”)。
  • mybatis默认事务管理器JDBC,数据池POOLED

默认环境和环境 ID 顾名思义。 环境可以随意命名,但务必保证默认的环境 ID 要匹配其中一个环境 ID。

3、属性

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

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

4、映射器

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

方式一、【推荐】

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

方式二、使用class文件绑定注册

<mappers>
        <mapper class="com.csf.dao.UserDao"></mapper>
    </mappers>

注意点:

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

方式三、使用扫描包

  <package name="com.csf.dao"/>

注意点:

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

5、typeAliases优化


<!--配置别名,注意顺序-->
<typeAliases>
    <typeAlias type="com.kuang.pojo.User" alias="User"/>
</typeAliases>

每一个在包 com.kuang.pojo 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。

若有注解,则别名为其注解值。见下面的例子:


@Alias("user")
public class User {
    ...
}

6、作用域(Scope)和生命周期

理解我们目前已经讨论过的不同作用域和生命周期类是至关重要的,因为错误的使用会导致非常严重的并发问题。

我们可以先画一个流程图,分析一下Mybatis的执行过程!

作用域理解

SqlSessionFactoryBuilder 的作用在于创建 SqlSessionFactory,创建成功后,SqlSessionFactoryBuilder 就失去了作用,所以它只能存在于创建 SqlSessionFactory 的方法中,而不要让其长期存在。因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。

SqlSessionFactory 可以被认为是一个数据库连接池,它的作用是创建 SqlSession 接口对象。因为 MyBatis 的本质就是 Java 对数据库的操作,所以 SqlSessionFactory 的生命周期存在于整个 MyBatis 的应用之中,所以一旦创建了 SqlSessionFactory,就要长期保存它,直至不再使用 MyBatis 应用,所以可以认为 SqlSessionFactory 的生命周期就等同于 MyBatis 的应用周期。

由于 SqlSessionFactory 是一个对数据库的连接池,所以它占据着数据库的连接资源。如果创建多个 SqlSessionFactory,那么就存在多个数据库连接池,这样不利于对数据库资源的控制,也会导致数据库连接资源被消耗光,出现系统宕机等情况,所以尽量避免发生这样的情况。

因此在一般的应用中我们往往希望 SqlSessionFactory 作为一个单例,让它在应用中被共享。所以说 SqlSessionFactory 的最佳作用域是应用作用域。

如果说 SqlSessionFactory 相当于数据库连接池,那么 SqlSession 就相当于一个数据库连接(Connection 对象),你可以在一个事务里面执行多条 SQL,然后通过它的 commit、rollback 等方法,提交或者回滚事务。所以它应该存活在一个业务请求中,处理完整个请求后,应该关闭这条连接,让它归还给 SqlSessionFactory,否则数据库资源就很快被耗费精光,系统就会瘫痪,所以用 try…catch…finally… 语句来保证其正确关闭。

所以 SqlSession 的最佳的作用域是请求或方法作用域。

学会了Crud,和基本的配置及原理,后面就可以学习些业务开发

7、ResultMap

mysql中字段与属性不一致问题

  • pojo中User类的定义

    
    public class User {
     
        private int id;  //id
        private String name;   //姓名
        private String password;   //密码和数据库不一样!
        
        //构造
        //set/get
        //toString()
    }
    
  • 接口
    User selectUserById(int id);
    4、mapper映射文件

<select id="selectUserById" resultType="user">
    select * from user where id = #{id}
</select>
  • 测试
@Test
public void testSelectUserById() {
    SqlSession session = MybatisUtils.getSession();  //获取SqlSession连接
    UserMapper mapper = session.getMapper(UserMapper.class);
    User user = mapper.selectUserById(1);
    System.out.println(user);
    session.close();
}
  • 结果

    User{id=1, name='陈世枫', password='null'}
    

分析:

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

解决方案:

一、使用结果集映射->ResultMap

  <resultMap id="UserMap" type="com.csf.pojo.User">
        <id column="pwd" property="password"></id>
    </resultMap>
   <select id="getUser" resultMap="UserMap">
       select * from user where id =#{id}
   </select>

自动映射

  • resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来。

  • 实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份 resultMap 能够代替实现同等功能的长达数千行的代码。

  • ResultMap 的设计思想是,对于简单的语句根本不需要配置显式的结果映射(属性和字段相同就不需要配置了),而对于复杂一点的语句只需要描述它们的关系就行了。

4、分页

思考:为什么需要分页?

在学习mybatis等持久层框架的时候,会经常对数据进行增删改查操作,使用最多的是对数据库进行查询操作,如果查询大量数据的时候,我们往往使用分页进行查询,也就是每次处理小部分数据,这样对数据库压力就在可控范围内。

使用limit分页

#语法
SELECT * FROM table LIMIT stratIndex,pageSize
 
SELECT * FROM table LIMIT 5,10; // 检索记录行 6-15  
 
#为了检索从某一个偏移量到记录集的结束所有的记录行,可以指定第二个参数为 -1:   
SELECT * FROM table LIMIT 95,-1; // 检索记录行 96-last.  
 
#如果只给定一个参数,它表示返回最大的记录行数目:   
SELECT * FROM table LIMIT 5; //检索前 5 个记录行  
 
#换句话说,LIMIT n 等价于 LIMIT 0,n。 

步骤:

1、修改Mapper文件

select * from user limit #{startIndex},#{pageSize} 2、Mapper接口,参数为map
//选择全部用户实现分页
List<User> selectUser(Map<String,Integer> map);

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

推断:起始位置 = (当前页面 - 1 ) * 页面大小

//分页查询 , 两个参数startIndex , pageSize
@Test
public void testSelectUser() {
    SqlSession session = MybatisUtils.getSession();
    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);
 
    List<User> users = mapper.selectUser(map);
 
    for (User user: users){
        System.out.println(user);
    }
 
    session.close();
}

5、日志

6、面向接口编程

大家之前都学过面向对象编程,也学习过接口,但在真正的开发中,很多时候我们会选择面向接口编程

  • 根本原因 : 解耦 , 可拓展 , 提高复用 , 分层开发中 , 上层不用管具体的实现 , 大家都遵守共同的标准 , 使得开发变得容易 , 规范性更好

  • 在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了;

  • 而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是指按照这种思想来编程。

关于接口的理解

  • 接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离。

  • 接口的本身反映了系统设计人员对系统的抽象理解。

接口应有两类:

  • 第一类是对一个个体的抽象,它可对应为一个抽象体(abstract class);

  • 第二类是对一个个体某一方面的抽象,即形成一个抽象面(interface);

  • 一个体有可能有多个抽象面。抽象体与抽象面是有区别的。

三个面向区别

  • 面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性及方法 .

  • 面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现 .

  • 接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题.更多的体现就是对系统整体的架构

7、注解开发

利用注解开发

mybatis最初配置信息是基于 XML ,映射语句(SQL)也是定义在 XML 中的。而到MyBatis 3提供了新的基于注解的配置。不幸的是,Java 注解的的表达力和灵活性十分有限。最强大的 MyBatis 映射并不能用注解来构建

sql 类型主要分成 :

  • @select ()

  • @update ()

  • @Insert ()

  • @delete ()

1.注解在接口上实现

 @Select("select * from user")
   List<User> getUserList();

2.在核心配置文件(mybatis-config.xml)中绑定接口

<mappers>
    <mapper class="com.csf.dao.UserDao"></mapper>
</mappers>

3.测试

本质:反射

底层:动态代理

注解开发-CRUD

  • 有多个变量时在前面添加@Param(" xxx"),并且sql语句中获取到的值以param中为主

1.Mapper接口的定义

   //增
   @Insert("insert into user (id,name,pwd) values (#{id},#{name},#{pwd})")
   int addUser(@Param("id") int id,@Param("name")  String name,@Param("pwd")  String pwd);

   //删
   @Delete("delete from user where id=#{id}")
   int delUser(int id);
   //改
   @Update("update user set name =#{name} where id=#{id} ")
   int updateUser(@Param("id")int id,@Param("name")String name);

2.测试

 //删除
    @Test
    public void test3(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserDao mapper = sqlSession.getMapper(UserDao.class);
        int i = mapper.delUser(4);
        System.out.println(i);
        sqlSession.commit();
    }
    //添加
    @Test
    public void test4(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserDao mapper = sqlSession.getMapper(UserDao.class);
        int i = mapper.addUser(6,"胡道森","55555");
        System.out.println(i);
        sqlSession.commit();
    }
    //修改
    @Test
    public void test5(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserDao mapper = sqlSession.getMapper(UserDao.class);
        int i = mapper.updateUser(6,"张作霖");
        System.out.println(i);
        sqlSession.commit();
    }

8、多对一处理

  • 多个学生对应一个老师
测试环境搭建

1、导入lombok

2、建立实体类

3、配置mybatis-config.xml

4、建立Mapper文件

5、在mybatis-config.xml配置Mapper文件

6、测试是否成功

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=utf8INSERT 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');
按照嵌套处理
<select id="getStudentList" resultMap="StudentTeacher">
    select * from student
</select>
    <resultMap id="StudentTeacher" type="com.csf.pojo.Student">
        <result property="name" column="name"/>
        <result property="id" column="id"/>
        <!-- 复杂的属性需要特殊处理    对象:association 集合:collection   -->
        <association property="teacher" column="tid" 			javaType="com.csf.pojo.Teacher" select="getTeacher"/>
    </resultMap>
    <select id="getTeacher" resultType="com.csf.pojo.Teacher">
        select * from teacher where id = #{id}
    </select>

按结果处理
<!-- 按照结果嵌套处理   方式二-->
<select id="getStudentList2" resultMap="StudentTeacher2 ">
    select s.id sid,s.name sname,t.name tname,t.id ttid from student s,teacher t where s.tid=t.id
</select>
    <resultMap id="StudentTeacher2" type="com.csf.pojo.Student">
        <result property="name" column="sname"/>
        <result property="id" column="sid"/>
        <association property="teacher" javaType="com.csf.pojo.Teacher">
            <result property="name" column="tname"/>
            <result property="id" column="ttid"/>
        </association>
    </resultMap>

Mysql查询方式:

  • 子查询(嵌套查询)
  • 联表查询

9、一对多处理

环境搭建
  • 如上
按照结果处理

其中TeacherMapper:

    List<Teacher> getTeacher(int id);

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

<!--    按结果查询-->
    <select id="getTeacher" resultMap="TeacherToStudent">
        select t.id tid,t.name tname,s.id sid,s.name sname from teacher t,student s where t.id=s.tid and tid=#{id}
    </select>
    <resultMap id="TeacherToStudent" type="com.csf.pojo.Teacher">
        <result property="id" column="tid"/>
        <result property="name" column="tname"/>
        <!--因为students是集合用collection,泛型是Student所以ofType为Student-->
        <collection property="students" ofType="com.csf.pojo.Student">
            <result property="id" column="sid"/>
            <result property="name" column="sname"/>
            <result property="tid" column="tid"/>
        </collection>
    </resultMap>
<!--    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)])-->
按嵌套查询
 <select id="getTeacher2" resultMap="StudentByTeacherId">
        select * from teacher where id=#{id}
    </select>
    <resultMap id="StudentByTeacherId" type="com.csf.pojo.Teacher">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <collection property="students"  javaType="ArrayList" column="id" select="GetStudent"/>
    </resultMap>
    <select id="GetStudent" resultType="com.csf.pojo.Student">
        <!--此处id由collection中的column标签的id传过来的-->
        select * from student where tid=#{id}
    </select>
总结
  • 关联-association【多对一】

  • 集合-collection【一对多】

  • javaType &ofType

    ​ 1.javaType:用来指定实体类的中属性的类型

    ​ 2.ofType:用来指定映射到List或集合中的pojo类型,泛型中的约束类型

10、动态SQL标签

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

环境搭建

1、实体类Blog创建

2、配置对应Mapper文件

3、mybatis-config.xml文件配置

4、Blog表格创建

-- auto-generated definition
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          not null comment '浏览量'
)
    charset = utf8;


注意:表格向对象转化时开启

<setting name="mapUnderscoreToCamelCase" value="true"/>
下划线驼峰自动转换

5、添加数据

SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        Blog blog = new Blog();
        blog.setId(IDutils.getID());
        blog.setTitle("Mybatis");
        blog.setAuthor("狂神说");
        blog.setCreateTime(new Date());
        blog.setViews(9999);

        int i = mapper.addBlog(blog);
        if (i>0){
            System.out.println("YES");
        }

        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);
        sqlSession.commit();
        sqlSession.close();

IF

接口:

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

编写SQL语句

<!--     查询IF-->
     <select id="queryBlogIF" parameterType="map" resultType="com.csf.pojo.Blog">
          select * from blog
          <where>
               <if test="title!=null">
                    and title=#{title}
               </if>
               <if test="author!=null">
                    and author=#{author}
               </if>
          </where>
     </select>

结果:select * from blog WHERE title=? and author=?

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

即如果条件为一个自动去除and,条件为多个时自动去除首个的and

choose、when、otherwise

接口层:

//    choose
List<Blog> queryBlogChoose(Map map);

编写SQL语句:

<!--     choose-->
     <select id="queryBlogChoose" parameterType="map" resultType="com.csf.pojo.Blog">
          select * from blog
          <where>
               <choose>
                    <when test="author!=null">
                         and author=#{author}
                    </when>
                    <when test="title!=null">
                        and title=#{title}
                    </when>

                    <otherwise>
                         views=#{views}
                    </otherwise>
               </choose>
          </where>
     </select>

注意点:

  • choose当中匹配到了一个就推出了—只会匹配到一个
  • 当传入的map为空时,sql语句:select * from blog WHERE views=?

where、set

接口层:

//update
    int queryBlogUpdate(Map map);

编写SQL语句:

<!--     Update-->
     <update id="queryBlogUpdate" parameterType="map">
          update blog
          <set>
               <if test="author!=null">
                    author=#{author},
               </if>
               <if test="title!=null">
                    title=#{title},
               </if>
          </set>
          where views=#{views}
     </update>

结果: update blog SET author=?, title=? where views=?

注意点:

  • set可自动去掉最后一个‘ ,’使得拼接成功
  • set里是更新的内容,where后写的是条件语句

trim

SQL片段

有的时候,可能建议写功能的部分抽取出来,方便服用

1.使用SQL标签抽取公告部分

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

2.在使用的地方使用include即可

    <select id="queryBlogIF" parameterType="map" resultType="com.csf.pojo.Blog">
          select * from blog
          <where>
              <include refid="if-title-author"></include>
          </where>
     </select>

Foreach

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

1、编写接口

//foreach
    List<Blog> queryBlogForeach(Map map);

2、编写SQL语句

<select id="queryBlogForeach" parameterType="map" resultType="com.csf.pojo.Blog">
          select * from blog
          <where>
            <!--collection:map集合中的Key
               item: 集合中所代表的字段
               open:以什么开始
               close:以什么结束
               separator:中间的分割符
               id=#{id}:中间的取值
               -->
               <foreach collection="ids" item="id" open="and (" close=")" separator="or">
                    id=#{id}
               </foreach>
          </where>
     </select>

3、测试

@Test
public void test6() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    Map map = new HashMap();
    List list=new ArrayList();
    list.add(1);
    list.add(2);
    list.add(3);
    map.put("ids",list);
    List<Blog> blogs = mapper.queryBlogForeach(map);
    for (Blog blog : blogs) {
        System.out.println(blog);
    }

}

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

动态SQL在开发中大量的使用,一定要熟练掌握!

11、缓存

1、什么是缓存 [ Cache ]?

  • 内存中的临时数据。

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

2、为什么使用缓存?

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

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

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

2.Mybatis缓存

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

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

    • 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)

    • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。

    • 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

3.一级缓存

  • 一级缓存也叫做本地缓存:SqlSession
    • 与数据库同义词会话期间查询到的数据会放在本地缓存中。
    • 以后如果需要获取相同的数据,直接从缓存中拿,就没必要再去查询数据库
1.测试

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

2、编写接口方法


//根据id查询用户
    Blog refer(@Param("id") int id);

3、接口对应的Mapper文件

<select id="refer" resultType="blog">
          select * from blog where id=#{id}
     </select>

4、测试

@Test
    public void test(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        Blog blog1 = mapper.refer(1);
        System.out.println(blog1);
        System.out.println("==================");
        Blog blog2 = mapper.refer(1);
        System.out.println(blog2);
        System.out.println("=======");
        System.out.println(blog1==blog2);
    }

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

4、缓存失效的情况
  • 所有的insert、update、delete语句都会刷新缓存

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

  • 查询的不同

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

  • 手动清理缓存

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

4、二级缓存

  • 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存

  • 基于namespace级别的缓存,一个名称空间,对应一个二级缓存;

  • 工作机制

    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;

    • 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;

    • 新的会话查询信息,就可以从二级缓存中获取内容;

    • 不同的mapper查出的数据会放在自己对应的缓存(map)中;

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

    步骤:

    ​ 1、开启全局缓存(默认开启)

    ​ 2、在xml文件中使用二级缓存

    <cache
      eviction="FIFO"
      flushInterval="60000"
      size="512"
      readOnly="true"/>
    
    <!--也可以不配置属性-->
    <cache/>
    

    ​ 3、测试

    ​ 未开启二级缓存前:查询了两次

    @Test
        public void test2(){
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            SqlSession sqlSession2 = MybatisUtils.getSqlSession();
            BlogMapper mapper2 = sqlSession2.getMapper(BlogMapper.class);
            BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
            Blog blog1 = mapper.refer(1);
            System.out.println(blog1);
            System.out.println("==================");
            sqlSession.close();
            Blog blog2 = mapper2.refer(1);
            System.out.println(blog2);
            System.out.println("=======");
            System.out.println(blog1==blog2);
            sqlSession2.close();
        }
    

    ​ 开启后:只查询了一次

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

    注意点:

    • 只要开启了二级缓存,在同一个Mapper下就有效
    • 所有的数据都会先放在一级缓存中;
  • 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存

  • 基于namespace级别的缓存,一个名称空间,对应一个二级缓存;

  • 工作机制

    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;

    • 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;

    • 新的会话查询信息,就可以从二级缓存中获取内容;

    • 不同的mapper查出的数据会放在自己对应的缓存(map)中;

    [外链图片转存中…(img-9GgLzMAF-1647738192361)]

    步骤:

    ​ 1、开启全局缓存(默认开启)

    ​ 2、在xml文件中使用二级缓存

    <cache
      eviction="FIFO"
      flushInterval="60000"
      size="512"
      readOnly="true"/>
    
    <!--也可以不配置属性-->
    <cache/>
    

    ​ 3、测试

    ​ 未开启二级缓存前:查询了两次

    @Test
        public void test2(){
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            SqlSession sqlSession2 = MybatisUtils.getSqlSession();
            BlogMapper mapper2 = sqlSession2.getMapper(BlogMapper.class);
            BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
            Blog blog1 = mapper.refer(1);
            System.out.println(blog1);
            System.out.println("==================");
            sqlSession.close();
            Blog blog2 = mapper2.refer(1);
            System.out.println(blog2);
            System.out.println("=======");
            System.out.println(blog1==blog2);
            sqlSession2.close();
        }
    

    ​ 开启后:只查询了一次

    [外链图片转存中…(img-N1C80CcO-1647738192361)]

    注意点:

    • 只要开启了二级缓存,在同一个Mapper下就有效
    • 所有的数据都会先放在一级缓存中;
    • 只有当会话提交或者关闭的时候,才换转存到二级缓存
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值