MyBatis

MyBatis

环境:

  • JDK17
  • MySQL5.7
  • maven3.8
  • IDEA

回顾:

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

1、简介

1.1、什么是MyBatis

img

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

  • 它支持自定义 SQL、存储过程以及高级映射

  • MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作

  • MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

  • MyBatis本是apache的一个开源项目iBatis,2010年这个项目由apache software foundation迁移到了[google code](https://baike.baidu.com/item/google code/2346604),并且改名为MyBatis。

  • 2013年11月迁移到Github

如何获得MyBatis

  • Maven仓库

    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.6</version>
    </dependency>
    
  • Github

    https://github.com/mybatis
    
  • 中文文档手册

    https://mybatis.net.cn/getting-started.html
    

1.2、持久化

数据持久化

  • 持久化就是将程序的数据在持久层状态和瞬时状态转化的过程
  • 内存:断电即失
  • 数据库(JDBC),IO文件持久化

为什么需要持久化

  • 有一些对象,不能让他丢掉
  • 内存太贵了

1.3、持久层

Dao层,Service层,Controler层……

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

1.4、为什么需要MyBatis

  • 帮助程序员将数据存入到数据库中

  • 方便

  • 传统JDBC代码太复杂了。简化。框架

  • 不用MyBatis也可以。更容易上手

1.5、MyBatis特点

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

1.6、MyBatis执行流程

image

2、第一个MyBatis程序

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

2.1、搭建环境

搭建数据库

CREATE DATABASE mybatis
USE mybatis
create table user(
    id INt(20) NOT NULL PRIMARY KEY ,
    name varchar(30) default NULL,
    pwd VARCHAR(30) default NULL
    )ENGINE=INNODB DEFAULT CHARSET =utf8;
INSERT INTO user (id,name,pwd) values
(1,'张三','123456'),
(2,'李四','123456'),
(3,'王五','123456')

新建项目

  1. 新建空的Maven项目

  2. 删除src目录

  3. 导入Maven依赖

        <!--项目依赖-->
        <dependencies>
            <!--    具体依赖的的jar包配置文件-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.11</version>
                <scope>test</scope>
            </dependency>
            <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.5.10</version>
            </dependency>
                <dependency>
                    <groupId>mysql</groupId>
                   <artifactId>mysql-connector-java</artifactId>
                    <version>8.0.29</version>
                  </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.13.2</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    

2.2、创建一个模块

  • 编写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.cj.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true &amp;characterEncoding=utf8&amp;useSSL=true&amp;serverTimezone=UTC"/>
                    <property name="username" value="root"/>
                    <property name="password" value="root1@12}"/>
                </dataSource>
            </environment>
        </environments>
    </configuration>
    
  • 编写mybatis工具类

    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();
            }
        }
        //既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。
    
        /**
         * @return sqlSessionFactory.openSession();
         */
        public static SqlSession getSqlSession(){
            return sqlSessionFactory.openSession();
        }
    }
    

