MyBatis

MyBatis面试题MyBatis

  • XxxMapper.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">

1、MyBatis

  • 什么是MyBatis?

    MyBatis是支持定制化SQL、存储过程以及高级映射的优秀的持久化框架。
    MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。
    MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录。

  • 为什么要使用MyBatis?

    MyBatis是一个半自动化的持久层框架。

    • JDBC

      → SQL夹在Java代码块里,耦合度高导致编码内伤

      → 维护不易且实际开发需求中sql是有变化的,频繁修改的情况多见

    • Hibernate和JPA

      → 长难复杂SQL,对于Hibernate而言处理也不容易

      → 内部自动生产的SQL,不容易做特殊优化

      → 基于全映射的全自动框架,大量字段的POJO进行部分映射时比较困难,导致数据库性能下降

    • 对于开发人员而言,核心sql还是需要自己优化

    • sql和java编码分开,功能边界清晰,一个专注业务、一个专注数据

  • 去哪里找MyBatis?

    https://github.com/mybatis/mybatis-3/

  • 如何使用MyBatis?

    ① 创建一张测试表

    ② 创建对应的JavaBean

    ③ 创建mybatis配置文件,sql映射文件

    ④ 测试


      1)根据全局配置文件,利用SqlSessionFactoryBuilder创建SqlSessionFactory

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);

      2)使用SqlSessionFactory获取sqlSession对象,一个sqlSession对象代表和数据库的一次会话


SqlSession openSession = factory.openSession();

      3)使用SqlSession根据方法id进行操作

SqlSession openSession = factory.openSession();
try{
    Object one = openSession.selectOne("com.atguigu.dao.EmployeeMapper.getEmpById",1);
    System.out.println(one);
}finally{
    openSession.close();
}
  • HelloWorld-接口式编程

    ① 创建一个Dao接口

    ② 修改Mapper文件

    ③ 测试

// 使用SqlSession获取映射器进行操作

SqlSession sqlSession = factory.openSession();
try{
    EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
    Employee employee = mapper.getEmpById(1);
    System.out.println(employee);
}finally{
    sqlSession.close();
}
  • SqlSession

    SqlSession的实例不是线程安全的,因此是不能被共享的。

SqlSession每次使用完成后需要正确的关闭,这个关闭操作是必须的。

SqlSession可以直接调用方法的id进行数据库操作,但是我们一般还是推荐使用SqlSession获取到Dao接口的代理类,执行代理对象的方法,可以更安全的进行类型检查操作。


2、MyBatis全局配置文件

  • configuration配置
    • properties属性
    • settings设置
    • typeAliases类型命名
    • typeHandlers类型处理器
    • objectFactory对象工厂
    • plugins插件
    • enviroments环境
      • enviroment环境变量
      • transactionManager事务管理器
      • dataSource数据源
    • databaseIdProvider数据库厂商标识
    • mappers映射器
  • properties属性
<!-- 使用properties标签,
     resource属性读取类路径下属性文件 
     url属性读取指定路径属性文件
-->
<properties resource="dbconfig.properties"></properties>
  • settings设置

    这是MyBatis中极为重要的调整设置,他们会改变MyBatis的运行时行为。

<settings>
    <!-- 开启驼峰命名法 -->
    <setting name="mapUnderscoreToCamelCase" value="true" />
</settings>
  • typeAliases别名处理器
<!-- 类型别名是为Java类型设置一个短的名字,可以方便我们引用某个类 -->
<typeAliases>
    <typeAlias type="com.atguigu.bean.Employee" alias="employee" />
    <typeAlias type="com.atguigu.bean.Department" alias="department" />
</typeAliases>

<!-- 类很多的情况下,可以批量设置别名为这个包下的每一个类创建一个默认的别名,
     默认就是简单类名小写 
-->
<typeAliases>
    <package name="com.atguigu.bean" />
</typeAliases>

