Mybatis常见知识点

Mybatis

  • 概念
    • 为什么要使用Mybatis
    • 谈谈你对Mybatis的理解
  • 优点
    • Mybatis的优点是什么
    • Mybatis和Hibernate的不同点
        • 1.hibernate 是全自动,而 mybatis 是半自动。
        • 2.hibernate 数据库移植性远大于 mybatis。
        • 3.hibernate 拥有完整的日志系统,mybatis 则欠缺一些。
        • 4.mybatis 相比 hibernate 需要关心很多细节
        • 5.sql 直接优化上,mybatis 要比 hibernate 方便很多
    • 为什么国内常使用Mybatis做为数据层框架
  • 常见面试题
    • 1.MyBatis 中 #{} 和 ${}的区别是什么
        • 符号类型
        • 防注入问题
        • 参数替换位置
        • 参数解析
        • 流程
    • 2.Mybatis有几种分页方式
    • 3.Mybatis是否支持延迟加载,有什么好处和优缺点
        • 懒加载的延迟思路
    • 4.Mybatis的注解有哪些?什么情况下使用注解
        • 增删改查类注解
        • 注解@Mapper和 @MapperScan
    • 5.Mybatis的缓存?一级缓存和二级缓存?
        • 缓存是什么?
        • 为什么需要缓存
        • 那些数据会放入到缓存
        • Mybatis的一级缓存
        • Mybatis的二级缓存
        • 原理
    • 6.谈谈Mybatis的动态SQL
    • 7.模糊查询like语句该怎么写?
    • 8.Mybatis的XML文件都有哪些标签属性
        • properties标签
        • setting标签
        • typeAliases标签
        • environments标签
        • mappers标签*
    • 9.如何获取自动生成的(主)键值
    • 10.如何在Mapper接口中传递多个参数
    • 11.Mybatis使用到的设计模式

概念

为什么要使用Mybatis

    半自动化的ORM框架,相比于全自动化的Hibernate,Mybatis的封装程度没有它那么高,但主要解决了SQL和对象的映射关系
    在Mybatis里面,SQL和代码是分离的。

  1. 使用连接池对连接进行管理
  2. SQL和代码分离,集中管理。
  3. 结果集映射
  4. 参数映射和动态SQL
  5. 重复SQL的提取
  6. 缓存管理
  7. 插件机制

谈谈你对Mybatis的理解

  1. Mybatis是⼀个半ORM(对象关系映射)框架,它内部封装了 JDBC,开发时只需要关注 SQL 语句本身,不需要花费精⼒去处理加载驱动、创建连接、创建 Statement 等繁杂的过程。程序员直接编写原⽣态 SQL,可以严格控制 SQL 执⾏性能,灵活度⾼。

  2. MyBatis 可以使⽤ XML 或注解来配置和映射原⽣信息,将 POJO 映射成数据库中的记录,避免了⼏乎所有的JDBC 代码和⼿动设置参数以及获取结果集。

  3. 通过 XML ⽂件或注解的⽅式将要执⾏的各种 Statement 配置起来,并通过 Java 对象和 Statement 中 SQL 的动态参数进⾏映射⽣成最终执⾏的 SQL 语句,最后由 MyBatis 框架执⾏ SQL并将结果映射为 Java 对象并返回。(从执⾏ SQL到返回 Result 的过程)。


优点

Mybatis的优点是什么

  1. 易于上手和掌握。
  2. sql写在xml里,便于统一管理和优化。
  3. 解除sql与程序代码的耦合。
  4. 提供映射标签,支持对象与数据库的orm字段关系映射
  5. 提供对象关系映射标签,支持对象关系组建维护
  6. 提供xml标签,支持编写动态sql。

总结:
     mybatis的优点其实也是缺点,正因为mybatis的使用简单,数据的可靠性、完整性的瓶颈更多依赖与程序员对sql的使用水平。sql写在xml里。 虽然方便了修改、优化和统一预览,但可读性很低,测试也非常困难而且受限,无法向JDBC那样在代码里根据逻辑实现复杂动态sql拼接。mybatis简单看就是提供了字段映射和对象关系的jdbc,省去了数据赋值到对象的步骤而已。除此之外并无太多作为,不要把它想象成hibernate那样强大,简单小巧易用上手,方便浏览修改sql就是它最大的优点了。

Mybatis和Hibernate的不同点

