Mybatis

MyBatis是一个优秀的Java持久层框架,它解决了JDBC的繁琐工作,提供动态SQL和映射功能。MyBatis通过SqlSessionFactoryBuilder创建SqlSessionFactory,再由SqlSession执行SQL。它支持XML和注解两种方式配置映射,提供一级和二级缓存,并通过DAO接口实现SQL的动态执行和结果映射。MyBatis的动态SQL标签如if、choose等简化了条件判断,同时预编译SQL提高效率,防止SQL注入。此外,它支持实体属性与数据库字段不一致的映射,实现灵活的分页。MyBatis作为半自动ORM工具,相比全自动的Hibernate,更强调SQL的控制权。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、Mybatis 介绍

MyBatis 本是apache的一个开源项目iBatis,2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAOs)。当前,最新版本是MyBatis 3.5.1 ,其发布时间是2019年4月8日。

MyBatis 是一款支持普通 SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的 JDBC 代码和参数的手工设置以及结果集的检索。MyBatis 使用简单的 XML或注解用于配置和原始映射,将接口和 Java 的POJOs(Plain Ordinary Java Objects,普通的 Java对象)映射成数据库中的记录。

每个MyBatis应用程序主要都是使用SqlSessionFactory实例的,一个SqlSessionFactory实例可以通过SqlSessionFactoryBuilder获得。SqlSessionFactoryBuilder可以从一个xml配置文件或者一个预定义的配置类的实例获得。

用xml文件构建SqlSessionFactory实例是非常简单的事情。推荐在这个配置中使用类路径资源(classpath resource),但你可以使用任何Reader实例,包括用文件路径或file://开头的url创建的实例。MyBatis有一个实用类----Resources,它有很多方法,可以方便地从类路径及其它位置加载资源。

二、Mybatis的作用。为何不直接用JDBC

1️⃣JDBC 弊端

  1. 重复创建链接和释放链接,造成资源浪费。解决方式:使用连接池。
  2. 出现硬编码,体现在数据库驱动,url、用户名密码、sql。解决方式:使用配置文件。
  3. 结果获取不方便。解决方式:把结果放到一个POJO中。

2️⃣MyBatis 是一个支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架。相对于 JDBC,MyBatis 有以下优点:

  1. 把 sql 语句从 Java 中独立出来,统一管理,便于维护和管理。
  2. 封装了底层的 JDBC,API 的调用,并且能够将结果集自动转换成 JavaBean 对象,简化了 Java 数据库编程的重复工作。
  3. 提供了缓存功能。
  4. 入参无需用对象封装(或者map封装),使用@Param

三、Mybatis 解决 jdbc 编程的问题

  1. 数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。
    解决:在 SqlMapConfig.xml 中配置数据链接池,使用连接池管理数据库链接。
  2. sql 与代码耦合,不易维护,实际应用 sql 变化的可能较大,sql 变动需要改变代码。
    解决:将 sql 语句配置在 XXXXmapper.xml 文件中与代码分离。
  3. 向 sql 语句传参数麻烦,因为 where 条件不一定,可能多可能少,占位符需要和参数一一对应。
    解决:Mybatis 自动将 java 对象映射至 sql 语句,通过 statement 中的 parameterType 定义输入参数的类型。
  4. 对结果集解析麻烦,sql 变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成 pojo 对象解析比较方便。
    解决:Mybatis 自动将 sql 执行结果映射至 java 对象,通过 statement 中的 resultType 定义输出结果的类型。

四、Mybatis 的一级缓存和二级缓存

SqlSession 是 Mybatis 的核心处理类。

MyBatis 中的缓存分为两种:一级缓存sqlSession和二级缓存mapper。当使用同一个 sqlSession 时,查询到的数据可能是一级缓存;而当使用同一个 mapper 时,查询到的数据可能是二级缓存。一级缓存默认开启的。二级缓存需要配置才开启,当配置文件配置了cacheEnabled=true时,就会开启二级缓存。