2.3、编写代码

  • 实体类

    public class User {
        private int id;
        private String name;
        private String pwd;
    
        public User() {
        }
    
        public User(int id, String name, String pwd) {
            this.id = id;
            this.name = name;
            this.pwd = pwd;
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getPwd() {
            return pwd;
        }
    
        public void setPwd(String pwd) {
            this.pwd = pwd;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", pwd='" + pwd + '\'' +
                    '}';
        }
    }
    
  • Dao接口

    public interface UserDao {
        List<User> getUserList();
    }
    
  • 接口实现类由原来的UserDaoImpl转变为一个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">
    <!--namespace=绑定一个对应对Dao接口-->
    <mapper namespace="com.dao.UserDao">
        <select id="getUserList" resultType="com.pojo.User" >
            select * from mybatis.user
        </select>
    </mapper>
    

2.4、测试

注意点:

  1. org.apache.ibatis.binding.BindingException: Type interface com.dao.UserDao is not known to the MapperRegistry. 要注册Mapper!

  2. 在pom.xml中添加资源过滤器,防止资源导出失败

    <!--    在build中配置resources,来防止我们资源导出失败的问题-->
    <build>
      <resources>
        <resource>
          <directory>src/main/resources</directory>
          <includes>
            <include>**/*.properties</include>
            <include>**/*.xml</include>
          </includes>
          <filtering>true</filtering>
        </resource>
        <resource>
          <directory>src/main/java</directory>
          <includes>
            <include>**/*.properties</include>
            <include>**/*.xml</include>
          </includes>
          <filtering>true</filtering>
        </resource>
      </resources>
    </build>
    

测试

public class UserDaoTest {
    @Test
    public void test(){
        //第一步:获取SqlSession的对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        try{
            //第二步:getMapper
            UserDao userDao = sqlSession.getMapper(UserDao.class);
            for (User user : userDao.getUserList()) {
                System.out.println(user);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //第三步:关闭SqlSession
            sqlSession.close();
        }
    }
}

结果

User{id=1, name='张三', pwd='123456'}
User{id=2, name='李四', pwd='123456'}
User{id=3, name='王五', pwd='123456'}

3、CRUD

3.1、namespace

namespace中的包名要和Dao/mapper接口的包名一致

3.2、Select

选择,查询语句;

  • id:就是对应namespace中的方法名
  • resultType:Sql语句执行的返回值
  • parameterType:参数类型
  1. 编写接口

    //根据ID查询用户
    User getUserById(int id);
    
  2. 编写对应的mapper中的sql语句

    <select id="getUserById" parameterType="int" resultType="com.pojo.User">
        select * from mybatis.user where id=#{id};
    </select>
    
  3. 测试:

    /**
     * 通过ID查询用户
     */
    @Test
    public void getUserById(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = mapper.getUserById(1);
        System.out.println(user);
        sqlSession.close();
    }
    

3.3、Insert

  1. 编写接口

    //插入用户
    int addUser(User user);
    
  2. 编写对应的mapper中的sql语句

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

    @Test
    public  void addUser(){
      SqlSession sqlSession = MybatisUtils.getSqlSession();
      try{
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        int state = mapper.addUser(new User(8, "赵六", "123456"));
        if(state>0){
          System.out.println(state+"行受影响");
        }
        //提交事物
        sqlSession.commit();
      }catch (Exception e){
        System.out.println("数据已存在");
      }
      finally {
        sqlSession.close();
      }
    }
    

3.4、update

  1. 编写接口

    //修改用户
    int upDateUser(User user);
    
  2. 编写对应的mapper中的sql语句

    <update id="upDateUser" parameterType="com.pojo.User">
        update mybatis.user set name=#{name},pwd=#{pwd}  where id=#{id};
    </update>
    
  3. 测试:

    /**
         * 修改用户
         */
    @Test
    public  void upDateUser(){
      SqlSession sqlSession = MybatisUtils.getSqlSession();
      UserMapper mapper = sqlSession.getMapper(UserMapper.class);
      int state = mapper.upDateUser(new User(4, "赵六", "123458"));
      if(state>0){
        System.out.println("更新"+state+"行受影响");
      }
      //提交事物
      sqlSession.commit();
      sqlSession.close();
    }
    

3.5、Delete

  1. 编写接口

    //删除用户
    int deleteUser(int id);
    
  2. 编写对应的mapper中的sql语句

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

    /**
         * 删除用户
         */
    @Test
    public  void deleteUser(){
      SqlSession sqlSession = MybatisUtils.getSqlSession();
      UserMapper mapper = sqlSession.getMapper(UserMapper.class);
      int res = 4;
      int state = mapper.deleteUser(res);
      if(state>0){
        System.out.println("ID="+res+"已删除,"+state+"行受影响");
      }
      //提交事物
      sqlSession.commit();
      sqlSession.close();
    }
    

注意点:增删改需要提交事物

3.6、使用Map

假设,我们的实体类,或者数据库中的表,字段或者参数过多,我们应当考虑使用Map

  1. 编写接口

    //Map插入一个数据
    int mapAddUser(Map<String, Object> map);
    
  2. 编写对应Mapper中sql

    <!--Map插入用户,传递map的key-->
    <insert id="mapAddUser" parameterType="map" >
       insert into mybatis.user (id,name,pwd) values (#{id},#{name},#{pwd});
    </insert>
    
  3. 测试

    /**
     * 使用MAP添加用户
     */
    @Test
    public  void addMapUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("id",5);
        map.put("name","赵六");
        map.put("pwd","123456");
        System.out.println(map.toString());
        mapper.mapAddUser(map);
        sqlSession.commit();
        sqlSession.close();
    }
    

Map传递参数,直接在sql中取出key即可「parameterType="map"」

对象传递参数,直接在sql中取对象的属性即可「parameterType="Object"」

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

3.7、模糊查询

  • Java代码执行的时候,传递通配符%%

    select * from mybatis.user where name like #{value};
    String value = "%李%"
    
  • 在sql拼接中使用通配符

    select * from mybatis.user where name like "%"#{value}"%";
    String value = "李"
    
  1. 编写接口

    //模糊查询
        List<User> getUserLike(String value);
    }
    
  2. 编写对应Mapper中sql

    <!--    模糊查询-->
    <select id="getUserLike" parameterType="String" resultType="com.pojo.User">
        select * from mybatis.user where name like "%"#{value}"%";
    </select>
    
  3. 测试

    /**
     * 使用模糊查询
     */
    @Test
    public void getUserLike(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userLike = mapper.getUserLike("李");
        for (User user : userLike) {
            System.out.println(user);
        }
        sqlSession.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)

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

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

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