1.hibernate 是全自动,而 mybatis 是半自动。

    hibernate 完全可以通过对象关系模型实现对数据库的操作,拥有完整的 JavaBean 对象与数据库的映射结构来自动生成 sql。而 mybatis 仅有基本的字段映射,对象数据以及对象实际关系仍然需要通过手写 sql 来实现和管理。

2.hibernate 数据库移植性远大于 mybatis。

    hibernate 通过它强大的映射结构和 hql 语言,大大降低了对象与数据库(oracle、mysql 等)的耦合性,而 mybatis 由于需要手写 sql,因此与数据库的耦合性直接取决于程序员写 sql 的方法,如果 sql 不具通用性而用了很多某数据库特性的 sql 语句的话,移植性也会随之降低很多,成本很高。

3.hibernate 拥有完整的日志系统,mybatis 则欠缺一些。

    hibernate 日志系统非常健全,涉及广泛,包括:sql 记录、关系异常、优化警告、缓存提示、脏数据警告等;而 mybatis 则除了基本记录功能外,功能薄弱很多。

4.mybatis 相比 hibernate 需要关心很多细节

    hibernate 配置要比 mybatis 复杂的多,学习成本也比 mybatis 高。但也正因为 mybatis 使用简单,才导致它要比 hibernate 关心很多技术细节。mybatis 由于不用考虑很多细节,开发模式上与传统 jdbc 区别很小,因此很容易上手并开发项目,但忽略细节会导致项目前期 bug 较多,因而开发出相对稳定的软件很慢,而开发出软件却很快。hibernate 则正好与之相反。但是如果使用 hibernate 很熟练的话,实际上开发效率丝毫不差于甚至超越 mybatis。

5.sql 直接优化上,mybatis 要比 hibernate 方便很多

    由于 mybatis 的 sql 都是写在 xml 里,因此优化 sql 比 hibernate 方便很多。而 hibernate 的 sql 很多都是自动生成的,无法直接维护 sql;虽有 hql,但功能还是不及 sql 强大,见到报表等变态需求时,hql 也歇菜,也就是说 hql 是有局限的;hibernate 虽然也支持原生 sql,但开发模式上却与 orm 不同,需要转换思维,因此使用上不是非常方便。总之写 sql 的灵活度上 hibernate 不及 mybatis。

为什么国内常使用Mybatis做为数据层框架

    首先,十年前我们主要使用的ORM框架就是iBatis,而阿里巴巴是对国内Java开发者影响最大的一家公司。阿里在国内Java社区的影响力有目共睹,这个大家应该都能感受到, 阿里对Java社区贡献了很多实用的开源工具,并且国内Java开发者对于阿里开源的产品接纳程度也最高。
    MyBatis封装较少,提供的切入点较多,适合进行架构。遇到超级复杂的场景的时候有不错的sql支持。曾经JPA适合做增删改,mybatis只擅长查询,但是现在的tk.mybatis已经补上了这一块短板,而JPA的依然没有补上他的查询短板。在复杂情况下需要在代码里嵌入大量sql片段或手动用代码拼装sql,但是老实说,都到这份上了,写sql不是还更快一点?因此,做企业级应用时,如果组内Hibernate会的人多,可以考虑用这个,但是依然会埋下一个性能的坑。做互联网级应用时,建议还是用Mybatis吧。
    综合考虑,Mybatis的优点是简单高效,优化起来也方便,比较符合现在的开发节奏,现在的互联网公司都是先快速开发占领市场,然后再优化代码。而且这个过程需求经常是变来变去的,开发人员也有流动性,这种情况下用Mybatis显然更加适合。


常见面试题

1.MyBatis 中 #{} 和 ${}的区别是什么

    动态 sql 是 mybatis 的主要特性之一,在 mapper 中定义的参数传到 xml 中之后,在查询之前, mybatis 会对其进行动态解析。mybatis 为我们提供了两种支持动态 sql 的语法:#{}以及${}

符号类型

    #{}:参数占位符,即预编译
    ${}:字符串替换符,即SQL拼接

防注入问题

    #{}:很大程度上能防止sql 注入
    ${}:不能防止sql 注入

参数替换位置

    #{}:变量替换是在DBMS 中
    ${}:变量替换是在 DBMS 外

    DBMS:数据库管理系统(Database Management System)是一种操纵和管理数据库的大型软件,是用于建立、使用和维护数据库,简称DBMS。它对数据库进行统一的管理和控制,以保证数据库的安全性和完整性。
    用户通过DBMS访问数据库中的数据,数据库管理员也通过DBMS进行数据库的维护工作。它提供多种功能,可使多个应用程序和用户用不同的方法在同时或不同时刻去建立,修改和询问数据库。

