Mybatis笔记

1、认识Mybatis

1.1 Mybatis简介

1、什么是mybatis??

  • 1)Mybatis是一款优秀的持久层框架,它支持自定义SQL、存储过程以及高级映射;
  • 2)几乎免除了所有的JDBC代码以及设置参数和获取结果集的操作;
  • 3)可以通过简单的XML或注解来配置和映射原始类型、接口和Java类(POJO)为数据库中的记录

2、什么是ORM??

  1. ORM(Object Relational Mapping)对象关系映射:为了解决面向对象与关系数据库存在的互补匹配的技术,即将Java程序中的对象以一种映射方式保存到数据库中。

  2. 对应关系如下所示:

    面向对象概念面向关系概念
    对象表的行(记录)
    属性列(字段)
  3. ORM框架的特点

    • 把对数据库的操作封装成一套API;
    • 具有操作数据库的CRUD操作;
    • 具有独立性,当持久层发生改变时,不用修改任何业务代码
  4. 当前的ORM框架

    • JPA 本身是一种ORM规范,由各大框架提供实现
    • Hibernate
    • Mybatis

3、为什么说mybatis是半自动化的ORM映射工具,它与全自动的有什么区别??

​ mybatis在查询关联对象或关联集合对象时,需要手动编写sql来实现。

4、传统的JDBC开发存在的问题以及Mybatis中的解决方法

  • ① 频繁创建数据库连接对象、释放,容易造成系统资源的浪费,影响系统性能。 ==> 使用连接池解决
    • 在mybatis-config中配置数据库连接池,使用数据库连接池管理连接对象
  • ② sql语句定义、参数设置、结果集处理存在硬编码(写死的参数、sql语句)。
    • 将sql语句配置在XxxMapper.xml配置文件中,与Java代码分离。
  • ③ 结果集处理存在重复代码,处理麻烦。映射成Java对象比较方便
    • Mybatis自动将Java对象映射成sql语句
  • ④ 使用preparedStatement向占有位符号传参数时存在硬编码,因为sql语句的where条件不一样,可能多也可能少,修改sql还要修改代码,系统不易维护。
    • mybatis自动将sql语句执行结果转换为Java对象。

1.2 持久化

1、持久化是程序数据在持久状态和瞬时状态转换的机制

  • ① 即把数据保存到可永久存储的存储设备中;
  • ② 主要应用:将对象保存在
    • 数据库
    • 磁盘文件
    • xml数据文件
  • ③ 机制:JDBC、文件IO

2、为什么需要持久化??

  • 内存断电即失;
  • 内存价格昂贵

1.3 JDBC介绍

1.3.1 简介

1、概念

Java DataBase Connectivity:Java数据库连接

2、本质

定义了操作所有关系型数据库的规则(接口),每个数据库厂商需要去实现这些接口,提供数据库驱动Jar包。我们可以使用这些接口编程,真正执行的代码是驱动JAR包中的实现类。

3、步骤

  1. 导入驱动jar包
  2. 编写代码注册驱动
  3. 获取数据库连接对象Connection
  4. 定义Sql语句
  5. 获取执行Sql语句的对象Statement
  6. 执行Sql语句,接收返回结果
  7. 处理结果
  8. 释放连接资源

1.3.2 解释需要使用的类

1、DriverManager驱动管理对象。

功能:

  • 1)注册驱动
    • static void registerDriver(Driver driver):注册给定的驱动程序。
    • mysql5 之后可以不用写注册驱动
  • 2)获取数据库连接
    • static Connection getConnect(String url, String user, String password)
    • url:jdbc:mysql://ip:端口号/数据库名称
    • user:用户名
    • password:用户密码
2、Connection接口:数据库管理对象
  • 1)获取执行SQL的方法
    • Statement createStatemnet()
    • PreparedStatemrnt preparedStatement(String sql)
  • 2)管理事务
    • 开启:void setAutoCommit(boolean autoCommit):false为开启自动提交
    • 提交:void commit()
    • 回滚:void rollback()
3、Statement接口:用于执行静态SQL(参数是给定的)
  • boolean execute(String sql):任何SQL语句

  • int executeUpdate(String sql):执行DML语句

    • DML:insert、update、delete

    • 返回值:影响的行数。可以通过返回值判断是否成功

      • 大于0:成功
      • 否则失败
