Mybatis

MyBatis

1、Mybatis简介

1.1、什么是MyBatis

  • 持久层框架
  • 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集的过程
  • 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 实体类 【Plain
    Old Java Objects,普通的 Java对象】映射成数据库中的记录。
  • Mybatis官方文档 : http://www.mybatis.org/mybatis-3/zh/index.html

1.2、持久化

  • 持久化是将程序数据在持久状态和瞬时状态间转换的机制。
    • 即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。
    • JDBC就是一种持久化机制。文件IO也是一种持久化机制。
  • 为什么需要持久化服务呢?那是由于内存本身的缺陷引起的
    • 内存断电后数据会丢失
    • 内存成本高

1.3、持久层

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

1.4、为什么需要Mybatis

  • Mybatis就是帮助程序猿将数据存入数据库中 , 和从数据库中取数据。
  • 传统的jdbc操作 , 有很多重复代码块。比如 : 数据取出时的封装 , 数据库的建立连接等等... , 通过框
    架可以减少重复代码,提高开发效率 。
  • MyBatis 是一个半自动化的ORM框架 (Object Relationship Mapping) -->对象关系映射

1.5、MyBatis的优点

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

2、MyBatis第一个程序

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

2.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');
insert into `user`(`id`,`name`,`pwd`)
values(2,'张三','123456');
insert into `user`(`id`,`name`,`pwd`)
values(3,'李四','123456');

2.2、导入MyBatis相关 jar 包

<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>

2.3、编写MyBatis核心配置文件

mytais-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments 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&amp;useUnicode=true&amp;characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="com/kuang/dao/userMapper.xml"/>
    </mappers>
</configuration>

2.4编写MyBatis工具类

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

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 getSession(){
        return sqlSessionFactory.openSession();
    }
}

2.5创建实体类

@Data
public class User {
    private int id; //id
    private String name; //姓名
    private String pwd; //密码
}

2.6编写Mapper接口类

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

2.7编写Mapper.xml配置文件

namespace 十分重要,不能写错!

<?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.kuang.dao.UserMapper">
    <select id="selectUser" resultType="com.kuang.entity.User">
        select * from user
    </select>
</mapper>

2.8编写测试类

踩坑:

  • 关于编译mapper.xml文件target里没有的问题

    • 在pom.xml文件里build中配置resource,来防止资源导出失败的问题
     <build>
            <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>
        </build>
    
  • mybatis配置文件 需要引入mapper文件,路径用'/'

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

import com.kuang.dao.UserMapper;
import com.kuang.entity.User;
import com.kuang.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;

import java.util.List;

public class Mytest {
    @Test
    public void selectUser() {
        SqlSession session = MybatisUtils.getSession();
//方法一:
//List<User> users =
//        session.selectList("com.kuang.dao.UserMapper.selectUser");
//方法二:
        UserMapper mapper = session.getMapper(UserMapper.class);
        List<User> users = mapper.selectUser();
        for (User user: users){
            System.out.println(user);
        }
        session.close();
    }
}

3丶CURD

3.1 namespace

1、将上面案例中的UserMapper接口改名为 UserDao;

2、将UserMapper.xml中的namespace改为为UserDao的路径 .

3、再次测试

结论:
配置文件中namespace中的名称为对应Mapper接口或者Dao接口的完整包名,必须一致!