参数解析

    #{}:将传入的数据都当成一个字符串,会对传入的变量自动加一个单引号。如:user_id = #{userId},如果传入的值是111,那么解析成sql时的值为user_id = ‘111’,如果传入的值是id,则解析成的sql为user_id = ‘id’
    ${}:将传入的参数直接显示生成在sql中,且不加任何引号。如:user_id = ${userId},如果传入的值是111,那么解析成sql时的值为user_id = 111 , 如果传入的值是id,则解析成的sql为user_id = id

流程

    #{}:动态解析 -> 预编译 -> 执行
    ${}:动态解析 -> 编译 -> 执行

2.Mybatis有几种分页方式

  1. 第一种分页,全部查出来,然后在进行分页,这种分页不好
  2. 第二种分页,使用limit,前端传进来页码和每页的条数,在sql使用limit进行查询
  3. 第三种分页方式,用RowBounds分页,创建RowBounds对象,使用有参传开始的条数((page -1)*pagesize)和每页几条数据(pagesize),调用mapper方法讲RowBounds对象传递进去,RowBounds中有2个字段offset和limit。这种方式获取所有的ResultSet,从ResultSet中的offset位置开始获取limit个记录。但这并不意味着JDBC驱
  4. 使用pageHelper插件分页、主要是通过mybatis拦截器实现的
  
        <dependency>
            <groupId>com.github.pagehelpergroupId>
            <artifactId>pagehelperartifactId>
            <version>5.3.0version>
        dependency>
        <dependency>
            <groupId>com.github.pagehelpergroupId>
            <artifactId>pagehelper-spring-boot-autoconfigureartifactId>
            <version>1.4.1version>
        dependency>

        <dependency>
            <groupId>com.github.pagehelpergroupId>
            <artifactId>pagehelper-spring-boot-starterartifactId>
            <version>1.4.1version>
        dependency>


spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/emp_dep?useUnicode=true&characterEncoding=UTF-8&useSSL=true&serverTimezone=UTC
    username: root
    password: root

pagehelper:
  #指定数据库的方言
  helper-dialect: mysql
  #分页合理化参数,默认值为false。当该参数设置为 true 时,
  #pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页。
  reasonable: true
  #支持通过 Mapper 接口参数来传递分页参数,默认值false,
  #分页插件会从查询方法的参数值中,自动根据上面 params 配置的字段中取值,查找到合适的值时就会自动分页
  support-methods-arguments: true
  #为了支持startPage(Object params)方法,增加了该参数来配置参数映射,用于从对象中根据属性名取值
  params: =count=countSql


    Mybatis使用RowBounds对象进行分页,它是针对ResultSet结果集执行的内存分页,而非物理分页。可以在sql内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。

    分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql,根据dialect方言,添加对应的物理分页语句和物理分页参数。


3.Mybatis是否支持延迟加载,有什么好处和优缺点

    延迟加载又称为懒加载,mybatis是支持懒加载的。在进行关联查询时,按照设置的延迟规则推迟对关联对象的查询。延迟加载可以有效的减少数据库的压力。延迟加载只是针对有延迟设置的关联对象的推迟查询,对于主主查询是直接进行执行SQL语句。

    MyBatis关联查询加载时机

  1. 直接加载:执行完主对象的查询后,马上执行对关联对象的查询语句
  2. 侵入式延迟:执行完对主对象对查询后,不会执行对关联对象的查询,但当访问主对象的属性详情是,就会执行关联对象的查询
  3. 深度延迟:只有当真正访问关联对象的详情时,才会执行查询语句

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

懒加载的延迟思路

    立即加载方式:查询账户(Account)信息并且关联查询用户(User)信息。

select * from account a left outer join user u on u.id = a.uid

    延迟加载方式:如果先查询账户(Account)信息即可满足要求。当我们需要查询用户(User)信息时再查询用户(User)信息。把对用户(User)信息的按需去查询就是延迟加载。

SQL语句:
select * from account 封装到Account类里,
当使用到 Account类 的成员变量 User类时,执行
SQL语句:

select * from account where uid = #{uid}

4.Mybatis的注解有哪些?什么情况下使用注解

增删改查类注解

    @Select

    @Delete

    @Insert

    @Update

    @Options能够设置缓存时间,能够为对象生成自增的key。