Mybatis默认的事务管理器就是JDBC(type="[JDBC|MANAGED]),

Mybatis默认的连接池:POOLED(type="[UNPOOLED|POOLED|JNDI]")

    <environments default="test">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://114.96.74.49:3306/mybatis?useUnicode=true &amp;characterEncoding=utf8&amp;useSSL=true&amp;serverTimezone=UTC"/>
                <property name="username" value="root"/>
                <property name="password" value="root1@12"/>
            </dataSource>
        </environment>
        <environment id="test">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://114.96.74.49:3306/mybatis?useUnicode=true &amp;characterEncoding=utf8&amp;useSSL=true&amp;serverTimezone=UTC"/>
                <property name="username" value="root"/>
                <property name="password" value="root1@12"/>
            </dataSource>
        </environment>
    </environments>

4.3、属性(properties)

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

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

编写一个数据库配置文件(db.properties)

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://114.96.74.49:3306/mybatisuseUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=UTC
username=root
password=root1@12

在configuration里面添加(注意放在上面)

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

环境配置

<environment id="test">
    <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>
  • 可以直接引入外部文件
  • 可以在其中增加一些属性配置
  • 如果两个文件都用同一字段,优先使用外部配置文件

4.4、类型别名(typeAliases)

  • 类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置

  • 意在降低冗余的全限定类名书写。例如:

<typeAliases>
  <typeAlias alias="Author" type="domain.blog.Author"/>
  <typeAlias alias="Blog" type="domain.blog.Blog"/>
  <typeAlias alias="Comment" type="domain.blog.Comment"/>
  <typeAlias alias="Post" type="domain.blog.Post"/>
  <typeAlias alias="Section" type="domain.blog.Section"/>
  <typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>

也可以指定一个包名,MyBatis会在包名下面搜索需要的JavaBean,比如:

扫描实体类的包,它的默认别名就为这个类的类名首字母小写

<typeAliases>
  <package name="domain.blog"/>
</typeAliases>
==================================================================
 <select id="getUserList" resultType="User" >
      select id,name,pwd as password  from mybatis.user
</select>

每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为author;若有注解,则别名为其注解值。见下面的例子:

@Alias("author")
public class Author {
    ...
}

4.5、设置

这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项设置的含义、默认值等。

设置名 描述 有效值 默认值
mapUnderscoreToCamelCase 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 true | false False
logImpl 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING
cacheEnabled 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 true | false true
lazyLoadingEnabled 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 true | false false

4.6、其他配置

  • typeHandlers(类型处理器)
  • objectFactory(对象工厂)
  • plugins(插件)
    • Mybatis-generator-core
    • Mybatis -plus
    • 通用Mapper

4.7、映射器(mappers)

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

方式一:「推荐使用」

<!-- 使用相对于类路径的资源引用 -->
<mappers>
  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
  <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
  <mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>

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

注意点:

  • 接口和他的Mapper配置文件必须同名
  • 接口和他的Mapper配置文件必须在同一个包下
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
  <mapper class="org.mybatis.builder.AuthorMapper"/>
  <mapper class="org.mybatis.builder.BlogMapper"/>
  <mapper class="org.mybatis.builder.PostMapper"/>
</mappers>

方式三(不推荐)

<!-- 使用完全限定资源定位符(URL) -->
<mappers>
  <mapper url="file:///var/mappers/AuthorMapper.xml"/>
  <mapper url="file:///var/mappers/BlogMapper.xml"/>
  <mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>

方式四:使用扫描包进行注入绑定

注意点:

  • 接口和他的Mapper配置文件必须同名
  • 接口和他的Mapper配置文件必须在同一个包下
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
  <package name="org.mybatis.builder"/>
</mappers>

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

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

SqlSessionFactoryBuilder

  • 一旦创建了 SqlSessionFactory,就不再需要它了。
  • 局部变量

SqlSessionFactory

  • 想象为数据库连接池
  • SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例
  • 因此SqlSeesionFactory的最佳作用域是应用作用域
  • 最简单的就是使用单例模式或者静态模式

SqlSession

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

image

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

5、属性名字段名不一致的问题

5.1、问题

数据库中的字段

image

新建一个项目,拷贝之前的,测试实体类字段不一致的情况

private int id;
private String name;
private String password;

返回结果集:

User{id=1, name='张三', password='null'}
User{id=2, name='李四', password='null'}
User{id=3, name='王五', password='null'}
User{id=4, name='赵六', password='null'}
User{id=8, name='赵六', password='null'}

解决方法【暴力】

  • 起别名

    select id,name,pwd as password  from mybatis.user
    

5.2、ResultMap

结果集映射

  • resultMap 元素是 MyBatis 中最重要最强大的元素
  • ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
  • constructor用于在实例化类时,注入结果到构造方法中
    • idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能
    • arg - 将被注入到构造方法的一个普通结果
  • id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
  • result – 注入到字段或 JavaBean 属性的普通结果
  • association– 一个复杂类型的关联;许多结果将包装成这种类型
    • 嵌套结果映射 – 关联可以是 resultMap 元素,或是对其它结果映射的引用
  • collection– 一个复杂类型的集合
    • 嵌套结果映射 – 集合可以是 resultMap 元素,或是对其它结果映射的引用
  • discriminator– 使用结果值来决定使用哪个resultMap
    • case– 基于某些值的结果映射
      • 嵌套结果映射 – case 也是一个结果映射,因此具有相同的结构和元素;或者引用其它的结果映射
id name pwd
id name password
<resultMap id="UserMap" type="User">
<!--  column数据库中的字段,property实体类中的属性-->
<result  column="pwd" property="password"></result>
</resultMap>
<select id="getUserList" resultMap="UserMap" >
    select * from mybatis.user
</select>

结果:

User{id=1, name='张三', password='123456'}
User{id=2, name='李四', password='123456'}
User{id=3, name='王五', password='123456'}
User{id=4, name='赵六', password='123456'}
User{id=8, name='赵六', password='123456'}

6、日志

6.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中具体使用哪一个日志实现,在设置中设定!

STDOUT_LOGGING 标准日志实现

<!--    设置-->
<settings>
  <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

输出:

Opening JDBC Connection
Created connection 41031373.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@27216cd]
==>  Preparing: select * from mybatis.user
==> Parameters: 
<==    Columns: id, name, pwd
<==        Row: 1, 张三, 123456
<==        Row: 2, 李四, 123456
<==        Row: 3, 王五, 123456
<==        Row: 4, 赵六, 123456
<==        Row: 8, 赵六, 123456
<==      Total: 5
User{id=1, name='张三', password='123456'}
User{id=2, name='李四', password='123456'}
User{id=3, name='王五', password='123456'}
User{id=4, name='赵六', password='123456'}
User{id=8, name='赵六', password='123456'}
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@27216cd]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@27216cd]
Returned connection 41031373 to pool.