3.2 select

  • select标签是mybatis中最常用的标签之一

  • select语句有很多属性可以详细配置每一条SQL语句

    • id 1丶命名空间中唯一的标识符 2丶接口中的方法名与映射文件中的SQL语句ID 一一对应
    • parameterType 传入SQL语句的参数类型 。【万能的Map,可以多尝试使用】
    • resultType SQL语句返回值类型。【完整的类名或者别名】

    需求:根据id查询用户

    1. 在UserMapper中添加对应方法

      public interface UserMapper {
      //查询全部用户
      List<User> selectUser();
      //根据id查询用户
      User selectUserById(int id);
      }
      
    2. 在UserMapper.xml中添加Select语句

      <select id="selectUserById" resultType="com.kuang.entity.User">
              select * from user where id = #{id}
          </select>
      
    3. 测试类中测试

      @Test
      public void tsetSelectUserById() {
      SqlSession session = MybatisUtils.getSession(); //获取SqlSession连接
      UserMapper mapper = session.getMapper(UserMapper.class);
      User user = mapper.selectUserById(1);
      System.out.println(user);
      session.close();
      }
      

    课堂练习:根据 密码 和 名字 查询用户

    思路一:直接在方法中传递参数

    1. 在接口方法的参数前加 @Param属性

    2. Sql语句编写的时候,直接取@Param中设置的值即可,不需要单独设置参数类型

      //根据密码和名字查询用户
          User selectUserByNP(@Param("username") String username, @Param("pwd") String pwd);
      <select id="selectUserByNP" resultType="com.kuang.entity.User">
              select * from user where name = #{username} and pwd = #{pwd}
          </select>
      

    思路二:使用万能的Map

    1. 在接口方法中,参数直接传递Map;

      User selectUserByNP2(Map<String,Object> map);
      
    2. 编写sql语句的时候,需要传递参数类型,参数类型为map

      <select id="selectUserByNP2" parameterType="map"
                  resultType="com.kuang.entity.User">
              select * from user where name = #{username} and pwd = #{pwd}
          </select>
      
    3. 在使用方法的时候,Map的 key 为 sql中取的值即可,没有顺序要求!

      Map<String, Object> map = new HashMap<String, Object>();
      map.put("username","小明");
      map.put("pwd","123456");
      User user = mapper.selectUserByNP2(map);
      

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

3.3 insert