五、Mybatis DAO 接口为何不需要实现类

Mybatis 实现了 DAO 接口 与 xml 映射文件的绑定,自动生成接口的具体实现,使用起来变得更加省事和方便。

1️⃣Dao 接口,就是常说的 Mapper 接口:
①接口的全限名,就是映射文件中的 namespace 的值。
②接口的方法名,就是映射文件中 MappedStatement 的 id 值。
③接口方法内的参数,就是传递给 sql 的参数。

2️⃣Dao 接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为 key 值,可唯一定位一个 MappedStatement。例如:com.wg.mappers.ZooDao.selById,可以唯一找到 namespace 为com.wg.mappers.ZooDao 下面 id = selById 的 MappedStatement。在 Mybatis 中,每一个 select、insert、update、delete 标签,都会被解析为一个 MappedStatement 对象。

3️⃣Dao 接口里的方法,是不能重载的,因为是全限名+方法名的保存和寻找策略。Dao 接口的工作原理是JDK的动态代理,Mybatis 运行时会使用 JDK 动态代理为 Dao 接口生成代理 proxy 对象(如使用 Spring 会注入到容器中),代理对象 proxy 会拦截接口方法,转而执行 MappedStatement 所代表的 sql,然后将 sql 执行结果返回。

六、Mybatis 不同的 xml 映射文件,id 是否可以重复

不同的 xml 映射文件,如果配置了 namespace,那么 id 可以重复;如果没有配置,那么 id 不能重复;毕竟 namespace 不是必须的,只是最佳实践而已。
原因就是 namespace+id 是作为 Map<String,MappedStatement> 的 key 使用的,如果没有 namespace,id 重复会导致数据互相覆盖。有了 namespace,id 就可以重复,namespace 不同,namespace+id 自然也就不同。

七、处理实体类中的属性名和表中的字段名不一致问题

名称对应

八、 模糊查询like语句写法

模糊查询like语句

九、什么情况用注解,什么情况用 xml 绑定

  1. 注解使用情况:sql 语句简单时
  2. xml 绑定使用情况:xml 绑定(@RequestMap用来绑定xml文件)

十、Mybatis是如何进行分页的?分页插件的原理是什么?

  1. Mybatis 使用 RowBounds 对象进行分页,它是针对 ResultSet 结果集执行的内存分页,而非物理分页。
  2. 也可以在 sql 内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。分页插件的基本原理是使用 Mybatis 提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的 sql,然后重写 sql,根据 dialect 方言,添加对应的物理分页语句和物理分页参数。

十一、Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?

  1. 使用 resultMap 标签,逐一定义列名和对象属性名之间的映射关系。
  2. 使用 sql 列的别名功能,将列别名书写为对象属性名,比如 AcctName AS NAME,对象属性名一般是小写,但是列名不区分大小写,Mybatis 会忽略列名大小写,智能找到与之对应对象属性名,甚至可以写成 AcctName AS NaMe,Mybatis 一样可以正常工作。有了列名与属性名的映射关系后,Mybatis 通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回。找不到映射关系的属性,是无法完成赋值的。

十二、如何在mapper中传递多个参数

第1种:

//DAO层的函数
public User selUser(String name,String area); 

对应的xml,#{0}代表接收的是dao层中的第一个参数,#{1}代表dao层中第二参数,更多参数一致往后加即可。

<select id="selectUser" resultMap="BaseResultMap"> 
 select * from userInfo where user_name = #{0} and user_area=#{1} 
</select> 

第2种: 使用 @param:

 public interface usermapper { 
 user selUser(@param("userName") String userName, 
 @param("hashedPassword") String hashedPassword); 
 }

然后就可以在 xml 如下使用(推荐封装为一个map,作为单个参数传递给mapper):

 <select id="selUser" resultType="user"> 
 select id, username, hashedPassword 
 from userInfo 
 where userName = #{username} 
 and hashedPassword = #{hashedPassword} 
 </select>