4、ResultSet:结果集对象

功能:封装查询结果集

ResultSet executeQuery(String sql) throws SQLException;  //执行DQL语句

方法:

  • boolean next():游标(索引)向下移动一行
  • getXxx(Param par):获取数据,一次只能获取某一行的某一列
    • 参数:
      • int 代表列表的编号,从1开始。 getString(1)获取第一列
      • String 代表列名称 getString(“name”):获取name这一列

遍历的步骤:

  1. 游标向下移动一行,游标初始为-1
  2. 判断是否有数据
  3. 获取数据
5、PreparedStatement:执行sql

功能:使用该对象完成CRUD操作

1)sql注入问题:在拼接sql时,有一些sql关键字参与拼接,容易造成安全问题

2)解决sql注入问题:使用预编译,在写sql语句时参数使用”?“占位符,执行sql时,给"?"注入值

  • 预编译: SQL 预编译指的是数据库驱动在发送 SQL 语句和参数给 DBMS 之前对 SQL 语句进行编译,这样 DBMS 执行 SQL 时,就不需要重新编译。
select * from user where name=? and password=?

3)方法:prepareStatement(String sql)

4)给参数赋值,使用setXxx方法。

setName(1,"Mike");
    //1:表示?的位置,从1开始
    //Mike:?的值
setPassword(2,"123456");

2、第一个Mybatis程序

2.1 Mybatis的编程步骤

  1. 通过sqlSessionFactoryBuilder.build(inputStream)构建SqlSessionFactory
  2. 通过sqlSessionFactory.openSqlSession()方法获取sqlSession连接对象/实例
  3. 通过sqlSession执行数据库操作
  4. 调用sqlSession.commit()方法提交事务
  5. 调用sqlSession.close()方法关闭连接对象

2.2 程序步骤

  1. 搭建数据库并编写相应的数据库连接数据源db.properties

    #连接数据库
    #mysql 5的driver=com.mysql.jdbc.Driver
    #mysql 8以上的驱动器如下
    driver=com.mysql.cj.jdbc.Driver  
    url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=utf8
    #serverTimezone=UTC 时区
    #useUnicode=true     使用Unicode编码
    #characterEncoding=utf8 使用utf8编码
    username=root
    password=xxxxxx
    
  2. 导入mybatis的jar包

    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.4</version>
    </dependency>
    
  3. 编写mybatis的核心配置文件mybatis-config.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <!-- 导入外部的数据库连接的配置文件 -->
        <properties resource="db.properties"/>
    
        <!-- 给声明的类起别名 -->
        <typeAliases>
            <typeAlias type="com.wei.pojo.User" alias="User"/>
        </typeAliases>
    
        <!-- 数据库环境 -->
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"></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>
    	<!--transactionManager:事务管理器 
    				JDBC
    				MANAGED:让容器管理对象的声明周期
    		dataSource:数据源
    			unpooled:没有使用到“池的概念”
    			pooled:利用“池”,复用数据库连接对象
    			JNDI:应用在EJB或应用服务器这类容器中
    	-->
        <mappers>
            <!--命名空间,类似包的概念,绑定一个对应的Dao|Mapper接口 -->
            <mapper resource="com/wei/mapper/UserMapper.xml"/>
        </mappers>
    </configuration>
    
  4. 编写mybatis工具类 MybatisUtils

    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 {
                /*根据核心配置文件获取 会话工厂
                 每一个数据库对应一个SqlSessionFactory实例,需要的连接对象由这个会话工厂产*/
                String resource = "mybatis-config.xml";  
                InputStream inputStream = Resources.getResourceAsStream(resource);
                //build()方法不是静态方法,所以需要创建一个实例进行调用
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        /*获取sqlSession对象
        SqlSession:线程不安全的,类似Connection,提供操作数据库的CRUD方法*/
        public static SqlSession getSqlSession(){
            return sqlSessionFactory.openSession();
        }
    }
    
  5. 创建实体类Pojo

  6. 编写mapper接口,实现CRUD操作

    public interface UserMapper {
        /** 根据用户id获取用户信息
         * @param id
         * @return 若存在,则返回用户详细信息
         */
        User getUserById(@Param("id")Integer id);
        //使用@Param注解给参数命名,在sql语句中直接通过 #{id}获取该参数信息
    }
    
  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">
    
    <!-- namespace标准命名空间一定要写,不写就会报错 -->
    <mapper namespace="com.wei.dao.UserMapper">
        <select id="getUserById" parameterType="int" resultType="User">
            select id,username,password from user where id=#{id}
        </select>
    </mapper>
    
  8. 编写测试类

    public void testGetUserById() {
        //获取连接对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        System.out.println(mapper.getUserById(1));
        sqlSession.close();  //关闭连接
    }
    