<!-- 也可以使用@Alias注解为其指定一个别名 -->
@Alias("emp")
public class Employee{}
  • typeHandlers类型处理器

    无论是MyBatis在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时,都会用类型处理器将获取的值以合适的方式转换成Java类型。

    • 自定义类型处理器

      我们可以重写类型处理器或创建自己的类型处理器来处理不支持的或非标准的类型。

      步骤:

      1)实现org.apache.ibatis.type.TypeHandler接口或者继承org.apache.ibatis.type.BaseTypeHandler
      
      2)指定其映射某个JDBC类型(可选操作)
      
      3)在mybatis全局配置文件中注册
      
  • plugins插件

    插件是MyBatis提供的一个非常强大的机制,我们可以通过插件来修改MyBatis的一些核心行为。插件通过动态代理机制,可以介入四大对象的任何一个方法的执行。

    • Executor(update,query,flushStatements,commit,rollback,getTransaction,close,isClosed)
    • ParameterHandler(getParameterObject,setParameters)
    • ResultSetHandler(handleResultSets,handleOutputParameters)
    • StatementHandler(prepare,parameterize,batch,update,query)
  • environments环境

    → MyBatis可以配置多种环境,比如开发、测试和生产环境需要有不同的配置

    → 每种环境使用一个environment标签进行配置并指定唯一标识符

    → 可以通过environments标签中的default属性指定一个环境的标识符来快速的切换环境

    • environment指定具体环境

      id:指定当前环境的唯一标识

      transactionManager、和dataSource都必须有

<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>
实际开发中我们使用Spring管理数据源,并进行事务控制的配置来覆盖上述配置
  • databaseIdProvider环境

    MyBatis可以根据不同的数据库厂商执行不同的语句

<databaseIdProvider type="DB_VENDOR">
    <property name="MySQL" value="mysql"/>
    <property name="Oracle" value="oracle"/>
    <property name="SQL Server" value="sqlServer"/>
</databaseIdProvider>

<select id="getEmpsByDeptId" resultType="Emp"
        parameterType="Integer" databaseId="mysql">
    SELECT * FROM tbl_emp WHERE deptId=#{id}
</select>
  • mapper映射
<!-- mapper逐个注册SQL映射文件 -->
<mappers>
    <mapper resource="mybatis/mapper/PersonDao.xml"/>
    <mapper url="file:///D:/UserDao.xml"/>
    <mapper class="com.atguigu.dao.PersonDaoAnnotation"/>
</mappers>

<!-- 或者使用批量注册 -->
<!-- 这种方式要求SQL映射文件名必须和接口名相同并且在同一个目录下 -->
<mappers>
    <package name="com.atguigu.dao"/>
</mappers>

3、MyBatis映射文件

映射文件指导着MyBatis如何进行数据库增删查改,有着非常重要的意义

→ cache — 命名空间的二级缓存配置

→ cache-ref — 其他命名空间缓存配置的引用

→ resultMap — 自定义结果集映射

→ sql — 抽取可重用语句块

→ insert — 映射插入语句

→ update — 映射更新语句

→ delete — 映射删除语句

→ select — 映射查询语句

  • insert、update、delete元素

  • 主键生成方式

    若数据库支持自动生成主键的字段(比如MySQL和SQL Server),则可以设置useGeneratedKeys="true",然后再把keyProperty设置目标属性上。