6.2、Log4j

什么是Log4j

  • Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件
  • 我们也可以控制每一条日志的输出格式
  • 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程
  • 通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
  1. 先导入log4j的包

    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    
  2. 写配置文件log4j.properties

    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/shun.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. Mybatis的配置文件中设置

    <!--    设置-->
    <settings>
      <setting name="logImpl" value="LOG4J"/>
    </settings>
    

简单使用

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

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

    static Logger logger=Logger.getLogger(UserMapperTest.class);
    
  3. 测试:

    @Test
    public void testLog4j(){
        logger.info("into:进入了Log4j");
        logger.debug("debug:进入了Log4j");
        logger.error("error:进入了Log4j");
    }
    
  4. 在log文件中查看

    [INFO][22-07-10][dao.UserMapperTest]into:进入了Log4j
    [DEBUG][22-07-10][dao.UserMapperTest]debug:进入了Log4j
    [ERROR][22-07-10][dao.UserMapperTest]error:进入了Log4j
    

7、分页

思考:为什么要分页?

  • 减少数据的处理量

使用Limit分页

语法:SELECT * FROM USER LIMIT startIndex,pagesize
SELECT * FROM USER LIMIT 3; [0,N]

使用Mybatis实现分页,核心SQL

  1. 接口

    //limit分页
    List<User> getUserByLimit(Map<String,Integer> map);
    
  2. Mapper.xml

    <!--    Limit分页-->
    <select id="getUserByLimit" parameterType="map" resultMap="UserMap">
      select * from mybatis.user limit #{startIndex},#{pageSize};
    </select>
    
  3. 测试

    /**
     * Limit分页
     */
    @Test
    public void testLimit(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        HashMap<String, Integer> map = new HashMap<>();
        map.put("startIndex",2);
        map.put("pageSize",3);
        List<User> userByLimit = mapper.getUserByLimit(map);
        for (User user : userByLimit) {
            System.out.println(user);
        }
        sqlSession.close();
    }
    