2.3 添加日志文件

1、在mybatis.xml中添加日志配置

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

2、在pom.xml主配置文件中添加依赖

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

3、编写日志打印输出数据源

#日志文件由三部分组成
#root :设置默认的日志输出级别和风格。
#appender :可以把日志输出到控制台或文件中去。
#logger :设置自定义日志级别和风格。

#将等级为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/wei.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

控制台日志输出

[org.apache.ibatis.logging.LogFactory]-Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
[org.apache.ibatis.logging.LogFactory]-Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.

[org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
[org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
[org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
[org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.

//开启JDBC数据库连接池
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection  
//创造连接对象connection
[org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 282821294.
//sql语句的预编译
[com.wei.dao.UserMapper.getUserById]-==>  Preparing: select id,username,password from user where id=? 
//传入的参数
[com.wei.dao.UserMapper.getUserById]-==> Parameters: 1(Integer)
//获取的结果条数
[com.wei.dao.UserMapper.getUserById]-<==      Total: 1
//结果详细信息
User(id=1, username=小华, password=xiaohua123)
//关闭数据库连接
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@10db82ae]
//将连接对象返回给数据库连接池
[org.apache.ibatis.datasource.pooled.PooledDataSource]-Returned connection 282821294 to pool.

日志输出到文件

[DEBUG][21-01-06][org.apache.ibatis.logging.LogFactory]Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.

[DEBUG][21-01-06][org.apache.ibatis.logging.LogFactory]Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
[DEBUG][21-01-06][org.apache.ibatis.datasource.pooled.PooledDataSource]PooledDataSource forcefully closed/removed all connections.

2.4 日志介绍

1、作用:

  • 出错时可以根据输出的SQL语句快速排查问题;
  • 将日志与代码隔离 <<==>> 日志框架的作用

2、Mybatis的内置日志工厂有:

  • SLF4J
  • Apache Commons Logging
  • Log4j2
  • Log4j
  • JDK Logging

3、日志级别

ERROR > WARN > INFO > DEBUG > TRACE

  1. 例如设置日志级别为INFO后,只有优先级高于或等于INFO的日志信息才能被输出;
  2. 日志级别越低,输出的日志越详细。

4、日志的组成:

日志框架简图

5、使用

  1. 在pom.xml中导入日志依赖

    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    
  2. 在全局配置文件中配置

    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>
    
  3. 定义日志文件log.properties(如2.3中编写日志文件)

3、Mybatis原理

3.1 Mybatis核心组件

  • SqlSessionFactoryBuilder构建器:创建SqlSessionFactory对象
  • SqlSessionFactory会话工厂:创建SqlSession会话对象
  • SqlSession会话对象:线程不安全的,类似connection对象。提供操作数据库的CRUD方法,可以调用操作方法环绕操作Mapper组件。
  • Excutor执行器:SqlSession本身不能操作数据库,需要Excutor完成,其提供两个接口
    • 缓存执行器(缺省)
    • 基本执行器
  • MappedStatement:映射语句封装执行语句时的信息,如SQL、输入参数、输出结果等

Mybatis有三种基本的执行器:

  1. SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭该对象
  2. ResueExecutor:重复使用Statement对象。执行update或select,以sql作为key查找Statement对象,如果存在就使用,不存在就新建一个Statement对象,用完后,不需要关闭该对象,而是将其存放在Map<String,Statement>中。
  3. BatchExecutor:批处理执行器。执行update(不执行select,JDBC批处理不支持select),将所有的sql都添加到批处理中(addBatch()),等待统一执行executeBatch(),它缓存了多个Statement对象,每个Statement对象都是addBatch()添加完毕后,等待逐一执行executeBatch()批处理。

【注】作用范围: Executor的这些特点,都严格限制在SqlSession生命周期范围内。

Mybatis中如何指定使用哪一种Executor执行器?

​ 在Mybatis配置文件中,在设置(settings)可以指定默认的ExecutorType执行器类型,也可以手动给DefaultSqlSessionFactory的创建SqlSession的方法传递ExecutorType类型参数,如SqlSession openSession(ExecutorType execType)。

​ 配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements); BATCH 执行器将重用语句并执行批量更新。

SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
//可以执行批量操作的sqlSession
SqlSession openSession = sqlSessionFactory.openSession(ExecutorType.BATCH);

3.2 执行原理

在这里插入图片描述

  • 1)读取 MyBatis 全局配置文件:mybatis-config.xml 为 MyBatis 的全局配置文件,配置了 MyBatis 的运行环境等信息,例如数据库连接信息。Configuration类
  • 2)加载映射文件Mapper.xml。映射文件即 SQL 映射文件,该文件中配置了操作数据库的 SQL 语句,需要在 MyBatis 配置文件 mybatis-config.xml 中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。
  • 3)构造会话工厂:通过 MyBatis 的环境等配置信息构建会话工厂 SqlSessionFactory。
  • 4)创建会话对象:由会话工厂创建 SqlSession 对象,该对象中包含了执行 SQL 语句的所有方法。
  • 5)Executor 执行器:MyBatis 底层定义了一个 Executor 接口来操作数据库,它将根据 SqlSession 传递的参数动态地生成需要执行的 SQL 语句,同时负责查询缓存的维护。
  • 6)MappedStatement 对象:在 Executor 接口的执行方法中有一个 MappedStatement 类型的参数,该参数是对映射信息的封装,用于存储要映射的 SQL 语句的 id、参数等信息。
  • 7)输入参数映射:输入参数类型可以是 Map、List 等集合类型,也可以是基本数据类型和 POJO 类型。输入参数映射过程类似于 JDBC 对 preparedStatement 对象设置参数的过程。
  • 8)输出结果映射:输出结果类型可以是 Map、 List 等集合类型,也可以是基本数据类型和 POJO 类型。输出结果映射过程类似于 JDBC 对结果集的解析过程。

