Mybatis笔记

# Mybatis
  • 参数封装
    • mybatis方法传入单参数时,不用指定具体参数名,传入多参数时,需要使用一下方式指定
      • 多参数时,方法参数被封装为map key即位传入的参数名称,可用@Param重新命名参数,可直接用#{xxx}取出;
      • 也可在不用@Param时,使用Map的索引取出,使用#{1} #{0} 或 #{param1} #{param2}
      • 可使用原始的pojo传参,可直接使用属性名 取出
      • 可使用TO传参,自行封装 eg:分页查询,封装 page size
  • 参数封装map原理
    • 调用对应的mapper方法
    • 使用mapperproxy代理类 处理
    • 传入要操作的方法类型,crud
    • 进行参数处理:首先根据传入的参数整理names(map),此时调用的是paramnameresolver进行解析,如果参数使用的@Param注解,则使用传入的值作为names的value, names的size作为key ,如果没有使用param注解,则使用当前names的size作为value,得到一个最终的names map
    • 如果args参数数组为空,直接返回;如果只有一个参数并且没有使用param注解,则直接返回第一个数组元素;
    • 如果有多个元素,或者元素使用的param注解,则需要后续处理,遍历组装好的names 组装一个新的参数map,此时以names的value作为参数map 的key ,nemes的key作为传入参数数组的索引args[0 1 2 …],取出对应的参数值,遍历结束,封装参数的map则处理完成
  • $和#的区别 ⭐
    • ’#‘ 预编译的形式,留下占位符,将参数设置到sql语句中,防止sql注入
    • ’$‘ 取出的值直接拼接在sql语句中,存在安全问题;
    • 特殊情况:原生jdbc不支持预编译的语句需要使用$
      • 多数情况使用# 但是当涉及到分表的时候,message_202201 202201需要根据需求进行查询不同的月表时,可使用KaTeX parse error: Expected group after '_' at position 17: …进行sql拼接 message_̲{date} ;
      • order by ${xxx} ${orderdesc}. orderdesc(排序顺序asc desc)
    • #中的参数,jdbcType在某些情况下需要被指定,
      • eg:在oracle 中,如果传入的是null值,如果jdbcType没有被指定,则mybatis默认使用的是OTHER 这个类型在aracle中是无法识别的,需要使用jdbcTpye=NULL指定参数为空时的类型,也可以在全局配置setting配置 jdbcTypefroNull 设置默认为空的时候使用NULL作为参数类型
  • select返回不同集合
    • 当select查询返回list的时候,resultType中要填写的list中元素的类型
      • eg:如果查询的是List 返回值类型要填写User的全类名
    • 当返回一条记录的map类型是,列名做key value做值,返回值类型需要写map
      • public Map<String, Object > selectById
    • 当返回的是一个map对象,对应多条记录,key是记录的主键或其他,value为记录值,则需要返回类型为集合中记录的类型
      • public Map<Integer , User> selectById
      • 此时需要在方法上添加@MapKey(””)注解指定作为Map 的key的列 ,此处返回值需要设置为User的全类名
    • select 返回自定义Bean 可使用resultMap 自定义返回Bean 使用resultMap标签自定义映射字段,主键使用id标签 ,mybatis底层自动配置,其他字段使用result标签映射
    • select 查询包含关联表的数据
      • 包含关联单个对象
        • select 查询的bean包含其他bean 的主键时,可使用级联操作,使用result指定另一个bean 的各个属性
        • 使用association标签指定其他bean 的封装队则
        • 使用association标签进行分步查询,在标签中使用select 属性指定另一个bean中的查询方法,并用cloumn指定需要传参的列
        • 延迟加载(懒加载),可在分步查询的基础上开启,需要在setting中设置,默认为true 开启,当不需要查询外键对应的对象时,不查询另一个sql,当需要使用外键对应的bean中包含的属性时,才进行查询
      • 包含集合属性
        • 当查询的bean中包含集合类型的属性时,需要使用自定义resultmap 指定属性时,集合类型使用collection标签 定义集合类型内部的封装规则,
        • oftype 指定集合中的对象类型
        • 场景:一堆多 部门数据包含员工数据,查询时可使用此方法
        • 可使用分步查询,collection标签中可使用select属性指定下一步查询的方法
        • 同时延迟加载也适用 collection 内部有fetchtype 属性,可确定延迟加载是否开启
          • 即使全局开启了懒加载,也可使用此属性确定内部的加载属性
          • lazy 延迟加载 eager 立即加载
      • 鉴别器标签
        • 对指定列进行判断,符合某种条件,则返回某些类型,或者组装新的resultmap
  • 动态sql⭐
    • 当使用if进行条件判断时where存在两种写法
      • 1、where后面使用1=1 防止第一个属性为空时,where 后面接and 的问题
      • 2、使用where 标签,条件判断 全部放入内部,此处需注意,where标签只能过滤语句前的and 或者 or ,放在语句后的and无法过滤
        • 针对此种优化,可使用trim 将前缀「prefix」设置为where 将后缀覆盖「suffixOverride」设置为and,可在内部条件判断过后,针对语句进行加前缀where 并且去掉后缀and
        • 另外还有前缀覆盖prefixOverride 和添加后缀属性suffix
    • set 标签可解决更新是属性后面的逗号遗留问题,同样可使用trim标签解决
  • 批量插入(以下几种存在mybatis限制,均为sql拼接的方式,实际上mybatis对sql长度也存在限制,可以使用mybatis 的批量插入,将sqlsession的type设置为BATCH )
    • 使用foreach遍历集合,依次使用values进行插入,此种方式oracle不支持
    • foreach 包装完整sql 分号分割,需要手动开启mysql开关,此方式同样适用于批量修改,批量删除
    • oracle不支持上述第一种,但是可以使用begin end内部封装多个sql 语句,以此实现批量插入,也可使用中间表实现
  • 默认内置参数
    • _parameter
      • 单个参数,_parameter代表的就是这个参数
      • 多个参数,会被封装为map _parameter 代表的是这个map
    • _databaseId
      • 如果配置了databaseIdProvider _databaseId就是当前数据库类型的别名
  • bind标签
    • 涉及某个字段模糊查询时,几种处理方式
      • 传参数时 拼接规则 如 %lisi% 推荐,更加灵活
      • 使用bind标签 ,属性取出时,直接使用#{_userName}取出参数 不够灵活 不推荐
  • sql标签进行 公用语句抽取
    • 每次要查询的字段,可以使用sql标签抽取,使用include标签引用,但是引用时如果需要传参数,只能使用$,不能使用#
  • mybatis两级缓存机制🌟
    • 一级缓存(本地缓存/sqlSession级别缓存) sqlSession的一个map
      • 一级缓存时一直开启的,同一个数据库连接会话,查询的数据会放到本地缓存中,当需要查询相同的数据时,直接查询本地缓存,相同的对象时user1==user2的,不会生成新的对象
      • 一级缓存失效原因
        • sqlSession 不同,多个链接会话之间,缓存不共享
        • 同一个sqlSession会话,查询条件不同
        • 同一个sqlSession会话,两次查询之间存在增删改操作
        • 同一个sqlSession会话,手动清除一级缓存,sqlSession.clearCahe();
    • 二级缓存(全局缓存)基于nameSpace缓存,一个nameSpace对应一个二级缓存
      • 工作机制
        • 一个会话,查询结束,会将数据存放在一级缓存中
        • 当会话关闭后,一级缓存被清理,将数据存放在二级缓存中
          • 注意:只有当会话被提交或关闭后,一级缓存才会转移至二级缓存
          • 一个nameSpace对应一个二级缓存 不共享
      • 使用
        • 二级缓存需要手动配置开启
          • mybatis 配置文件中,使用setting标签配置开启
        • mapper.xml 手动配置二级缓存具体缓存策略
          • 内部存在多个属性,可设置缓存清理策略
        • POJO需要实现序列化接口
      • 缓存相关配置/属性
        • casheEnable=false 只会关闭二级缓存,不会影响到一级缓存
        • 每个select标签中 都有默认的useCache属性,默认为true 如果手动改为false 也只会关闭二级缓存,不会影响一级缓存
        • 增删改操作之所以会清除缓存,因为 增删改标签中,存在默认的属性flushCache=true 这个属性会同时清除一级缓存和二级缓存 select标签中默认时false 不会清理缓存
      • 缓存原理机制
        • 当新会话进入时,首先会查询二级缓存,当二级缓存中没有时,查询一级缓存
        • 可以从底层源码中看到,mybatis 的缓存实现就是将数据存放值map中
  • mybatis运行原理🌟
    1. 获取sqlsessionfactory对象
      1. 使用SqlSessionFactoryBuilder对象的build方法 传入加载了mybatis全局配置文件的输入流
      2. build内部创建了Xml解析器XmlConfigBuild(内部使用Xpathparse解析器,主要封装了Dom4j)对配置文件进行解析,把每一个标签对应的值保存到configuration中
      3. 解析对应的mapper.xml 将mapper.xml的每个标签每个增删改方法解析成一个mappedstatement对象,并将该对象存储至configuration中
      4. 返回build(configuration)方法,创建一个DefaultSqlSession 对象 这个对象包含了全局的配置信息configuration
    2. 获取sqlsession对象
      1. sqlSessionfactory.opensession方法 调用openSessionFromDataSource 添加事物等信息
      2. 创建Excutor 根据配置创建对象的Excutor (类型包含Simple Reuse Batch 默认时Simple)
      3. 如果开启了二级缓存,则用CachingExcutor封装上一步生成的excutor
      4. 如果使用了其他的拦截器,则遍历所有拦截器分别包装excutor
      5. 创建DefaultSqlsession 内部包含全局配置configuration 和上面最终的excutor
    3. 获取接口的代理对象「MapperProxy」
      1. sqlsession.getmapper传入要获取的对象类型
      2. 从全局配置configuration中获取Mapperegistry 这个对象在第一步时加载了所有的Mapper对象类型
      3. 获取mapper 的代理工厂mapperproxyfactory 通过调用newinstance获取mapper的代理类mapperproxy并返回
      4. 由mapperproxy执行后续的CRUD方法
    4. 执行增删改查方法
      1. maperproxy代理对象实际上进行CRUD的时他内部的DefaultSqlSession
      2. DefaultSqlSession 调用内部的executor
      3. executor调用StatementHandler
        1. StatementHandler 主要用于sql预编译,设置参数等工作
        2. StatementHandler 调用ParameterHandler进行参数处理
        3. StatementHandler 调用ResultSetHandler进行结果集处理
        4. 这一步整个过程使用TypeHandler进行数据库类型和javaBean 的转换
      4. 这一步底层封装的时原生的JDBC
    5. mybatis底层四大对象
      1. executor
      2. StatementHandler
      3. ParameterHandler
      4. ResultSetHandler
    6. 插件机制
      1. 可以使用插件为目标对象创建一个代理对象,类似AOP
      2. 重点:以上四大对象被创建出来不是直接返回的,而是通过interceptorChain.pluginAll(xxxHandler)进行插件封装然后返回,这也是插件原理
      3. pluginAll内部遍历所有的插件,然后返回Interceptor.plugin后返回上述的四大目标对象
      4. 多个插件会代理对象进行层层代理封装,执行时先执行最外层「最后配置的插件」的代理对象方法,层层代理时,顺序是按照配置的顺序
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值