# 技术栈知识点巩固——Mybatis
Posted 爱码代码的喵
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了# 技术栈知识点巩固——Mybatis相关的知识,希望对你有一定的参考价值。
技术栈知识点巩固——Mybatis
文章目录
- 技术栈知识点巩固——Mybatis
MyBatis
-
MyBatis
是一款优秀的持久层框架,它支持定制化SQL
、存储过程以及高级映射。MyBatis
避免了几乎所有的JDBC
代码和手动设置参数以及获取结果集。MyBatis
可以使用简单的XML
或注解来配置和映射原生信息,将接口和Java
的POJOs(Plain Ordinary Java Object
,普通的Java
对象)映射成数据库中的记录。 -
MyBatis
专注于SQL
本身,是实体类与SQL
语句之间建立的映射关系(不是实体类与表),是一个足够灵活的dao
层优化方案,适用于性能要求高,需求多变的项目 。
Mybatis 工作原理
MyBatis框架的优点和缺点
优点
- 与
JDBC
相比,减少了50%
以上的代码量 - 最简单的持久化[框架、小巧简单易学
SQL
代码从程序代码中彻底分离出来,可重用- 提供
XML
标签,支持编写动态SQL
- 提供映射标签,支持对象与数据库的
ORM
字段关系映射
缺点
SQL
语句编写工作量大,熟练度要高- 数据库移植性差,比如
mysql
移植到Oracle
,SQL
语句会有差异,要适配各种数据库
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&characterEncoding=UTF-8&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
数据库中支持批量插入,所以只要配置useGeneratedKeys
和keyProperty
就可以批量插入并返回主键了。
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
的值;接口的方法名,就是映射文件中Mapper
的Statement
的id
值;
接口方法内的参数,就是传递给sql
的参数。 Mapper
接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为key
值,可唯一定位一个MapperStatement
。在Mybatis
中,每一个标签,都会被解析为一个MapperStatement
对象。
Mapper接口里的方法
Mapper
接口里的方法,是不能重载的,因为是使用全限名+方法名的保存和寻找策略。Mapper
接口的工作原理是JDK
动态代理,Mybatis
运行时会使用JDK
动态代理为Mapper
接口生成代理对象proxy
,代理对象会拦截接口方法,转而执行MapperStatement
所代表的sql
,然后将sql
执行结果返回。
Mybatis插件
Mybatis
可以编写针对Executor
、StatementHandler
、ParameterHandler
、ResultSetHandler
四个接口的插件,mybatis
使用JDK
的动态代理为需要拦截的接口生成代理对象,然后实现接口的拦截方法,所以当执行需要拦截的接口方法时,会进入拦截方法(AOP
面向切面编程的思想)
Mybatis where 标签
- 使用 Where 标签解决
where 1=1 and
这个问题
ExecutorType.BATCH
-
Mybatis
内置的ExecutorType
有3
种,默认为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的主要内容,如果未能解决你的问题,请参考以下文章