4、Mapper映射器

目的:找sql语句

位置:mybatis-config.xml中的标签

方式:告诉Mybatis去哪里找映射文件

<!--1、使用相对路径的资源使用 **-->
<mapper resource="com/wei/dao/XxxMapper.xml"/>

<!--2、使用完全限定资源定位符(url):文件的路径 -->
<mapper url=".../XxxMapper.xml"/>

<!--3、使用映射器接口实现类的完全限定类名 -->
<mapper class="com.wei.dao.XxxMapper"/>

<!--4、将包的映射器接口全部注册为映射器  **-->
<mapper name="com.wei.dao"/>

#{ } 和 ${} 的区别???

<select id="getUserById" parameterType="int" resultType="User">
    select id,username,password from user where id=#{id}
</select>
  • #{}是占位符,预编译处理;${}是拼接符,字符串替换,没有预编译处理;
  • Mybatis在处理#{}时,是以字符串为参数传入,将SQL中的#{}替换为"?",调用PreparedStatement的set方法来赋值;
  • Mybatis在处理$${}时,是原值传入。就是把 ${}替换成变量的值,相当于JDBC中的Statement方法;
  • 变量替换后,#{}对应的变量会自动加上单引号’ ',而${}对应的变量不会自动加上单引号;
  • #{}可以有效的防止SQL注入,提高系统的安全性;${}不能防止SQL注入;
  • #{} 的变量替换是在DBMS 中;${} 的变量替换是在 DBMS 外

5、核心配置文件

5.1 属性properties

<!-- 导入数据库连接的配置文件 -->
<properties resource="db.properties"/>

