导图社区 Mybatis
Mybatis思维导图:包含常见对象,DefaultSqlSession(默认),一个会话使用一个SqlSession,并且在使用完毕后需要close,DefaultSqlSession(默认),BatchExecutor(重用语句并执行批量更新)等等
编辑于2022-04-18 16:51:10Mybatis
https://mybatis.org/mybatis-3/zh/index.html
分页实现
逻辑分页
自带的 RowBounds 进行分页,它是一次性查询很多数据,然后在数据中再进行检索
并非是一次性查询出所有数据
RowBounds 表面是在“所有”数据中检索数据,其实并非是一次性查询出所有数据,因为 MyBatis 是对 jdbc 的封装,在 jdbc 驱动中有一个 Fetch Size 的配置,它规定了每次最多从数据库查询多少条数据,假如你要查询更多数据,它会在你执行 next()的时候,去查询更多的数据。就好比你去自动取款机取 10000 元,但取款机每次最多能取 2500 元,所以你要取 4 次才能把钱取完。只是对于 jdbc 来说,当你调用 next()的时候会自动帮你完成查询工作。这样做的好处可以有效的防止内存溢出。
物理分页
利用原生的sql关键字limit
利用interceptor来拼接sql
使用Interceptor拦截器
PageHelper工具
执行SQL语句之前动态的将SQL语句拼接了分页的语句,从而实现了从数据库中分页获取的过程
Executor执行器
在Mybatis配置文件中,可以指定默认的ExecutorType执行器类型,也可以手动给DefaultSqlSessionFactory的创建SqlSession的方法传递ExecutorType类型参数
SimpleExecutor
每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement
ReuseExecutor
执行update或select,以sql作为key查找Statement对象
存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map内
BatchExecutor
执行update,都添加到批处理中(addBatch()),等待统一执行(executeBatch())
与JDBC批处理相同
自定义插件
拦截对象
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
职责
拦截执行器的方法(log记录),主要用于sql重写
执行SQL的全过程,包括组装参数、组装结果集返回和执行SQL过程,都可以拦截,较为广泛
方法
update
insert
update
delete
query
queryCursor
flushStatements
在commit的时候自动调用,SimpleExecutor,ReuseExecutor,BatchExecutor处理不同
commit
rollback
createCacheKey
isCached
clearLocalCache
deferLoad
getTransaction
close
关闭事务
isClosed
判断事务是否关闭
setExecutorWrapper
ParameterHandler (getParameterObject, setParameters)
当StatementHandler使用prepare()方法后,接下来就是使用它来设置参数。所以如果有对参数做自定义逻辑处理的时候,可以通过拦截ParameterHandler来实现
职责
拦截参数,用于参数处理
动态参数设置
拦截执行SQL的参数组装,可以重写组装参数规则
方法
setParameters
设置参数
getParameterObject
获取参数
StatementHandler (prepare, parameterize, batch, update, query)
职责
主要负责处理MyBatis与JDBC之间Statement的交互,通俗而言就是负责操作Statement对象与数据库之间的交互
执行SQL的过程,可以重写执行SQL的过程
方法
Statement prepare(Connection connection, Integer transactionTimeout)
从connection中获取statement
void parameterize(Statement statement)
对sql进行设置参数
void batch(Statement statement)
批量执行
int update(Statement statement)
执行预编译后的sql语句(update,delete,insert)
List<E> query(Statement statement, ResultHandler resultHandler)
执行查询sql
Cursor<E> queryCursor(Statement statement)
使用游标执行查询sql
BoundSql getBoundSql()
获取执行SQL语句的封装类BoundSql
ParameterHandler getParameterHandler()
参数处理器
实现类
RoutingStatementHandler
说明
根据StatementType来创建一个代理,代理的主要对象即对应BaseStatementHandler的三种实现类
SimpleStatementHandler
管理Statement对象并向数据库推送不需要预编译的SQL语句
PreparedStatementHandler
管理Statement对象并向数据库推送需要预编译的SQL语句
CallableStatementHandler
管理Statement对象并调用数据库中的存储过程
概要
ResultSetHandler (handleResultSets, handleOutputParameters)
拦截结果,用于结果集二次处理
方法
List<E> handleResultSets(Statement stmt)
Cursor<E> handleCursorResultSets(Statement stmt)
void handleOutputParameters(CallableStatement cs)
概要
技术实现
实现Interceptor接口
Object intercept(Invocation invocation)
拦截目标对象的目标方法的执行
invocation的内容决定于@Intercepts的指定情况
Object plugin(Object target)
把当前拦截器生成一个代理放到拦截器链中
//官方推荐写法 public Object plugin(Object target) { //官方推荐写法 return Plugin.wrap(target, this); }
void setProperties(Properties properties)
将插件注册时 的property属性设置进来
仅插件初始化的时候执行
@Intercepts
标注一个拦截器
@Signature
被拦截方法的签名
通过参数
type(拦截对象)
Executor
解释
所有的Mapper语句的执行都是通过Executor进行的
update
/** * 执行update/insert/delete */ int update(MappedStatement ms, Object parameter) throws SQLException;
增删改语句是通过update方法进行的
query
/** * 执行查询,先在缓存里面查找 */ <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
查询是通过query方法进行的
query
/** * 执行查询 */ <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
查询是通过query方法进行的
queryCursor
/** * 执行查询,查询结果放在Cursor里面 */ <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
flushStatements
commit
StatementHandler
prepare
/** * 从连接中获取一个Statement */ Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException;
从链接中获取一个Statement
parameterize
/** * 设置statement执行里所需的参数 */ void parameterize(Statement statement) throws SQLException;
设置statement执行里所需的参数
batch
/** * 批量 */ void batch(Statement statement) throws SQLException;
批量
update
/** * 更新:update/insert/delete语句 */ int update(Statement statement) throws SQLException;
更新:update/insert/delete语句
query
/** * 执行查询 */ <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException;
queryCursor
<E> Cursor<E> queryCursor(Statement statement) throws SQLException;
ParameterHandler
ResultSetHandler
method(拦截对象的方法)
args(当前方法的参数列表)
注册
xml方式
<!-- mybatis-config.xml 注册插件--> <plugins> <plugin interceptor="io.yidoo.mybatis.plugin.SelectPruningColumnPlugin"> <property name="someProperty" value="100"/> </plugin> </plugins>
越靠后执行优先级越高
javaConfig方式
/** * mybatis拦截器配置 */ @Configuration public class MybatisConfiguration { /** * 注册拦截器 */ @Bean public SQLInterceptor mybatisInterceptor() { SQLInterceptor interceptor = new SQLInterceptor(); Properties properties = new Properties(); // 可以调用properties.setProperty方法来给拦截器设置一些自定义参数 interceptor.setProperties(properties); return interceptor; } }
插件执行顺序
常见对象
SqlSession
DefaultSqlSession(默认)
MyBatis中用于和数据库交互的顶层类
一个会话使用一个SqlSession,并且在使用完毕后需要close
重要参数
configuration
Executor
MappedStatement
Executor
BatchExecutor(重用语句并执行批量更新)
ReuseExecutor(重用预处理语句prepared statements)
SimpleExecutor(普通的执行器,默认)
RowBounds
Statement
GlobalConfig
DataSource
Configuration
初始化配置文件信息的本质就是创建Configuration对象,将解析的xml数据封装到Configuration内部的属性中
缓存
一级缓存(sqlsesson)
缓存实际上就是一个HashMap,key是真正执行的sql语句,value是缓存的结果
[namespace:sql:参数] 作为 key ,查询返回的语句作为 value 保存的
二级缓存(mapper)
基于 PerpetualCache 的 HashMap 本地缓存不同在于其存储作用域为 Mapper 级别
如果多个SQLSession之间需要共享缓存,则需要使用到二级缓存
Ehcache
默认没有开启二级缓存
如果 SqlSession 执行了 DML 操作(insert、update、delete),并 commit 了,那么 mybatis 就会清空当前 mapper 缓存中的所有缓存数据,这样可以保证缓存中的存的数据永远和数据库中一致,避免出现差异
namespace即是mapper接口的类路径
自定义缓存
常用标签
insert
update
delete
select
id
resultType
parameterMap
databaseId
fetchSize
flushCache
lang
parameterType
resultMap
resultOrdered
resultSets
resultSetType
statementType
timeout
useCache
cache
给定命名空间的缓存配置
flushInterval
eviction
size
readOnly
cache-ref
其他命名空间缓存配置的引用
resultMap
是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象 <!--column不做限制,可以为任意表的字段,而property须为type 定义的pojo属性--> <resultMap id="唯一的标识" type="映射的pojo对象"> <id column="表的主键字段,或者可以为查询语句中的别名字段" jdbcType="字段类型" property="映射pojo对象的主键属性" /> <result column="表的一个字段(可以为任意表的一个字段)" jdbcType="字段类型" property="映射到pojo对象的一个属性(须为type定义的pojo对象中的一个属性)"/> <association property="pojo的一个对象属性" javaType="pojo关联的pojo对象"> <id column="关联pojo对象对应表的主键字段" jdbcType="字段类型" property="关联pojo对象的主席属性"/> <result column="任意表的字段" jdbcType="字段类型" property="关联pojo对象的属性"/> </association> <!-- 集合中的property须为oftype定义的pojo对象的属性--> <collection property="pojo的集合属性" ofType="集合中的pojo对象"> <id column="集合中pojo对象对应的表的主键字段" jdbcType="字段类型" property="集合中pojo对象的主键属性" /> <result column="可以为任意表的字段" jdbcType="字段类型" property="集合中的pojo对象的属性" /> </collection> </resultMap>
属性
id
唯一标识
type
pojo类型
id
property
column
result
userName
column
collection
嵌套的集合属性
代表一对多关系
高级
嵌套查询
<collection column="传递给嵌套查询语句的字段参数" property="pojo对象中集合属性" ofType="集合属性中的pojo对象" select="嵌套的查询语句" > </collection> 注意:<collection>标签中的column:要传递给select查询语句的参数,如果传递多个参数,格式为column= ” {参数名1=表字段1,参数名2=表字段2}
association
嵌套的pojo属性
代表一对一关系
constructor
discriminator
鉴别器 根据实际选择实例,可以通过特定条件确定结果集
sql
可被其他语句引用的可重用语句块
parameterMap(已废弃)
常见参数
id
在命名空间中唯一的标识符,可以被用来引用这条语句
parameterType
将会传入这条语句的参数类的完全限定名或别名
这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset
parameterMap(已废弃)
resultType
从这条语句中返回的期望类型的类的完全限定名或别名
注意如果是集合情形,那应该是集合可以包含的类型,而不能是集合本身。使用 resultType 或 resultMap,但不能同时使用
resultMap
外部 resultMap 的命名引用
结果集的映射是 MyBatis 最强大的特性,对其有一个很好的理解的话,许多复杂映射的情形都能迎刃而解。使用 resultMap 或 resultType,但不能同时使用。
flushCache
将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:false
useCache
将其设置为 true,将会导致本条语句的结果被二级缓存,默认值:对 select 元素为 true
timeout
这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为 unset(依赖驱动)
fetchSize
这是尝试影响驱动程序每次批量返回的结果行数和这个设置值相等。默认值为 unset(依赖驱动)
statementType
STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED
Statement
提供了执行语句和获取结果的基本方法
不能防止sql注入
每次的执行都需要编译SQL
用途
普通的不带参的查询SQL
PreparedStatement(默认)
继承自Statement,都是接口,添加了处理输入参数的方法
可以使用占位符,是预编译的,批处理比Statement效率高
预编译,会被缓冲,在缓存区中可以发现预编译的命令,虽然会被再次解析,但不会被再次编译,能够有效提高系统性能
可以防止sql注入
把用户非法输入的单引号用\反斜杠做了转义,从而达到了防止sql注入的目的
用途
支持可变参数的SQL
概要
生产环境上一定要使用PreparedStatement,而不能使用Statement
静态SQL可以用Statement和PreparedStatement,带参数的用PreparedStatement,存储过程用CallableStatement
CallableStatement
继承自PreparedStatement
添加了调用存储过程核函数以及处理输出参数的方法
用途
支持调用存储过程,提供了对输出和输入/输出参数(INOUT)的支持
resultSetType
FORWARD_ONLY,SCROLL_SENSITIVE 或 SCROLL_INSENSITIVE 中的一个,默认值为 unset (依赖驱动)
FORWARD_ONLY
SCROLL_SENSITIVE
SCROLL_INSENSITIVE
resultSets
这个设置仅对多结果集的情况适用,它将列出语句执行后返回的结果集并每个结果集给一个名称,名称是逗号分隔的
基于 PerpetualCache 的 HashMap 本地缓存,它的声明周期是和 SQLSession 一致
默认开启
可能会出现脏数据
keyProperty
(仅对 insert 和 update 有用)唯一标记一个属性,MyBatis 会通过 getGeneratedKeys 的返回值或者通过 insert 语句的 selectKey 子元素设置它的键值,默认:unset。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。
useGeneratedKeys
keyColumn
(仅对 insert 和 update 有用)通过生成的键值设置表中的列名,这个设置仅在某些数据库(像 PostgreSQL)是必须的,当主键列不是表中的第一列的时候需要设置。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表
动态SQL
if
属性
test
choose
when
属性
test
otherwise
类似于 Java 中的 switch、case、default
trim
属性
prefix
给sql语句拼接的前缀
标识给标签里的内容加上前缀
prefixOverrides
去除sql语句前面的关键字或者字符,该关键字或者字符由prefixOverrides属性指定,假设该属性指定为"AND",当sql语句的开头为"AND",trim标签将会去除该"AND"
suffix
给sql语句拼接的后缀
标识给标签里的内容加上后缀
suffixOverrides
去除sql语句后面的关键字或者字符,该关键字或者字符由suffixOverrides属性指定
表示去除sql语句结尾部分指定的字符串
功能
可以完成< set > 或者是 < where >
where
只会在至少有一个子元素的条件返回 SQL 子句的情况下才去插入“WHERE”子句
如果标签返回内容是以AND、OR开头,会自动删除
set
应用于update
当标签返回内容以逗号结尾,自动删除逗号
foreach
属性
collection
如果传入的是单参数且参数类型是一个List的时候,collection属性值为list
如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array
如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可以封装成map
在作为入参时可以使用@Param("keyName")来设置键,设置keyName后,list,array将会失效
还有一种作为参数对象的某个字段的时候
如果User有属性List ids。入参是User对象,那么这个collection = "ids".如果User有属性Ids ids;其中Ids是个对象,Ids有个属性List id;入参是User对象,那么collection = "ids.id"
open
迭代以后的内容加前缀
close
迭代以后的内容加后缀
index
在list和数组中,index是元素的序号,在map中,index是元素的key
item
集合中元素迭代时的别名
separator
元素之间的分隔符
在in()的时候,separator=","会自动在元素中间用“,“隔开
bind
相当于动态生成新字段
属性
name
新字段的名字
value
新字段的值
该值可以使用其他已存在的字段进行拼接,如字符串可以用+号
<select id="selectUserByBind" resultType="com.po.MyUser" parameterType= "com.po.MyUser"> <!-- bind 中的 uname 是 com.po.MyUser 的属性名--> <bind name="paran_uname" value="'%' + uname + '%'"/> select * from user where uname like #{paran_uname} </select>
script
基于注解的sql不能直接使用动态SQL,但用<script>包起来就可以使用
selectKey
include
引用已有的
refid
其他
Integer 或者 Long 在字段值为 0 时,非空 if 判断不会执行
could not determine data type of parameter
强制类型转换
<if test="name !=null and name !=''"> AND u.name like concat('%',#{name}::text,'%') </if>
#{}和${}的区别
#将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号
$将传入的数据直接显示生成在sql中
#{}是预编译处理,${}是字符串替换
支持延迟加载
延迟加载的原理的是调用的时候触发加载,而不是在初始化的时候就加载信息
设置 lazyLoadingEnabled=true 即可
like实现方式
LIKE '%${name}%'
LIKE "%"#{name}"%"
name LIKE CONCAT(CONCAT('%',#{name},'%'))
bind标签
<bind name="pattern1" value="'%' +name + '%'" />
Java代码提前拼装好
支持枚举
TypeHandler
作用
内置
自定义
1. 实现接口或继承类
org.apache.ibatis.type.TypeHandler
org.apache.ibatis.type.BaseTypeHandler
2. 设置JavaType和JdbcType来限定这个TypeHandler 的范围
3. 编码
@MappedTypes(String.class) @MappedJdbcTypes(value = {CLOB,CLOB,VARCHAR,LONGVARCHAR,NVARCHAR,NCHAR,NCLOB},includeNullJdbcType = true) public class MyStringTypeHandle extends BaseTypeHandler<String> { @Override public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException { ps.setString(i, parameter); } @Override public String getNullableResult(ResultSet rs, String columnName) throws SQLException { return StringEscapeUtils.unescapeHtml4(rs.getString(columnName)); } @Override public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException { return StringEscapeUtils.unescapeHtml4(rs.getString(columnIndex)); } @Override public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { return StringEscapeUtils.unescapeHtml4(cs.getString(columnIndex)); } }
4. 启用
数据源
多数据源
自定义sqlSessionFactory
基础架构