8、使用注解开发

8.1、面向接口编程

1.什么是面向接口编程

​ 面向接口编程就是先把客户的业务逻辑线提取出来,作为接口,业务具体实现通过该接口的实现类来完成。当客户需求变化时,只需编写该业务逻辑的新的实现类,通过更改配置文件(例如Spring框架)中该接口的实现类就可以完成需求,不需要改写现有代码,减少对系统的影响。

2.面向接口编程的优点

  1. 降低程序的耦合性。其能够最大限度的解耦,所谓解耦既是解耦合的意思,它和耦合相对。耦合就是联系 ,耦合越强,联系越紧密。在程序中紧密的联系并不是一件好的事情,因为两种事物之间联系越紧密,你更换 其中之一的难度就越大,扩展功能和debug的难度也就越大。
  2. 易于程序的扩展;
  3. 有利于程序的维护;

3.接口编程在设计模式中的体现:开闭原则

	其遵循的思想是:对扩展开放,对修改关闭。其恰恰就是遵循的是使用接口来实现。在使用面向接口的编程过程中,将具体逻辑与实现分开,减少了各个类之间的相互依赖,当各个类变化时,不需要对已经编写的系统进行改动,添加新的实现类就可以了,不在担心新改动的类对系统的其他模块造成影响。

8.2、使用注解开发

  1. 注解在接口上实现

    @Select("select * from user")
    List<User> getUsers();
    
  2. 核心配置文件中绑定接口

    <!--    绑定接口-->
    <mappers>
      <mapper class="com.dao.UserMapper"></mapper>
    </mappers>
    
  3. 测试使用

    本质:反射机制实现

    底层:动态代理

    @Test
    public void test(){
        SqlSession session = MybatisUtils.getSqlSession();
        //底层主要应用反射
        UserMapper mapper = session.getMapper(UserMapper.class);
        List<User> users = mapper.getUsers();
        for (User user : users) {
            System.out.println(user);
        }
        session.close();
    }
    

8.3、CRUD

我们可以在工具类创建的时候自动提交事务

public static SqlSession getSqlSession(){
    return sqlSessionFactory.openSession(true);
}
  1. 核心配置文件中绑定接口

    <!--    绑定接口-->
    <mappers>
      <mapper class="com.dao.UserMapper"></mapper>
    </mappers>
    
  2. 注解在接口上实现

    //方法存在多个参数,所有对参数前面都必须加上注解@Param("参数")
    @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);
    
  3. 测试CRUD

    static Logger logger=Logger.getLogger(UserMapperTest.class);
    /**
     * 测试注解查询
     */
    @Test
    public void testSelect(){
        SqlSession session = MybatisUtils.getSqlSession();
        //底层主要应用反射
        UserMapper mapper = session.getMapper(UserMapper.class);
        User userByID = mapper.getUserByID(1);
        System.out.println(userByID);
        session.close();
    }
    /**
     * 测试注解插入
     */
    @Test
    public void testAddUser(){
        SqlSession session = MybatisUtils.getSqlSession();
        //底层主要应用反射
        UserMapper mapper = session.getMapper(UserMapper.class);
        try{
            int state = mapper.addUser(new User(7, "钱七", "123456"));
            if (state>0){
                System.out.println("插入"+state+"条数据成功");
            }
        }catch (PersistenceException e){
            logger.error("主键重复");
            System.out.println("主键重复");
        }finally {
            session.close();
        }
    }
    /**
     * 测试注解修改
     */
    @Test
    public void testUpdateUser(){
        SqlSession session = MybatisUtils.getSqlSession();
        //底层主要应用反射
        UserMapper mapper = session.getMapper(UserMapper.class);
        try{
            User user = new User(7, "钱七", "123456");
            int state = mapper.upDateUser(user);
            if (state>0){
                System.out.println("修改:"+user.toString());
            }
        }catch (PersistenceException e){
            logger.error("更新失败");
            System.out.println("更新失败");
        }finally {
            session.close();
        }
    }
    /**
     * 测试注解删除
     */
    @Test
    public void testDeleteUser(){
        SqlSession session = MybatisUtils.getSqlSession();
        //底层主要应用反射
        UserMapper mapper = session.getMapper(UserMapper.class);
        try{
            int state = mapper.deleteUser(7);
            if (state>0){
                System.out.println("删除ID=7的用户");
            }
        }catch (PersistenceException e){
            logger.error("删除失败");
            System.out.println("删除失败");
        }finally {
            session.close();
        }
    }
    