若属性在不止一个地方进行了配置,那么按以下顺序进行加载:

  1. 首先读取在properties元素体内指定的属性;
  2. 然后根据 properties 元素中的 resource 属性读取类路径下属性文件,或根据 url 属性指定的路径读取属性文件,并覆盖之前读取过的同名属性。
  3. 最后读取作为方法参数传递的属性,并覆盖之前读取过的同名属性。

因此,通过方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,最低优先级的则是 properties 元素中指定的属性。

5.2 设置setting

使用频率高的:

  1. 懒加载:lazyLoadingEnabled,默认情况下为false,表示不开启

    <setting name="lazyLoadingEnabled" value="true">
    
  2. 日志实现:logImpl,指定mybatis所用日志的具体实现,未指定则自动查找

    <setting name="logImpl" value="LOG4J">
    
  3. 缓存开启关闭:localCacheScope,默认为session,会缓存一个会话中执行的所有查询(一级缓存)

    <setting name="localCacheScope" value="SESSION"/>
    
  4. 驼峰命名:mapUnderscoreToCameCase,是否开启驼峰命名。这属于自动映射,即从A_COLUMN到Java属性名中的 aColumn属性,默认不开启(false)。解决属性名和数据表中列名不一致的一种方法。

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

5.3 类型别名typeAliases

类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。

<!-- 起别名 -->
<typeAliases>
    <typeAlias type="com.wei.pojo.User" alias="User"/>
</typeAliases>
<!-- 当这样配置的时候,User可以使用在com.wei.dao.User的任何地方 -->

<!--也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean -->
<typeAliases>
  <package name="com.wei.dao"/>
</typeAliases>

【注】在注解的情况下,使用类的小写作为其别名

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

6、ResultMap结果集映射

6.1 ResultMap

1、在编写XxxMapper中的sql语句可能出现的问题:字段名和属性名不一致

  1. 解决方法一:可以使用给sql语句中字段名起别名的方式(自动映射)

    select password as passwd from user
    -- password 为字段名
    -- passwd   为属性名
    -- 自动映射的方式,mybatis会自动寻找实体类Pojo中的setXxx()方法,如果没有找到setPasswd()方法就会返回null
    
  2. 解决方法二:使用ResultMap集合

2、ResultMap设计思想:对简单的语句做到零配置,对关系复杂的语句,只需要描述它们之间的关系就行。

3、使用(手动映射):

<!-- 1.显示定义一个外部的ResultMap -->
<resultMap id="mapName" type="User">
    <!--type=“全限定类名或别名” ,该属性表示把结果集中的每一行数据封装为什么类型的对象
		id表示主键
			property:实体类的属性名
			column:字段名
		result 非主键
-->
    <id property="id" column="id"/> 
    <result property="username" column="username"/>
    <result property="passwd" column="password"/>
</resultMap>

<!--2. 在CRUD标签中引用外部的ResultMap -->
<select id="getUserById" resultMap="mapName" parameterType="int">
    select id, username, password from user where id = #{id}
</select>
	<!--resultMap:引用外部ResultMap
		parameterType:需要传入的参数的类型
	注意:sql语句后面不能加上分号 ";",不然会报错		
	-->

6.2 多对一处理

关系在多的一方,以多的一方为主导关联一的一方,使用association。针对单属性集合,通常直接使用内联查询;配置单一元素(非数组、集合)的关联对象。

@Data
public class Student {
    private Integer id;
    private String name;
    private Integer tid;

    private Teacher teacher;  //多个学生可以对应一个老师,即多对一
}

@Data
public class Teacher {
    private Integer id;
    private String name;
}

属性:

  • property:关联对象的属性名
  • javaType:关联对象的属性类型
  • select:发送额外的SQL
  • column:指定列的值,传递给SQL语句