十三、Mybatis 的动态 sql

  1. Mybatis 的动态 sql 可以在 xml 映射文件内,以标签的形式编写动态 sql,完成逻辑判断和动态拼接 sql 的功能。
  2. Mybatis 提供了 9 种动态 sql 标签:trim、where、set、foreach、if、choose、when、otherwise、bind。
  3. 执行原理:使用 OGNL 从 sql 参数对象中计算表达式的值,根据表达式的值动态拼接 sql,以此来完成动态 sql 的功能。

十四、#{} 和 ${} 的区别

区别

理解

十五、sql 预编译

sql 预编译指的是数据库驱动在发送 sql 语句和参数给 DBMS 数据库管理系统(Database Management System)之前对 sql 语句进行编译,这样 DBMS 执行 sql 时,就不需要重新编译。JDBC 中使用对象 PreparedStatement 来抽象预编译语句,使用预编译。为什么需要预编译:

  1. 预编译阶段可以优化 sql 的执行。
    预编译之后的 sql 多数情况下可以直接执行,DBMS 不需要再次编译。越复杂的 sql,编译的复杂度将越大,预编译阶段可以合并多次操作为一个操作。
  2. 预编译语句对象可以重复利用。
    sql 会预编译在数据库系统中。执行计划同样会被缓存,它允许数据库做参数化查询。使用预处理语句比普通查询更快,因为做的工作更少。把 sql 预编译后产生的 PreparedStatement 对象缓存下来,下次对于同一个 sql,可以直接使用该缓存的 PreparedState 对象。它拥有更佳的性能优势,因为数据库对 sql 的分析,编译,优化已经在第一次查询前完成了。
  3. PreparedStatement 可以防止 sql 注入式攻击
    sql 注入攻击是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的 sql 语句段或命令,从而利用系统的 sql 引擎完成恶意行为的做法。

MyBatis 默认情况下,将对所有的 sql 进行预编译。MySQL 的预编译源码在com.mysql.jdbc.ConnectionImpl中。

说明:
MyBatis 在调用 connection 进行 sql 预编译之前,会对 sql 进行动态解析,动态解析主要包含如下的功能:

  • 占位符的处理
  • 动态 sql 的处理
  • 参数类型校验

MyBatis 强大的动态 sql 功能的具体实现就在此。

十六、Mybatis 为什么是半自动 ORM 映射工具

Hibernate 属于全自动 ORM 映射工具,使用 Hibernate 查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。而 Mybatis 在查询关联对象或关联集合对象时,需要手动编写 sql 来完成,所以,称之为半自动 ORM 映射工具。

十七、Ibatis 和 Mybatis

  1. Ibatis:2010年,apache 的 Ibatis 框架停止更新,并移交给了 google 团队,同时更名为 MyBatis。从2010年后 Ibatis 再没更新过,彻底变成了一个孤儿框架。一个没人维护的框架注定被 MyBatis 拍在沙滩上。
  2. Mybatis:Ibatis 的升级版本。

十八、Mybatis 架构

Mybatis架构.jpg

  1. mybatis 配置 SqlMapConfig.xml,此文件作为 mybatis 的全局配置文件,配置了 mybatis 的运行环境等信息。mapper.xml 文件即 sql 映射文件,文件中配置了操作数据库的sql语句。此文件需要在 SqlMapConfig.xml 中加载。
  2. 通过mybatis环境等配置信息构造 SqlSessionFactory 即会话工厂。
  3. 由会话工厂创建 sqlSession 即会话,操作数据库需要通过sqlSession进行。
  4. mybatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。
  5. Mapped Statement也是mybatis一个底层封装对象,它包装了mybatis配置信息及sql映射信息等。mapper.xml文件中一个sql对应一个Mapped Statement对象,sql的id即是Mapped statement的id。
  6. Mapped Statement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql前将输入的java对象映射至sql中,输入参数映射就是jdbc编程中对preparedStatement设置参数。
  • 输入映射(传入的参数)
    支持的数据类型:
    基本数据类型:基础类型以及包装类、String
    POJO
    Map
    包装的POJO:一个pojo中有pojo属性
  1. Mapped Statement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程。
  • 输出映射(产生的结果类型)
    基本数据类型:基础类型以及包装类、String
    POJO
    Map
    List