关于@Param()注解

  • 基本类型的参数或者String类型,需要加上
  • 应用类型不需要加
  • 如果我们只有一个基本类型大的话,可以忽略,但是建议加上
  • 我们在SQL中引用的就是我们这里的@Param("uid")中设定的属性

关于#{}和#{}的区别

  • {}可以防止SQL注入

9、Lombok使用

Lombok是一个Java库,能自动插入编辑器并构建工具,简化Java开发。通过添加注解的方式,不需要为类编写getter或eques方法,同时可以自动化日志变量。

简而言之:Lombok能以简单的注解形式来简化java代码,提高开发人员的开发效率。

Maven导入包(注意:老板本IDEA中要下载插件)

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.8</version>
</dependency>
  • @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 =
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private String pwd;

}

10、多对一处理

10.1、预处理

  • 多个学生,对应一个老师
  • 对于学生这边而言,多个学生关联一个老师【多对一】
  • 对于老师而言,集合,一个老师有很多学生【一对多】
  • association– 一个复杂类型的关联;许多结果将包装成这种类型
    • 嵌套结果映射 – 关联可以是 resultMap 元素,或是对其它结果映射的引用
  • collection– 一个复杂类型的集合
    • 嵌套结果映射 – 集合可以是 resultMap 元素,或是对其它结果映射的引用

SQL:

USE mybatis;
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');

测试环境搭建:

  1. 导入log4j
  2. 新建实体类Teacher,Student
  3. 建立Mapper接口
  4. 建立Mapper文件
  5. 在核心配置文件中绑定接口「方式很多」
  6. 测试查询
public class TestMyBatis {
    public static void main(String[] args) {
        SqlSession session = MybatisUtils.getSqlSession();
        TeacherMapper mapper = session.getMapper(TeacherMapper.class);
        Teacher teacher = mapper.getTeacher(1);
        System.out.println(teacher.toString());
        session.close();
    }
}

10.2、按照查询嵌套处理

Pojo:

Student

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    private int id;
    private String name;
    //学生需要关联一个老师
    private Teacher teacher;
}

Teacher

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
    private int id;
    private String name;
}

TeacherMapper的接口

public interface TeacherMapper {
    @Select("select * from teacher")
    Teacher getTeacher();
}

StudentMapper的接口

public interface StudentMapper {
    public List<Student> getStudent();
}

StudentMapper的XML映射

<mapper namespace="com.dao.StudentMapper">
    <select id="getStudent" resultMap="mapStudent">
            select * from mybatis.student
    </select>
    <resultMap id="mapStudent" type="Student">
        <result column="id" property="id"></result>
        <result column="name" property="name"></result>
        <association
                property="teacher" column="tid" javaType="Teacher" select="getTeacher">
        </association>
    </resultMap>
    <select id="getTeacher" resultType="Teacher">
        select * from mybatis.teacher 
    </select>
</mapper>

返回结果集:

Student(id=1, name=小明, teacher=Teacher(id=1, name=秦老师))
Student(id=2, name=小红, teacher=Teacher(id=1, name=秦老师))
Student(id=3, name=小张, teacher=Teacher(id=1, name=秦老师))
Student(id=4, name=小李, teacher=Teacher(id=1, name=秦老师))
Student(id=5, name=小王, teacher=Teacher(id=1, name=秦老师))

10.3、按照结果嵌套处理

StudentMapper的XML映射

<!--    方式二:按照结果嵌套处理-->
<mapper namespace="com.dao.StudentMapper">
    <select id="getStudent" resultMap="mapStudent">
        select  s.id sid,s.name sname,t.name tname
        from student s,teacher t
        where s.tid =t.id;
    </select>
    <resultMap id="mapStudent" type="Student">
        <result column="sid" property="id"></result>
        <result column="sname" property="name"></result>
        <association property="teacher" javaType="Teacher">
            <result property="name" column="tname"></result>
        </association>
    </resultMap>
</mapper>

返回结果集

Student(id=1, name=小明, teacher=Teacher(id=0, name=秦老师))
Student(id=2, name=小红, teacher=Teacher(id=0, name=秦老师))
Student(id=3, name=小张, teacher=Teacher(id=0, name=秦老师))
Student(id=4, name=小李, teacher=Teacher(id=0, name=秦老师))
Student(id=5, name=小王, teacher=Teacher(id=0, name=秦老师))