<mapper namespace="com.wei.dao.StudentMapper">

    <!--  需求:获取所有学生及其对应老师的信息
       思路:
       1. 获取所有学生的信息
       2. 根据获取的学生信息的老师ID->获取该老师的信息
       3. 思考问题,这样学生的结果集中应该包含老师,该如何处理呢,数据库中我们一般使用关联查询?
           1. 做一个结果集映射:StudentTeacher
           2. StudentTeacher结果集的类型为 Student
           3. 学生中老师的属性为teacher,对应数据库中为tid。
              多个 [1,...)学生关联一个老师=> 一对一,一对多
           4. 查看官网找到:association – 一个复杂类型的关联;使用它来处理关联查询
     -->
    <select id="getStudents" resultMap="StudentAndTeacher">
        select * from student;
    </select>
    <resultMap id="StudentAndTeacher" type="Student">
        <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
    </resultMap>

    <select id="getTeacher" resultType="Teacher">
        select id,name from teacher where id= #{id};
    </select>

    <!-- 按照查询结果处理 *** -->
    <select id="getStudents2" resultMap="StudentAndT">
        select s.id sid,
                s.name sname,
                t.name tname
        from student s, teacher t
        where s.tid = t.id
    </select>
    <resultMap id="StudentAndT" type="Student">
        <id property="id" column="sid"/>
        <result property="name" column="sname"/>
        <association property="teacher" javaType="Teacher">
            <result property="name" column="tname"/>
        </association>

    </resultMap>
</mapper>

6.3 一对多处理

  • 关系在一的一方。
  • 需要使用集合类型的关联属性,使用标签collection。
  • 通常使用延迟加载,也就是额外的SQL处理。

延迟加载的原理:使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。这就是延迟加载的基本原理。

<collection property="students" javaType="ArrayList" ofType="Student" column="id" select="getStudentsByTeacher" />

<select id="getStudentsByTeacher" resultType="Student">
	select * from student where tid=#{id}
</select>

<!--ofType:指定映射到List集合属性中POJO的类型
    javaType:一个Java类的全限定名,或一个类型别名
    column:数据库中的列名,或者是别名
    select:用于加载复杂类型属性的映射语句的ID,它会从column属性中指定的列检索数据,作为参数传递给此select语句。
-->

7、分页

为什么需要分页??

对数据库操作最频繁的动作是查询,若查询大量数据时,用分页能够每次处理小部分数据,对数据库的压力就可以控制在可控的范围内。

实现方式

  1. 使用limit语句(在SQL层面)

    • 语法:select * from table limit startIndex, pageSize
    • 实例 select * from table limit 5, 10 //检索记录行6到15
      • 检索从某个偏移量到记录集的结束的所有语句,可以指定第二个参数为 -1;
      • select * from table limit n //检索前n个记录
    • 公式:起始位置 = (当前页面 - 1)* 页面大小
  2. RowBounds分页

    • 在Java代码层实现

    • Mybatis使用RowBounds对象进行分页,它是针对ResultSet结果集执行的内存分页,而非物理分页。

    • 在测试类中:

      RowBounds rowBounds = new RowBounds((curPage - 1) * pageSize, pageSize);
      
    • 通过session.xxx()方法传递rowBounds

    //SqlSession接口
    <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds);
    
    <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds);
    
    <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds);
    

8、注解开发

面向接口编程

1、根本原因:

  • 解耦,可扩展,提高复用;
  • 在分层开发中,上层不用管具体的实现,大家都遵守共同的约定 / 标准,使得开发变得容易,规范性更好。

2、思想:

​ 系统设计的关键,各个对象之间的协作关系。小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都要着重考虑。这也是系统设计的主要工作内容。

3、接口:是定义(规范、约束)与实现的分离(解耦)。

注解开发

1、Sql类型的主要分类(CRUD)

  • @select:查询
  • @update:更新
  • @insert:插入
  • @delete:删除

2、不再需要mapper.xml映射文件

3、使用:

  • ① 在接口中添加注解

    public interface UserMapper {
        @Select("select * from user where id=#{id}")
        User getUserById(@Param("id")Integer id);
    }
    
  • ② 在mybatis的核心配置文件中注入

    <mapper class="com.wei.dao.UserMapper" />
    
  • ③ 测试

    @Test
    public void getUserById() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = mapper.getUserById(2);
        System.out.println(user);
        sqlSession.close();
    }
    /*结果
    User(id=2, username=小明, password=xiaoming123)
    */
    

4、本质:利用JVM的动态代理机制

9、动态SQL

1、指的是根据不同的查询条件,生成不同的SQL语句