上面的这些注解中的每一个代表了执行的真实SQL。它们每一个都使用字符串数组(或单独的字符串)。如果传递的是字符串数组,它们由每个分隔它们的单独空间串联起来。

注解@Mapper和 @MapperScan

@Mapper
    作用:在接口类上添加了@Mapper,在编译之后会生成相应的接口实现类
    添加位置:接口类上面

@Mapper
public interface UserDAO {
   //代码
}

@MapperScan
    作用:指定要变成实现类的接口所在的包,然后包下面的所有接口在编译之后都会生成相应的实现类
    添加位置:是在Springboot启动类上面添加


@SpringBootApplication
@MapperScan("com.winter.dao")
public class SpringbootMybatisDemoApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(SpringbootMybatisDemoApplication.class, args);
    }

5.Mybatis的缓存?一级缓存和二级缓存?

缓存是什么?

    缓存其实就是存储在内存中的临时数据,这里的数据量会比较小,一般来说,服务器的内存也是有限的,不可能将所有的数据都放到服务器的内存里面,所以, 只会把关键数据放到缓存中,缓存因为速度快,使用方便而出名!

为什么需要缓存

     BS架构里面,用户的所有操作都是对数据库的增删改查,其中查询的操作是最多的,但如果用户想要某个数据时每次都去数据库查询,这无疑会增加数据库的压力,而且获取时间效率也会降低,所以为了解决这些问题,缓存应用而生,使用了缓存之后,服务器只需要查询一次数据库,然后将数据保存到服务器主机的内存中,以后读取时就直接取内存中的数据,而不需要每次都查数据库,这种方案除了降低数据库压力之外,还提高了响应速度。

那些数据会放入到缓存

    通常情况下,都会将那些变化较少且经常用到的数据会放到缓存中,比如像字典、系统参数、有固定值的状态码等等;另外将用户保存到缓存也是一种很好的策略,这样登录的时候就可以极速响应了。

Mybatis的一级缓存

    myabtis的缓存分为两类,一级缓存和二级缓存。一级缓存是默认开启的,它在一个sqlSession会话里面的所有查询操作都会保存到缓存中。一般来说一个请求中的所有增删改查操作都是在同一个sqlSession里面的,所以我们可以认为每个请求都有自己的一级缓存。如果同一个sqlSession会话中2个查询中间有一个 insert 、update或delete 语句,那么之前查询的所有缓存都会清空,防止脏读。

Reader reader = Resources.getResourceAsReader("config/configuration.xml");
        //创建数据工厂
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = builder.build(reader);
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
 
         // 。。。。。。 
        // 这中间所走的所有查询操作都会进行缓存,一旦关闭sqlSession会话,缓存则会刷新
 
        //释放会话
        sqlSession.clearCache();
        // 关闭会话
        sqlSession.close();

Mybatis常见知识点_第1张图片

Mybatis的二级缓存

    二级缓存是全局的。也就是说:多个请求可以共用一个缓存,二级缓存需要手动开启,有2种方式配置二级缓存。

  1. 缓存会先放在一级缓存中,当sqlSession会话提交或者关闭时才会将一级缓存刷新到二级缓存中。
  2. 开启二级缓存后,用户查询时,会先去二级缓存中找,找不到在去一级缓存中找。

Mybatis常见知识点_第2张图片

配置方式:在mybatis.xml文件里面配置

 <settings>
        
        
    settings>
 
    public static void main(String[] args) throws IOException {
 
        // 加载mybatis配置文件
        Reader reader = Resources.getResourceAsReader("config/configuration.xml");
        //创建数据工厂
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
 
        SqlSessionFactory sqlSessionFactory = builder.build(reader);
 
        // 第一个会话
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
 
 
        // 获取会话一的mapper接口对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
 
        // 第一次查询
        User user = mapper.selectByPrimaryKey("3rfrf34r34");
        
 
        //释放第一个会话
        sqlSession.clearCache();
        sqlSession.close();
 
        // 第二个会话
        SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
        // 获取会话二的mapper接口对象
        UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
 
        // 第二次查询
        User user1 = mapper2.selectByPrimaryKey("3rfrf34r34");
        // 释放第二个会话
        sqlSession2.clearCache();
        sqlSession2.close();
    }

注意事项:

  • 映射语句文件中的所有 select 语句的结果将会被缓存。
  • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
  • 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
  • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
  • 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