回顾MySQL多对一的方法

  • 子查询
  • 联表查询

11、多对一处理

11.1、预处理

比如:一个老师拥有多个学生

对于老师而言就是一对多的关系

  1. 环境搭建,和刚刚一样

  2. 实体类

    Student

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Student {
        private int id;
        private String name;
        private int tid;
    }
    

    Teacher

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

11.2、按照结果嵌套处理

Teacher接口

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

Mapper的XML映射

<mapper namespace="com.dao.TeacherMapper">
<!--    按照结果进行查询-->
    <select id="getTeacher01" resultMap="TeacherMap01">
        select s.id sid,s.name sname,t.name tname,t.id tid
        from mybatis.student s,mybatis.teacher t
        where s.tid =t.id and t.id=#{tid}
    </select>
    <resultMap id="TeacherMap01" type="Teacher">
        <result column="tid" property="id"></result>
        <result column="tname" property="name"></result>
<!--        javaType 指定的类型,集合中的泛型,我们使用ofType-->
        <collection property="students" ofType="Student">
            <result property="id" column="sid"></result>
            <result property="name" column="sname"></result>
            <result property="tid" column="tid"></result>
        </collection>
    </resultMap>
</mapper>

测试:

public class TestMyBatis {
    @Test
    public void testStudent(){
        SqlSession session = MybatisUtils.getSqlSession();
        TeacherMapper mapper = session.getMapper(TeacherMapper.class);
        Teacher teacher = mapper.getTeacher01(1);
        System.out.println(teacher.toString());
        session.close();
    }
}
eacher(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)])

11.3、按照查询嵌套处理

Teacher接口

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

Mapper的XML映射

<mapper namespace="com.dao.TeacherMapper">
   <select id="getTeacher02" resultMap="TeacherMap02">
        select * from mybatis.teacher where id=#{tid}
    </select>
    <resultMap id="TeacherMap02" type="Teacher">
        <collection property="students" column="id" javaType="ArrayList" ofType="Student" select="teacherByStudent"></collection>
    </resultMap>
    <select id="teacherByStudent" resultType="Student">
        select * from mybatis.student where tid =#{id}
    </select>
</mapper>

测试:

public class TestMyBatis {
    @Test
    public void testStudent(){
        SqlSession session = MybatisUtils.getSqlSession();
        TeacherMapper mapper = session.getMapper(TeacherMapper.class);
        Teacher teacher = mapper.getTeacher02(1);
        System.out.println(teacher.toString());
        session.close();
    }
}
Teacher(id=0, 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)])

11.4、小结

  1. 关联-association【多对一】
  2. 集合-collection【一对多】
  3. javaType & ofType
    1. javaType:指定实体类中的属性类型
    2. ofType用来指定映射到List或者集合中的pojo类型,泛型中的约束类型

注意点

  • 保证SQL的可读性,尽量保证通俗易懂
  • 注意一对多和多对一中的属性名和字段问题
  • 如果问题不好排查错误,建议使用Log4j

12、动态SQL

什么是动态SQL:动态SQL就是根据不同的学生条件生成不同的SQL语句

如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。

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

12.1、环境搭建

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;

创建一个基础工程

  1. 导包

     <!--项目依赖-->
        <dependencies>
            <!--    具体依赖的的jar包配置文件-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.11</version>
                <scope>test</scope>
            </dependency>
            <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.5.10</version>
            </dependency>
                <dependency>
                    <groupId>mysql</groupId>
                   <artifactId>mysql-connector-java</artifactId>
                    <version>8.0.29</version>
                  </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.13.2</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.17</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.8</version>
            </dependency>
        </dependencies>
    <!--    在build中配置resources,来防止我们资源导出失败的问题-->
        <build>
            <resources>
                <resource>
                    <directory>src/main/resources</directory>
                    <includes>
                        <include>**/*.properties</include>
                        <include>**/*.xml</include>
                    </includes>
                    <filtering>true</filtering>
                </resource>
                <resource>
                    <directory>src/main/java</directory>
                    <includes>
                        <include>**/*.properties</include>
                        <include>**/*.xml</include>
                    </includes>
                    <filtering>true</filtering>
                </resource>
            </resources>
        </build>
    
  2. 编写配置文件

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
    <!--    引入外部配置文件-->
        <properties resource="db.properties"></properties>
        <settings>
    <!--        驼峰命名自动转换-->
            <setting name="mapUnderscoreToCamelCase" value="true"/>
        </settings>
    <!--    起别名-->
        <typeAliases>
            <package name="com.pojo"/>
        </typeAliases>
        <environments default="test">
            <environment id="test">
                <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 class="com.dao.BlogMapper"></mapper>
        </mappers>
    </configuration>
    
  3. 编写实体类

    import java.util.Date;
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Blog {
        private String id;
        private String title;
        private String author;
        private Date createTime;
        private int views;
    }
    
  4. 编写实体类对于的Mapper接口和Mapper.XML

    public interface BlogMapper {
          //插入数据
        int addBlog(Blog blog);
    }
    
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.dao.BlogMapper">
        <insert id="addBlog" parameterType="blog">
            insert into mybatis.blog(id, title, author, create_time, views) VALUES(#{id},#{title},#{author},#{createTime},#{views});
        </insert>
    </mapper>
    
  5. 插入数据

    /**
    * 插入数据
    */
    @Test
    public void addTest(){
      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(6000);
    
      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);
    
      sqlSession.close();
    }
    