需求:给数据库增加一个用户

  1. 在UserMapper接口中添加对应的方法

    //添加一个用户
    int addUser(User user);
    
  2. 在UserMapper.xml中添加insert语句

    <insert id="addUser" parameterType="com.kuang.entity.User">
            insert into user (id,name,pwd) values (#{id},#{name},#{pwd})
        </insert>
    
  3. 测试类中测试

    @Test
    public void testAddUser() {
    SqlSession session = MybatisUtils.getSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    User user = new User(5,"王五","zxcvbn");
    int i = mapper.addUser(user);
    System.out.println(i);
    session.commit(); //提交事务,重点!不写的话不会提交到数据库
    session.close();
    }
    

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

3.4 update

需求:修改用户的信息

  1. 在UserMapper接口中添加对应的方法

    //修改一个用户
    int updateUser(User user);
    
  2. 在UserMapper.xml中添加update语句

    <update id="updateUser" parameterType="com.kuang.entity.User">
            update user set name=#{name},pwd=#{pwd} where id = #{id}
        </update>
    
  3. 测试类中测试

    @Test
    public void testUpdateUser() {
    SqlSession session = MybatisUtils.getSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    User user = mapper.selectUserById(1);
    user.setPwd("asdfgh");
    int i = mapper.updateUser(user);
    System.out.println(i);
    session.commit(); //提交事务,重点!不写的话不会提交到数据库
    session.close();
    }
    

3.5 delete

需求:根据id删除一个用户

  1. 在UserMapper接口中添加对应的方法

    //根据id删除用户
        int deleteUser(int id);
    
  2. 在UserMapper.xml中添加delete语句

    <delete id="deleteUser" parameterType="int">
    delete from user where id = #{id}
    </delete>
    
  3. 测试类中测试

    @Test
    public void testDeleteUser() {
    SqlSession session = MybatisUtils.getSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    int i = mapper.deleteUser(5);
    System.out.println(i);
    session.commit(); //提交事务,重点!不写的话不会提交到数据库
    session.close();
    }
    

小结:

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

3.6万能Map

  1. 在UserMapper接口中添加对应的方法

    //添加一个用户
    int addUser2(Map<String,Object> map);
    
    User selectUserById2(Map<String,Object> map);
    
  2. 在UserMapper.xml中添加语句

    <insert id="addUser2" parameterType="map">
            insert into user (id,name,pwd) values (#{userid},#{username},#{passWd})
    </insert>
        
    <select id="selectUserById2" parameterType="map" resultType="com.kuang.entity.User">
            select * from user where id = #{heeloid}
    </select>
    
  3. 测试类中测试

    @Test
    public void testAddUser() {
    SqlSession session = MybatisUtils.getSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    User user = new User(5,"王五","zxcvbn");
    int i = mapper.addUser(user);
    System.out.println(i);
    session.commit(); //提交事务,重点!不写的话不会提交到数据库
    session.close();
    }
    
    @Test
        public void testselectUserById2() {
            SqlSession session = MybatisUtils.getSession();
            UserMapper mapper = session.getMapper(UserMapper.class);
            HashMap<String, Object> map = new HashMap<>();
            map.put("heeloid",6);
            User user = mapper.selectUserById2(map);
            System.out.println(user);
            session.commit(); //提交事务,重点!不写的话不会提交到数据库
            session.close();
        }
    

3.7模糊查询

  1. 在UserMapper接口中添加对应的方法

    List<User>  getUserLike(Map<String,Object> map);
    
  2. 在UserMapper.xml中添加语句

    <select id="getUserLike" parameterType="map" resultType="com.kuang.entity.User">
            select * from user where name like "%"#{username}"%"
    </select>
    
  3. 测试类中测试

    @Test
        public void testgetUserLike() {
            SqlSession session = MybatisUtils.getSession();
            UserMapper mapper = session.getMapper(UserMapper.class);
            HashMap<String, Object> map = new HashMap<>();
            map.put("username","李");
            List<User> userList = mapper.getUserLike(map);
            System.out.println(userList);
            session.commit(); //提交事务,重点!不写的话不会提交到数据库
            session.close();
        }
    

4丶配置解析

4.1核心配置文件

  • mybatis-config.xml 系统核心配置文件
  • MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息
  • 能配置的内容如下:
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
<!-- 注意元素节点的顺序!顺序不对会报错 -->

4.2 environments元素

<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>
</environments>
  • 配置MyBatis的多套运行环境,将SQL映射到多个不同的数据库上,必须指定其中一个为默认运行
    环境(通过default指定)

  • 子元素节点:environment

    • 具体的一套环境,通过设置id进行区别,id保证唯一!
    • 子元素节点:transactionManager - [ 事务管理器 ]
    <!-- 语法 -->
    <transactionManager type="[ JDBC | MANAGED ]"/>
    
    • 子元素节点:数据源(dataSource)

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

      2. 数据源是必须配置的

      3. 有三种内建的数据源类型

        type="[UNPOOLED|POOLED|JNDI]"
      4. unpooled: 这个数据源的实现只是每次被请求时打开和关闭连接

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

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

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

4.3 environments元素

4.3.1 mappers

  • 映射器 : 定义映射SQL语句文件

4.3.2 引入资源方式

<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 使用完全限定资源定位符(URL) -->
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
</mappers>
<!--
使用映射器接口实现类的完全限定类名
需要配置文件名称和接口名称一致,并且位于同一目录下
-->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
</mappers>
<!--
将包内的映射器接口实现全部注册为映射器
但是需要配置文件名称和接口名称一致,并且位于同一目录下
-->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>

4.3.3 Mapper文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.kuang.mapper.UserMapper">
</mapper>
  • namespace中文意思:命名空间,作用如下:

    1. namespace和子元素的id联合保证唯一 , 区别不同的mapper

    2. 绑定DAO接口

      2.1 namespace的命名必须跟某个接口同名

      2.2 接口中的方法与映射文件中sql语句id应该一一对应

      2.3namespace命名规则 : 包名+类名

MyBatis 的真正强大在于它的映射语句,这是它的魔力所在。由于它的异常强大,映射器的 XML 文件就
显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代
码。MyBatis 为聚焦于 SQL 而构建,以尽可能地为你减少麻烦

4.4 Properties优化

第一步 ; 在资源目录下新建一个db.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8
username=root
password=123456

第二步 : 将文件导入properties 配置文件

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

    <environments 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>
        <mapper resource="com/kuang/dao/UserMapper.xml"/>
    </mappers>
</configuration>

4.5 typeAliases优化

类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全
限定名的冗余

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

当这样配置时, User 可以用在任何使用 com.kuang.pojo.User 的地方。

指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:

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

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

省略alias属性之后,别名就是类的简名。
比如com.powernode.mybatis.pojo.Car的别名就是Car/car/CAR/caR 不区分大小写。

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

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

4.6 其他配置浏览

4.6.1 设置

  • 设置(settings)相关 => 查看帮助文档

    • 懒加载
    • 日志实现
    • 缓存开启关闭
  • 一个配置完整的 settings 元素的示例如下:

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

4.6.2 类型处理器

  • 无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出
    一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型
  • 你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。【了解即可】

4.6.3 对象工厂

  • MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完
    成。
  • 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时
    候通过有参构造方法来实例化。
  • 如果想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现。【了解即可】

4.7 生命周期和作用域

作用域(Scope)和生命周期

image-20230803112826522

作用域理解

  • 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 的最佳的作用域是请求或方法作用域。

    image-20230803112931114

5丶 ResultMap

要解决的问题:属性名和字段名不一致

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

5.1 查询为null问题

数据库密码pwd字段和实体类字段password不一致的情况下

Java中的实体类设计

public class User {
private int id; //id
private String name; //姓名
private String password; //密码和数据库不一样!
//构造
//set/get
//toString()
}
  • mybatis会根据这些查询的列名(会将列名转化为小写,数据库不区分大小写) , 去对应的实体类中查找
    相应列名的set方法设值 , 由于找不到setPwd() , 所以password返回null ; 【自动映射】

5.2 解决方案

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

<select id="selectUserById" resultType="User">
select id , name , pwd as password from user where id = #{id}
</select>

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

<resultMap id="UserMap" type="User">
<!-- id为主键 -->
<id column="id" property="id"/>
<!-- column是数据库表的列名 , property是对应实体类的属性名 -->
<result column="name" property="name"/>
<result column="pwd" property="password"/>
</resultMap>
<select id="selectUserById" resultMap="UserMap">
select id , name , pwd from user where id = #{id}
</select>

5.3 ResultMap

5.3.1 自动映射

  • resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC
    ResultSets 数据提取代码中解放出来
  • 实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份 resultMap 能够代替实现同
    等功能的长达数千行的代码。
  • ResultMap 的设计思想是,对于简单的语句根本不需要配置显式的结果映射,而对于复杂一点的语
    句只需要描述它们的关系就行了。

你已经见过简单映射语句的示例了,但并没有显式指定 resultMap 。比如:

<select id="selectUserById" resultType="map">
select id , name , pwd
from user
where id = #{id}
</select>

上述语句只是简单地将所有的列映射到 HashMap 的键上,这由 resultType 属性指定。虽然在
大部分情况下都够用,但是 HashMap 不是一个很好的模型。你的程序更可能会使用 JavaBean 或
POJO(Plain Old Java Objects,普通老式 Java 对象)作为模型。

ResultMap 最优秀的地方在于,虽然你已经对它相当了解了,但是根本就不需要显式地用到他们。

5.3.2 手动映射

  1. 返回值类型为resultMap !!!不是resultTyperesultMap
<select id="selectUserById" resultMap="UserMap">
select id , name , pwd from user where id = #{id}
</select>
  1. 编写resultMap,实现手动映射!

    <resultMap id="UserMap" type="User">
    <!-- id为主键 -->
    <id column="id" property="id"/>
    <!-- column是数据库表的列名 , property是对应实体类的属性名 -->
    <result column="name" property="name"/>
    <result column="pwd" property="password"/>
    </resultMap>
    

如果世界总是这么简单就好了。但是肯定不是的,数据库中,存在一对多,多对一的情况,我们之后会
使用到一些高级的结果集映射,association,collection这些,我们将在之后讲解,今天你们需要把这
些知识都消化掉才是最重要的!理解结果集映射的这个概念!

6丶分页的实现

6.1 日志工厂

Mybatis内置的日志工厂提供日志功能,具体的日志实现有以下几种工具:

  • SLF4J
  • Apache Commons Logging
  • Log4j 2
  • Log4j
  • JDK logging

具体选择哪个日志实现工具由MyBatis的内置日志工厂确定。它会使用最先找到的(按上文列举的顺序
查找)。 如果一个都未找到,日志功能就会被禁用。

标准日志实现

指定 MyBatis 应该使用哪个日志记录实现。如果此设置不存在,则会自动发现日志记录实现。

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

测试,可以看到控制台有大量的输出!我们可以通过这些输出来判断程序到底哪里出了Bug

6.2 Log4j

简介:

  • Log4j是Apache的一个开源项目

  • 通过使用Log4j,我们可以控制日志信息输送的目的地:控制台,文本,GUI组件....

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

  • 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就
    是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

使用步骤:

  1. 导入log4j的包

    <dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
    </dependency>
    
    1. 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/gyk.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
  1. setting设置日志实现

    <settings>
    <setting name="logImpl" value="LOG4J"/>
    </settings>
    
  2. 在程序中使用Log4j进行输出!

    //注意导包:org.apache.log4j.Logger
    static Logger logger = Logger.getLogger(MyTest.class);
    
    @Test
    public void selectUser() {
    logger.info("info:进入selectUser方法");
    logger.debug("debug:进入selectUser方法");
    logger.error("error: 进入selectUser方法");
    SqlSession session = MybatisUtils.getSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    List<User> users = mapper.selectUser();
    for (User user: users){
    System.out.println(user);
    } 
    session.close();
    }
    

    测试,看控制台输出!

6.3 limit实现分页

思考:为什么需要分页?

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

使用Limit实现分页

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

步骤:

1.修改Mapper文件  
<select id="selectUser" parameterType="map" resultMap="UserMap">
        select * from user limit #{startIndex},#{pageSize}
</select>

2.Mapper接口,参数为map

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

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

  • 推断:起始位置 = (当前页面 - 1 ) * 页面大小
@Test
    public void getUserByLimit(){
        SqlSession session = MybatisUtils.getSession(); //获取SqlSession连接
        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();
    }

6.4 RowBounds分页

淘汰

6.5、PageHelper

分页插件了解即可

7.使用注解开发

7.1、面向接口编程

  • 根本原因 : 解耦 , 可拓展 , 提高复用 , 分层开发中 , 上层不用管具体的实现 , 大家都遵守共同的标准
    , 使得开发变得容易 , 规范性更好
  • 在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,
    各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了;
  • 而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各模块之间的交
    互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是指按
    照这种思想来编程。

关于接口的理解

  • 接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离。
  • 接口的本身反映了系统设计人员对系统的抽象理解。
  • 接口应有两类:
    • 第一类是对一个个体的抽象,它可对应为一个抽象体(abstract class);
    • 第二类是对一个个体某一方面的抽象,即形成一个抽象面(interface);
  • 一个体有可能有多个抽象面。抽象体与抽象面是有区别的。

三个面向区别

  • 面向对象是指,我们考虑问题时,以对象为单位,考虑它的属性及方法 .
  • 面向过程是指,我们考虑问题时,以一个具体的流程(事务过程)为单位,考虑它的实现 .
  • 接口设计与非接口设计是针对复用技术而言的,与面向对象(过程)不是一个问题.更多的体现就是
    对系统整体的架构

7.2 利用注解开发

用不到

7.3 注解增删改

7.4 关于@Param

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

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

7.5 #与$的区别

  • #{} 的作用主要是替换预编译语句(PrepareStatement)中的占位符? 【推荐使用】
INSERT INTO user (name) VALUES (#{name});
INSERT INTO user (name) VALUES (?);
  • ${} 的作用是直接进行字符串替换
INSERT INTO user (name) VALUES ('${name}');
INSERT INTO user (name) VALUES ('kuangshen');

8.多对一的处理

多对一的理解:

  • 多个学生对应一个老师
  • 如果对于学生这边,就是一个多对一的现象,即从学生这边关联一个老师!

image-20230803112613671

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


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 teacher(`id`, `name`) VALUES (1, '秦老师');
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');

实体类

@Data //GET,SET,ToString,有参,无参构造
    public class Teacher {
    private int id;
    private String name;
}
@Data
public class Student {
    private int id;
    private String name;
    //多个学生可以是同一个老师,即多对一
    private Teacher teacher;
}

StudentMapper.xml

<?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.gyk.study.dao.StudentMapper">
<!--    <resultMap id="StudentTeacher" type="com.gyk.study.entity.Student">-->
<!--        &lt;!&ndash;association关联属性 property属性名 javaType属性类型 column在多的一方的表中的列名&ndash;&gt;-->
<!--        <association property="teacher" column="tid" javaType="com.gyk.study.entity.Teacher"-->
<!--                     select="getTeacher"/>-->
<!--    </resultMap>-->
    <resultMap id="StudentTeacher" type="Student">
        <!--association关联属性 property属性名 javaType属性类型 column在多的一方
        的表中的列名-->
        <association property="teacher" column="{id=tid}"
                     javaType="Teacher" select="getTeacher"/>
    </resultMap>

    <select id="getStudents" resultMap="StudentTeacher">
        select * from student
    </select>

<!--    <select id="getTeacher" resultType="com.gyk.study.entity.Teacher">-->
<!--        select * from teacher where id = #{id}-->
<!--    </select>-->

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

    <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"/>
        <!--关联对象property 关联对象在Student实体类中的属性-->
        <association property="teacher" javaType="Teacher">
            <result property="name" column="tname"/>
        </association>
    </resultMap>
</mapper>

MyTest

public class MyTest {
    static Logger logger = Logger.getLogger(MyTest.class);

    @Test
    public void testGetStudents(){
        logger.info("info:进入testGetStudents方法");
        logger.debug("debug:进入testGetStudents方法");
        logger.error("error: 进入testGetStudents方法");
        SqlSession session = MybatisUtils.getSession();
        StudentMapper mapper = session.getMapper(StudentMapper.class);
        List<Student> students = mapper.getStudents();
        for (Student student : students){
            System.out.println(
                    "学生名:"+ student.getName()
                            +"\t老师:"+student.getTeacher().getName());
        }
        session.close();
    }

    @Test
    public void testGetStudents2(){
        SqlSession session = MybatisUtils.getSession();
        StudentMapper mapper = session.getMapper(StudentMapper.class);
        List<Student> students = mapper.getStudents2();
        for (Student student : students){
            System.out.println(
                    "学生名:"+ student.getName()
                            +"\t老师:"+student.getTeacher().getName());
        }
    }
}

9.一对多的处理

一对多的理解:

  • 一个老师拥有多个学生
  • 如果对于老师这边,就是一个一对多的现象,即从一个老师下面拥有一群学生(集合)!

实体类

@Data
public class Student {
private int id;
private String name;
private int tid;
}
@Data
public class Teacher {
private int id;
private String name;
//一个老师多个学生
private List<Student> students;
}

TeacherMapper.xml

<?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.gyk.study.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 s.tid = t.id and t.id=#{id}
    </select>

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

MyTest

public class MyTest {
    static Logger logger = Logger.getLogger(MyTest.class);

    @Test
    public void testGetStudents(){
        logger.info("info:进入testGetStudents方法");
        logger.debug("debug:进入testGetStudents方法");
        logger.error("error: 进入testGetStudents方法");
        SqlSession session = MybatisUtils.getSession();
        TeacherMapper mapper = session.getMapper(TeacherMapper.class);
        Teacher teacher = mapper.getTeacher(1);
        System.out.println(teacher.getName());
        System.out.println(teacher.getStudents());
        session.close();
    }
}

9.1 小结

  1. 关联-association

  2. 集合-collection

  3. 所以association是用于一对一和多对一,而collection是用于一对多的关系

  4. JavaType和ofType都是用来指定对象类型的

    • JavaType是用来指定pojo中属性的类型

    • ofType指定的是映射到list集合属性中pojo的类型。

10 动态SQL

10.1 介绍

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

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

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

10.2 搭建环境

新建一个数据库表:blog

字段:id,title,author,create_time,views

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

2.IDutil工具类

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

3.实体类编写

import java.util.Date;
public class Blog {
private String id;
private String title;
private String author;
private Date createTime;
private int views;
//set,get....
}

4.编写Mapper接口及xml文件

public interface BlogMapper {
    int addBlog(Blog blog);
}
<?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.gyk.study.dao.BlogMapper">
    <insert id="addBlog" parameterType="blog">
        insert into blog (id, title, author, create_time, views)
        values (#{id},#{title},#{author},#{createTime},#{views});
    </insert>
</mapper>
  1. mybatis核心配置文件,下划线驼峰自动转换
<settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
<!--注册Mapper.xml-->
<mappers>
<mapper resource="com/gyk/study/dao/BlogMapper.xml"/>
</mappers>

Test

@Test
    public void addInitBlog(){
        SqlSession session = MybatisUtils.getSession();
        BlogMapper mapper = session.getMapper(BlogMapper.class);
        Blog blog = new Blog();
        blog.setId(IDUtil.genId());
        blog.setTitle("Mybatis如此简单");
        blog.setAuthor("狂神说");
        blog.setCreateTime(new Date());
        blog.setViews(9999);
        mapper.addBlog(blog);
        blog.setId(IDUtil.genId());
        blog.setTitle("Java如此简单");
        mapper.addBlog(blog);
        blog.setId(IDUtil.genId());
        blog.setTitle("Spring如此简单");
        mapper.addBlog(blog);
        blog.setId(IDUtil.genId());
        blog.setTitle("微服务如此简单");
        mapper.addBlog(blog);
        session.commit();
        session.close();
    }

10.3 if 语句

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

1.编写接口类

List<Blog> queryBlogIf(Map map);

2.编写SQL语句

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

3.测试

@Test
    public void testQueryBlogIf(){
        SqlSession session = MybatisUtils.getSession();
        BlogMapper mapper = session.getMapper(BlogMapper.class);
        HashMap<String, String> map = new HashMap<String, String>();
        map.put("title","Mybatis如此简单");
        map.put("author","狂神说");
        List<Blog> blogs = mapper.queryBlogIf(map);
        System.out.println(blogs);
        session.close();
    }

10.4 Where

修改上面的SQL语句:

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

这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个‘where’。

如果标签返回的内容是以AND 或OR 开头的,则它会剔除掉。【这是我们使用的最多的案例】

10.5 Set

1.编写接口类

int updateBlog(Map map);

2.编写SQL语句

<!--注意set是用的逗号隔开-->
    <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 testUpdateBlog(){
        SqlSession session = MybatisUtils.getSession();
        BlogMapper mapper = session.getMapper(BlogMapper.class);
        HashMap<String, String> map = new HashMap<String, String>();
        map.put("title","动态SQL");
        map.put("author","秦疆");
        map.put("id","553dc3f5b76946af94f7cc8aec079483");
        mapper.updateBlog(map);
        session.commit();
        session.close();
    }

10.6 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 testQueryBlogChoose(){
        SqlSession session = MybatisUtils.getSession();
        BlogMapper mapper = session.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);
        session.close();
    }

choose when 类似于switch case

从第一个when开始匹配 匹配上就不再往下继续匹配了,匹配不上继续向下匹配,若是都没匹配上拼otherwise里的语句

10.7 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 片段,提高片段的可重用性
②、在 sql 片段中不要包括 where

10.7 Foreach

将数据库中前三个数据的id修改为1,2,3;
需求:我们需要查询 blog 表中 id 分别为1,2,3的博客信息

1.编写接口类

List<Blog> queryBlogForeach(Map map);

2.编写SQL语句

<select id="queryBlogForeach" parameterType="map" resultType="blog">
        select * from blog
        <where>
            <!--
            collection:指定输入对象中的集合属性
            item:每次遍历生成的对象
            open:开始遍历时的拼接字符串
            close:结束时拼接的字符串
            separator:遍历对象之间需要拼接的字符串
            select * from blog where 1=1 and (id=1 or id=2 or id=3)
            -->
            <foreach collection="ids" item="id" open="and (" close=")"
                     separator="or">
                id=#{id}
            </foreach>
        </where>
    </select>

3.测试

@Test
    public void testQueryBlogForeach(){
        SqlSession session = MybatisUtils.getSession();
        BlogMapper mapper = session.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);
        System.out.println(blogs);
        session.close();
    }

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

11.缓存

11.1 简介

  1. 什么是缓存 [ Cache ]?
    • 存在内存中的临时数据。
    • 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库
      数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
  2. 为什么使用缓存?
    • 减少和数据库的交互次数,减少系统开销,提高系统效率。
  3. 什么样的数据能使用缓存?
    • 经常查询并且不经常改变的数据。

11.2 Mybatis缓存

  • MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的
    提升查询效率。
  • MyBatis系统中默认定义了两级缓存:一级缓存二级缓存
    • 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
    • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
    • 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二
      级缓存

11.3 一级缓存

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

11.3.1 初体验测试

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

  2. 编写接口方法

//根据id查询用户
    Student queryStudentById(@Param("id") int id);
  1. 接口对应的Mapper文件
<select id="queryStudentById" resultType="student">
        select * from student where id = #{id}
</select>
  1. 测试
@Test
    public void testQueryUserById(){
        SqlSession session = MybatisUtils.getSession();
        StudentMapper mapper = session.getMapper(StudentMapper.class);
        Student student = mapper.queryStudentById(1);
        System.out.println(student);
        Student student2 = mapper.queryStudentById(1);
        System.out.println(student2);
        System.out.println(student==student2);
        session.close();
    }
  1. 结果分析

image-20230809150950384

11.3.2 一级缓存失效的四种情况

  • 一级缓存是SqlSession级别的缓存,是一直开启的,我们关闭不了它;
  • 一级缓存失效情况:没有使用到当前的一级缓存,效果就是,还需要再向数据库中发起一次查询请求!
  1. sqlSession不同
@Test
    public void testQueryStudentById(){
        SqlSession session1= MybatisUtils.getSession();
        SqlSession session2 = MybatisUtils.getSession();
        StudentMapper mapper1 = session1.getMapper(StudentMapper.class);
        StudentMapper mapper2 = session2.getMapper(StudentMapper.class);
        Student student = mapper1.queryStudentById(1);
        System.out.println(student);
        Student student2 = mapper2.queryStudentById(1);
        System.out.println(student2);
        System.out.println(student==student2);
        session1.close();
        session2.close();
    }

观察结果:发现发送了两条SQL语句!
结论:每个sqlSession中的缓存相互独立

posted @   KxWanna  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示