原理

(1)一级缓存: 基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该 Session 中的所有 Cache 就将清空,默认打开一级缓存。

(2)二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap 存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现Serializable序列化接口(可用来保存对象的状态),可在它的映射文件中配置 ;

(3)对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存Namespaces)的进行了C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 clear 掉并重新更新,如果开启了二级缓存,则只根据配置判断是否刷新。


6.谈谈Mybatis的动态SQL

    Mybatis动态sql可以在Xml映射文件内,以标签的形式编写动态sql,执行原理是根据表达式的值 完成逻辑判断 并动态拼接sql的功能。Mybatis提供了9种动态sql标签:trim | where | set | foreach | if | choose | when | otherwise | bind。

7.模糊查询like语句该怎么写?

    在Java代码中添加sql通配符。

    string wildcardname = “%smi%”;
    list<name> names = mapper.selectlike(wildcardname);
 
    <select id=”selectlike”>
     select * from foo where bar like #{value}
    select>

    在sql语句中拼接通配符,会引起sql注入

    string wildcardname = “smi”;
    list<name> names = mapper.selectlike(wildcardname);
 
    <select id=”selectlike”>
         select * from foo where bar like "%"${value}"%"
    select>

8.Mybatis的XML文件都有哪些标签属性

properties标签

    properties标签就是从外部引入其他配置文件 ,resource属性是从本地引入,url属性是从网络下载引入

<properties resource="jdbcconfig.properties">properties>

setting标签

    settings标签包含很多重要的设置setting标签,name就是设置项的名称,value就是设置项的值

<settings>
	
	<setting name="mapUnderscoreToCamelCase" value="true"/>
settings>

setting还有很多设置项,具体的可以去Mybatis中文手册查看

typeAliases标签

    typeAliases起别名的标签,为全限定类名起别名,书写简便,注意别名不区分大小写

environments标签

    有属性default,和子标签environment,default属性指定使用哪种环境,值为environment标签的id值,transactionManager的type属性事务管理器的类别,使用JDBC即可。dataSource的type属性POOLED值,是指使用连接池技术,使用POOLED。

<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="${jdbc.driver}" />
				<property name="url" value="${jdbc.url}" />
				<property name="username" value="${jdbc.username}" />
				<property name="password" value="${jdbc.password}" />
			dataSource>
		environment>
	environments>

mappers标签*

    其子标签mapper就是加载映射文件的

  1. resource属性,是加载类路径下的映射文件

  2. class属性,是加载指定的接口,一般为实体类对应接口

  3. package标签,加载指定包下的所有类,类必须和其对应的映射文件放在一起,否则会加载失败

<mappers>
	<mapper resource="EmployeeMapper.xml" />
	<mapper class="org.mybatis.builder.PostMapper"/>
	<package name="org.mybatis.builder"/>
mappers>

9.如何获取自动生成的(主)键值


10.如何在Mapper接口中传递多个参数

1)第一种:
//DAO层的函数
Public UserselectUser(String name,String area);  
//对应的xml,#{0}代表接收的是dao层中的第一个参数,#{1}代表dao层中第二参数,更多参数一致往后加即可。
<select id="selectUser"resultMap="BaseResultMap">  
    select *  fromuser_user_t   whereuser_name = #{0} anduser_area=#{1}  
</select>2)第二种: 使用 @param 注解:
public interface usermapper {
   user selectuser(@param(“username”) string username,@param(“hashedpassword”) string hashedpassword);
}
然后,就可以在xml像下面这样使用(推荐封装为一个map,作为单个参数传递给mapper):
<select id=”selectuser” resulttype=”user”>
         select id, username, hashedpassword
         from some_table
         where username = #{username}
         and hashedpassword = #{hashedpassword}
</select>3)第三种:多个参数封装成map
try{
//映射文件的命名空间.SQL片段的ID,就可以调用对应的映射文件中的SQL
//由于我们的参数超过了两个,而方法中只有一个Object参数收集,因此我们使用Map集合来装载我们的参数
Map<String, Object> map = new HashMap();
     map.put("start", start);
     map.put("end", end);
     return sqlSession.selectList("StudentID.pagination", map);
 }catch(Exception e){
     e.printStackTrace();
     sqlSession.rollback();
    throw e; }
finally{
 MybatisUtil.closeSqlSession();
 }

11.Mybatis使用到的设计模式

你可能感兴趣的:(MyBatis,mybatis,java,数据库)