2、包括四个关键字:

  • if 条件判断
  • choose(when,otherwise):类似switch,其中when类似case,otherwise类似default
  • trim(where,set)
  • foreach

3、SQL片段:当某个sql语句使用很多的时候,为了增加代码的重用性,简化代码,需要将这些代码抽取出来,然后直接使用。

  • 提取

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

    <select id="queryBlog" paramterType="map" resultType="map">
        select * from blog
        <where>
            <include refid="if-test-author"></include>
            <!--还可以继续引用其他sql片段 -->
        </where>
    </select>
    

  1. 最好是基于单表定义sql片段,提高片段的可重用性;
  2. 在sql片段中不要包括where

4、foreach关键字的使用

<foreach collection="ids" item="id" open="and(" close=")" separator="or">
    id=#{id}
</foreach>
  • collection:指定输入对象中的集合属性
  • item:每次遍历生成的对象
  • open:开始遍历时的拼接字符串
  • close:结束遍历时的拼接字符串
  • separator:遍历对象之间需要拼接的字符串

10、缓存Cache

什么是缓存??

  • 存在内存中的临时数据;
  • 将用户经常查询的数据放在缓存中,不用再去磁盘获取

优势

  1. 提高查询效率,解决了高并发系统的性能问题;
  2. 减少和数据库的交互次数,减少系统开销。

Mybatis缓存

1、mybatis中存在两级缓存,分别是:

  • 一级缓存: 基于 PerpetualCache 的 HashMap 本地缓存,默认只开启一级缓存。SqlSession级别的缓存,也成为本地缓存,与数据库同一会话期间查询到的数据;
  • 二级缓存: 二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap 存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。

2、Mybatis定义了缓存接口Cache,用于实现二级缓存。

3、一级缓存失效的四种情况

  • ① sqlSession不同;
  • ② sqlSession相同,但是查询条件不同;
  • ③ sqlSession相同,但是两次相同条件的查询之间执行力增、删、改操作;
  • ④ sqlSession相同,但是却手动清除了一级缓存 sqlSession.clearCache()

4、二级缓存

  • 开启缓存

    <setting name="CacheEnabled" value="true"/>
    
  • 在每个mapper.xml中配置使用二级缓存

    <cache eviction="FIFO"
           flushInterval="6000"
           size=512
           readOnly="true">
    <!--eviction:创建了一个FIFO缓存。(清除策略)
    	flushIntervall:表示每隔多长时间刷新(单位:毫秒ms)
     	size:可以存储结果对象或列表的512个引用
    	readOnly:返回的对象为只读
    -->
    
  • 清除策略。默认的缓存清除策略是LRU

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

交互次数,减少系统开销。

Mybatis缓存

1、mybatis中存在两级缓存,分别是:

  • 一级缓存: 基于 PerpetualCache 的 HashMap 本地缓存,默认只开启一级缓存。SqlSession级别的缓存,也成为本地缓存,与数据库同一会话期间查询到的数据;
  • 二级缓存: 二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap 存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。

2、Mybatis定义了缓存接口Cache,用于实现二级缓存。

3、一级缓存失效的四种情况

  • ① sqlSession不同;
  • ② sqlSession相同,但是查询条件不同;
  • ③ sqlSession相同,但是两次相同条件的查询之间执行力增、删、改操作;
  • ④ sqlSession相同,但是却手动清除了一级缓存 sqlSession.clearCache()

4、二级缓存

  • 开启缓存

    <setting name="CacheEnabled" value="true"/>
    
  • 在每个mapper.xml中配置使用二级缓存

    <cache eviction="FIFO"
           flushInterval="6000"
           size=512
           readOnly="true">
    <!--eviction:创建了一个FIFO缓存。(清除策略)
    	flushIntervall:表示每隔多长时间刷新(单位:毫秒ms)
     	size:可以存储结果对象或列表的512个引用
    	readOnly:返回的对象为只读
    -->
    
  • 清除策略。默认的缓存清除策略是LRU

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

资料来源:

[1] 狂神说Java:https://www.bilibili.com/video/BV1NE411Q7Nx

[2] Mybatis官网:https://mybatis.org/mybatis-3/zh/index.html

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值