<insert id="insertCustomer" databaseId="mysql"
    useGeneratedKeys="true" keyProperty="id">
    INSERT INTO customer2(last_name,email,age)
    VALUES(#{lastName},#{email},#{age})
</insert>
  • 参数传参

  • select元素

    Select元素来定义查询操作

    → Id:唯一标识符

    — 用来引用这条语句,需要和接口的方法名一致
    

    → parameterType:参数类型

    — 可以不传,MyBatis会根据TypeHandler自动推断
    

    → resultType:返回值类型

    — 别名或者全类名,如果返回的是集合,定义集合中元素的类型。不能和resultMap同时使用
    
  • 自动映射

    ① 全局setting设置

    — autoMappingBehavior默认是PARTIAL,开启自动映射的功能。唯一的要求是列名和javaBean属性名一致
    
    — 如果autoMappingBehavior设置为null则会取消自动映射
    
    — 数据库字段命名规范,POJO属性符合驼峰命名法,如A_COLUMN → aColumn,我们可以开启驼峰命名规则映射功能,mapUnderscoreToCamelCase=true
    

    ② 自定义resultMap,实现高级结果集映射

    • constructor — 类在实例化时,用来注入结果到构造方法中
      — idArg - ID参数;标记结果作为ID可以帮助提高整体效能
      — arg - 注入到构造方法的一个普通结果
    • id — 一个ID结果;标记结果作为ID可以帮助提高整体效能
    • result — 注入到字段或JavaBean属性的普通结果
    • association — 一个复杂的类型关联;许多结果将包成这种类型
      — 嵌入结果映射 — 结果映射自身的关联,或者参考一个
    • collection — 复杂类型的集
      — 嵌入结果映射 — 结果映射自身的集,或者参考一个
    • discriminator — 使用结果值来决定使用哪个结果映射
      — case — 基于某些值的结果映射

    association

<!-- 我们可以使用联合查询,并以级联属性的方式封装对象 -->
<resultMap type="com.atguigu.bean.Lock" id="myLock">
    <id column="id" property="id"/>
    <result column="lockName" property="lockName"/>
    <result column="key_id" property="key.id"/>
    <result column="keyName" property="key.keyName"/>
</resultMap>

<!-- 也可以使用association标签定义对象的封装规则 -->
<!-- 1、association - 嵌套结果集 -->
<resultMap type="com.atguigu.bean.Lock" id="myLock2">
    <id column="id" property="id"/>
    <result column="lockName" property="lockName"/>
    <association property="key" javaType="com.atguigu.bean.Key">
        <id column="key_id" property="id"/>
        <result column="keyName" property="keyName"/>
</resultMap>

<!-- 2、association - 分段查询 -->
<resultMap type="com.atguigu.bean.Lock" id="myLock3">
    <id column="id" property="id"/>
    <result column="lockName" property="lockName"/>
    <association property="key"
        select="com.atguigu.dao.KeyMapper.getKeyById"
        column="key_id">
    </association>
</resultMap>

<!-- association - 分段查询 & 延迟加载 -->
<!-- 开启延迟加载和属性按需加载 -->
<settings>
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>

#### Collection-集合类型&嵌套结果集
<select id="getDeptById" resultMap="MyDept">
    SELECT d.id d_id,d.dept_name d_deptName,
    e.id e_id,e.last_name e_lastName,e.email e_email,
    e.gender e_gender,e.dept_id e_deptId
    FROM department d
    LEFT JOIN employee e ON e.`dept_id`=d.`id`
    WHERE d.`id`=#{id}
</select>

<resultMap type="com.atguigu.bean.Department" id="MyDept">
    <id column="d_id" property="id"/>
    <result column="d_deptName" property="deptName"/>
    <collection property="emps" ofType="com.atguigu.bean.Employee"
        columnPrefix="e_">
        <id column="id" property="id"/>
        <result column="lastName" property="lastName"/>
        <result column="email" property="email"/>
        <result column="gender" property="gender"/>
    </collection>
</resultMap>

4、MyBatis-缓存机制

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

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

  • 一级缓存
    • 一级缓存(local cache),即本地缓存,作用域默认为sqlSession。当Session flush 或 close后,该Session中的所有Cache将被清空。
    • 本地缓存不能被关闭,但可以调用clearCache()来清空本地缓存,或者改变缓存的作用域。
    • 在mybatis3.1之后,可以配置本地缓存的作用域,在mybatis.xml中配置。
—— 同一次会话期间只要查询过的数据都会保存在当前SqlSession的一个Map中
    key:hashCode + 查询的SqlId + 编写的sql查询语句 + 参数

—— 一级缓存失效的四种情况
  -> 1. 不同的SqlSession对应不同的一级缓存
  -> 2. 同一个SqlSession但是查询条件不同
  -> 3. 同一个SqlSession两次查询期间执行了任何一次增删改操作
  -> 4. 同一个SqlSession两次查询期间手动清空了缓存

  • 二级缓存

    • 二级缓存(second level cache),全局作用域缓存

    • 二级缓存默认不开启,需要手动配置

    • MyBatis提供二级缓存的接口以及实现,缓存实现要求POJO实现Serializable接口

    • 二级缓存在SqlSession关闭或提交之后才会生效

    • 使用步骤:

      → 1、全局配置文件中开启二级缓存

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

      → 2、需要使用二级缓存的映射文件处使用cache配置缓存

      **`<cache></cache>`**
      

      → 3、注意:POJO需要实现Serializable接口

  • 缓存有关设置




5、MyBatis-工作原理

posted @ 2022-01-21 15:10  ArosyCat  阅读(30)  评论(0编辑  收藏  举报