十九、MyBatis xml配置文件层次结构

MyBatis配置文件层次结构.jpg

##properties元素

properties是一个配置属性的元素,让开发者能在配置文件的上下文中使用它,MyBatis提供3种配置方式:

  1. property子元素。
  2. properties配置文件。
  3. SqlSessionFactoryBuilder使用Properties文件构建。

property子元素

 <property name="driver" value="com.mysql.jdbc.Driver"/>

properties配置文件
一般会使用一个单独的properties配置文件来配置属性值,以方便在多个配置文件中重复使用它们,也方便日后维护和随时修改。可以通过${key}的形式,取出在配置文件中配置的值。

<configuration>
    <!-- 引入配置文件 -->
    <properties resource="datasource.properties"/>

    <environments default="dev">
        <environment id="dev">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!-- 使用配置文件中的属性 -->
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

SqlSessionFactoryBuilder使用Properties文件构建
出于安全考虑,properties配置文件中的账号密码等元素可能是加密的,这个时候就需要对加密的元素进行处理。

    public static void func() throws Exception {
        Properties properties = new Properties();
        properties.load(Resources.getResourceAsStream("datasource.properties"));
        // 对原账号密码解密
        properties.setProperty("username", decode(properties.getProperty("username")));
        properties.setProperty("password", decode(properties.getProperty("password")));

        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        // SqlSessionFactoryBuilder可以使用一个InputStream和一个Properties构建SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is, properties);
    }

三种方式的优先级
MyBatis支持的3种配置方式可能同时出现,并且属性还会重复配置,MyBatis将按照下面的顺序来加载:

  1. 在properties元素体内指定的属性首先被读取。
  2. 根据 properties元素中的resource属性读取类路径下属性文件,或者根据url属性指定的路径读取属性文件,并覆盖已读取的同名属性。
  3. 读取作为build()方法参数传递的属性,并覆盖已读取的同名属性。

因此,通过build()方法参数传递的属性具有最高优先级,resource/url属性中指定的配置文件次之,最低优先级的是 properties属性中指定的属性。因此,我们尽量不要使用混合的方式来定义配置,首选的方式是使用properties文件。

二十、environments 环境变量

配置环境可以注册多个环境,每一个环境分为两大部分:一个是数据库源(dataSource)的配置,另外一个是数据库事务(transactionManager)的配置。

    <environments default="dev">
        <environment id="dev">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
            </dataSource>
        </environment>
    </environments>
  1. default:表示默认使用哪个数据源
  2. id:表示数据源的名称
  3. transactionManager的事务类型type一共有三种:JDBC,采用JDBC方式管理事务,独立编码中我们常常使用;MANAGED,采用容器方式管理事务,在JNDI数据源中常用;自定义,由使用者自定义数据库事务管理办法,适用于特殊应用。
  4. property元素配置数据源的各类属性
  5. dataSource的type属性是提供我们对数据厍连接方式的配置:UNPOOLED(非连接池数据库)、POOLED(连接池数据库)、JNDI(JNDI数据源)、自定义数据源。

二十一、数据源

MyBatis内部为我们提供了3种数据源的实现方式:

  • UNPOOLED,使用org.apache.ibatis.datasource.unpooled.UnpooledDataSource实现。
  • POOLED,使用org.apache.ibatis.datasource.pooled.PooledDataSource实现。
  • JNDI,使用org.apache.ibatis.datasource.jndi.JndiDataSourceFactory实现。

二十二、数据库事务

数据库事务是交由SqlSession去控制的,可以通过SqlSession提交或者回滚。在大部分的工作环境下,都会使用 Spring框架来控制它。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JFS_Study

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值