12.2、IF

Mapper接口

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

Mapper.XML

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

测试类:

/**
 * IF查询
 */
@Test
public void queryBlogIFTest(){
    SqlSession session = MyBatisUtils.getSqlSession();
    BlogMapper mapper = session.getMapper(BlogMapper.class);
    HashMap hashMap = new HashMap();
    hashMap.put("title","Mybatis");
    hashMap.put("author","匿名");
    for (Blog blog : mapper.queryBlogIF(hashMap)) {
        System.out.println(blog);
    }
    session.close();
}

结果:

Blog(id=ec54648442bd4af49bf3ee9c57a1502a, title=Mybatis, author=匿名, createTime=Tue Jul 12 11:29:25 CST 2022, views=6000)

12.3、Choose

when,otherwise

类似于switch case default

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

12.4、Trim

where,set

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

<select id="queryBlogChoose" parameterType="map" resultType="blog">
    select * from mybatis.blog
    <where>
     ……
    </where>
</select>

set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)

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

如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ...
</trim>

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

12.5、Foreach

动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。比如:

<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM POST P
  WHERE ID in
  <foreach item="item" index="index" collection="list"
      open="(" separator="," close=")">
        #{item}
  </foreach>
</select>

foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!

动态SQL就是在拼接SQL,我们只要保证SQL的正确性,按照SQL的方式,去排列组合就可以了

建议:

  • 先在MySQL中写出完整的SQL,再对应的去修改成为我们的动态SQL实现通用即可

13、缓存

13.1、简介

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

13.2、Mybatis缓存

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

13.3、一级缓存

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

测试:

@Test
public void queryUserByIdTest(){
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User users01 = mapper.queryUserById(1);
    User users02 = mapper.queryUserById(1);
    System.out.println(users01);
    System.out.println(users02);
    System.out.println(users01 == users02);
    sqlSession.close();
}

输出

User(id=1, name=张三, pwd=123456)
User(id=1, name=张三, pwd=123456)
true

日志:

==>  Preparing: select * from mybatis.user where id=?
==> Parameters: 1(Integer)
<==    Columns: id, name, pwd
<==        Row: 1, 张三, 123456
<==      Total: 1

结论:完全一样

增删改可以清理缓存

手动清理缓存:

sqlSession.clearCache();

13.4、二级缓存

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

核心配置文件中配置,虽然默认是是true,但是我们最好还是显示的开启

设置名 描述 有效值 默认值
cacheEnabled 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 true | false true

cache 标签有多个属性

  • LRU - 最近最少回收,移除最长时间不被使用的对象
  • FIFO - 先进先出,按照缓存进入的顺序来移除它们
  • SOFT - 软引用,移除基于垃圾回收器状态和软引用规则的对象
  • WEAK - 弱引用,更积极的移除基于垃圾收集器和弱引用规则的对象

需要在 Mapper 对应的xml中添加 cache 标签

<!-- 表示DEPT表查询结果保存到二级缓存(共享缓存) -->
<cache/>

这些属性可以通过 cache 元素的属性来修改。比如:

<cache
  eviction="FIFO"
  flushInterval="60000"
  size="512"
  readOnly="true"/>

另外对单独的查询可以添加关闭二级查询

<select id="queryUserById" parameterType="_int" resultType="User" useCache="false">
  select * from mybatis.user where id=#{id}
</select>
posted @   项sir  阅读(25)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
历史上的今天:
2021-07-12 Hello World!
2021-07-12 安装与卸载JDK
2021-07-12 金融市场体系-全球金融市场
XIANGSIR
点击右上角即可分享
微信分享提示