# 技术栈知识点巩固——Mybatis

Posted 爱码代码的喵

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了# 技术栈知识点巩固——Mybatis相关的知识,希望对你有一定的参考价值。

技术栈知识点巩固——Mybatis

文章目录

MyBatis

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

  • MyBatis专注于SQL本身,是实体类与SQL语句之间建立的映射关系(不是实体类与表),是一个足够灵活的dao层优化方案,适用于性能要求高,需求多变的项目 。

Mybatis 工作原理

MyBatis框架的优点和缺点

优点

  • JDBC相比,减少了50%以上的代码量
  • 最简单的持久化[框架、小巧简单易学
  • SQL代码从程序代码中彻底分离出来,可重用
  • 提供XML标签,支持编写动态SQL
  • 提供映射标签,支持对象与数据库的ORM字段关系映射

缺点

  • SQL语句编写工作量大,熟练度要高
  • 数据库移植性差,比如mysql移植到OracleSQL语句会有差异,要适配各种数据库

Mybatis 中 #和 $的区别是什么?

  • #是拿对象的值。
  • $直接解析成字符串格式。

什么是SQL注入 ,如何避免。

  • 恶意拼接查询
  • 传入非法参数
  • 过滤输入内容就是在数据提交到数据库之前,就把用户输入中的不合法字符剔除掉。
  • 在使用参数化查询的情况下,数据库服务器不会将参数的内容视为 SQL 语句的一部分来进行处理,而是在数据库完成 SQL 语句的编译之后,才套用参数运行。

说一下 mybatis 的一级缓存和二级缓存

  • 一级缓存:默认是开启的,sql session级别的。
  • 二级缓存:默认是关闭的,是Mapper级别的。

MyBatis 定义的接口,怎么找到实现的?

  • Mapper接口添加到MapperRegistry 中的一个HashMap中。用Mapper接口的Class对象作为Key,用Mapper 接口的 Class 对象作为 Key , 以一个携带Mapper接口作为属性的MapperProxyFactory 实例作为value

  • session.getMapper()的时候,会生成一个Mapper接口的代理对象、Mybatis 为了完成 Mapper 接口的实现,运用了Jdk代理模式。

  • 在代理类执行相应的方法的时候,会于数据库进行交互操作,进行增删改查。得到ResultSet,解析ResultSet映射成JavaBean

  • 简易版Mybatis请看:https://blog.csdn.net/qq_37248504/article/details/108371768


Mybatis的底层实现原理

  • 构建SqlSessionFactory通过xml方式、或者通过Java代码方式。
  • SqlSessionFactory中获取SqlSession
  • SqlSession 中获取Mapper
  • 调用 Mapper的方法。
  • 详细内容请见:https://blog.csdn.net/qq_37248504/article/details/108352168

PageHelper分页插件的使用

  • 依赖
<dependency>
	<groupId>com.github.pagehelper</groupId>
	<artifactId>pagehelper-spring-boot-starter</artifactId>
	<version>1.3.0</version>
</dependency>
  • 使用方式:mapper 执行之前
PageHelper.startPage(pageNum,pageSize);
  • 原理解释:Spring Boot 在启动的时候,为 SqlSessionFactory 添加了 PageInterceptor,这个拦截器会在 SQL 执行之前添加分页信息,分页信息是通过 Page 对象传递到当前线程中的,查询完成之后,会将查询的结果加上分页信息封装成 PageInfo 对象传给前端来解析。

Mybatis 代码生成器插件

<build>
        <plugins>
         <!--mybatis代码生成器的插件-->
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.6</version>
                <configuration>
                    <configurationFile>src/main/resources/mybatis/generator-config.xml</configurationFile>
                    <overwrite>true</overwrite>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>mysql</groupId>
                        <artifactId>mysql-connector-java</artifactId>
                        <version>$mysql.version</version>
                    </dependency>
                </dependencies>
            </plugin>
            
        </plugins>
 </build>
  • generator-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    <context id="MySQLTables" targetRuntime="MyBatis3">
        <plugin type="org.mybatis.generator.plugins.EqualsHashCodePlugin"/>

        <!--  配置数据库链接信息          -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3307/test?useUnicode=yes&amp;characterEncoding=UTF-8&amp;useSSL=false"
                        userId="root"
                        password="root">
            <!--            <property name="useInformationSchema" value="true"/>-->
        </jdbcConnection>

        <!-- 实体类位置       -->
        <javaModelGenerator targetPackage="com.li.springbootproject.domain.test" targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>

        <!-- mapper.xml        -->
        <sqlMapGenerator targetPackage="mapper.test" targetProject="src/main/resources">
            <property name="enableSubPackages" value="true"/>
        </sqlMapGenerator>

        <!-- mapper.java -->
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.li.springbootproject.mapper.test"
                             targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>

        <table schema="test" tableName="table_class" domainObjectName="TableClazzDO"></table>

        <!--   接口请求日志表     -->
        <!--                <table schema="core" tableName="t_interface_request_log" domainObjectName="InterfaceRequestLog"></table>-->
        <!-- 登录日志表 -->
        <!--        <table schema="core" tableName="t_login_log" domainObjectName="LoginLogDO"></table>-->

    </context>
</generatorConfiguration>

Mybatis批量插入返回主键

  • Mysql数据库中支持批量插入,所以只要配置useGeneratedKeyskeyProperty就可以批量插入并返回主键了。

Mybatis Executor

  • 执行器接口类图

  • BaseExecutor:实现了Executor的全部方法,包括对缓存,事务,连接提供了一系列的模板方法, 这些模板方法中留出来了四个抽象的方法等待子类去实现
  • SimpleExecutor: 特点是每次执行完毕后都会将创建出来的statement关闭掉,他也是默认的执行器类型
  • ReuseExecutor: 在它在本地维护了一个容器,用来存放针对每条sql创建出来的statement,下次执行相同的sql时,会先检查容器中是否存在相同的sql,如果存在就使用现成的,不再重复获取
  • BatchExecutor: 特点是进行批量修改,她会将修改操作记录在本地,等待程序触发提交事务,或者是触发下一次查询时,批量执行修改

Mybatis动态Sql

  • <if>:条件语句
  • <where>:当和if标签配合的时候,不用显示的声明类似where 1=1这种无用的条件
  • <choose><when><otherwise>:选择分支
  • <foreach>:遍历传入的集合对象
  • <include>:减少代码的重写
  • <set>:更新语句
  • <trim>:格式化标签

动态Sql 执行原理

  • 构建SqlSessionFactory对象时,解析mapper.xml文件,XMLStatementBuilder.parseStatementNode()根据不同的SQL标签生成对应的MappedStatement对象,存放在Configuration.mappedStatements集合中。
    然后遍历主标签里所有节点生成对应的SQL子节点对象(SqlNode)。XMLScriptBuilder.parseScriptNode()解析每个标签生成SqlNode对象。
  • 执行Executor.query()方法,根据keyStatementId获取到对应的MappedStatement对象,获取BoundSql对象时。遍历当前SqlSource.rootSqlNode对象。执行SqlNode.apply(),把动态sql拼接到DynamicContext.sqlBuilder字段中。
  • 使用sqlSourceParser.parse()方法替换sql$, #,并且把大括号中的值存放在
    ParameterMappingTokenHandler.parameterMappings集合中。

Mybatis分页方式

  • 逻辑分页:使用Mybatis自带的RowBounds进行分页,它是一次性查询很多数据,然后在数据中进行检索。一次性查询很多数据,如何在结果中检索分页的数据。这样做需要消耗大量的内存、有内存溢出的风险、对数据库压力较大。
  • 物理分页:自己手写分页sql或使用分页插件PageHelper,去数据库查询指定条数的分页数据。从数据库查询指定条数的数据,有效的防止了一次性全部查询出所有数据所带来风险。

当实体类属性名和表中的字段名不一样

  • Mapper映射文件中使用resultMap来自定义映射规则

Mapper接口

  • 接口的全限名,就是映射文件中的 namespace 的值;接口的方法名,就是映射文件中 MapperStatementid 值;
    接口方法内的参数,就是传递给 sql 的参数。
  • Mapper 接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为key 值,可唯一定位一个 MapperStatement。在 Mybatis 中,每一个标签,都会被解析为一个MapperStatement 对象。

Mapper接口里的方法

  • Mapper 接口里的方法,是不能重载的,因为是使用全限名+方法名的保存和寻找策略。Mapper 接口的工作原理是 JDK 动态代理,Mybatis 运行时会使用 JDK动态代理为 Mapper 接口生成代理对象 proxy,代理对象会拦截接口方法,转而执行 MapperStatement 所代表的 sql,然后将 sql 执行结果返回。

Mybatis插件

  • Mybatis可以编写针对ExecutorStatementHandlerParameterHandlerResultSetHandler四个接口的插件,mybatis使用JDK的动态代理为需要拦截的接口生成代理对象,然后实现接口的拦截方法,所以当执行需要拦截的接口方法时,会进入拦截方法(AOP面向切面编程的思想)

Mybatis where 标签

  • 使用 Where 标签解决 where 1=1 and 这个问题

ExecutorType.BATCH

  • Mybatis内置的ExecutorType3种,默认为simple,该模式下它为每个语句的执行创建一个新的预处理语句,单条提交sql

    batch模式重复使用已经预处理的语句,并且批量执行所有更新语句,显然batch性能将更优;

  • batch模式也有自己的问题,比如在Insert操作时,在事务没有提交之前,是没有办法获取到自增的id,这在某型情形下是不符合业务要求的

/**
  * sqlSession 手动提交事务
  *
  * @param list list
  */
private void multiInsertWithSqlSession(List<OrgTempDO> list) 
    SqlSessionFactory sqlSessionFactory = SpringContextUtils.getBean(SqlSessionFactory.class);
    SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
    InsertTestMapper mapper = sqlSession.getMapper(InsertTestMapper.class);

    List<List<OrgTempDO>> lists = splitList(list, splitSize);
    StopWatch stopWatch = new StopWatch("totalTask");
    stopWatch.start();
    for (List<OrgTempDO> orgTempList : lists) 
        // StopWatch stopWatch1 = new StopWatch("task1");
        // stopWatch1.start();
        mapper.insertOrgTempBatch(orgTempList);
        // stopWatch1.stop();
        // System.out.println("写入 10000 条执行耗时:" + stopWatch1.getTotalTimeMillis());
    
    sqlSession.commit();
    sqlSession.close();
    stopWatch.stop();
    System.out.println("写入 150000 条执行耗时:" + stopWatch.getTotalTimeMillis());

以上是关于# 技术栈知识点巩固——Mybatis的主要内容,如果未能解决你的问题,请参考以下文章

# 技术栈知识点巩固——数据库

# 技术栈知识点巩固——Nginx

# 技术栈知识点巩固——Nginx

# 技术栈知识点巩固——Css

# 技术栈知识点巩固——Css

# 全栈开发学习文档