2020-10-30

mybatis 框架学习笔记

目录
  • 自定义持久层框架
    • jdbc 代码基础回顾
      • 解决传统jdbc存在的问题
    • 自定义持久层框架设计思路
      • 使用端 --> 项目
    • 自定义持久层框架本身
    • mybatis 简介
      • sqlMapConfig.
      • 传统dao层开发方式
      • dao层开发方式
      • mybatis 外部properties 文件
      • mybatis aliasType
      • mapper.
      • 动态sql
    • mybatis复杂映射开发
      • 一对一查询
      • 一对多查询
      • 多对多查询
  • mybatis 注解开发
    • 注解一对多
    • 注解多对多
    • mybatis 缓存
      • 一级缓存
      • 一级缓存分析
      • 二级缓存
    • redis-cache 分析
    • mybatis的插件
      • 以上组件的创建原理
      • 自定义mybatis 插件
      • mybatis的第三方插件
    • mybatis架构原理
      • mybatis传统方式源码分析
      • Mapper代理方式
      • 设计模式
      • mybatis 执行流程图解
    • mybatis延迟加载
    • mybatis 动态sql相关类
      • 常用的数据库连接池
      • mybatis 查询数据量大的时候会造成oom 如何处理

2020年8月25日20:55:29

自定义持久层框架

  • 持久层框架 是对jdbc 的封装, 并解决了jdbc存在的问题
jdbc 代码基础回顾
try{ Class.formName("com.mysql.jdbc.Driver"); connection = .....}cache(Exception e){ }
  • 传统jdbc 存在 数据库配置信息存在硬编码问题 当更换数据库后需要再次对数据库驱动信息进行更改
  • 频繁创建释放数据库连接
  • sql语句 设置参数 获取结果集等参数均存在硬编码问题
  • 手动封装结果集 较为繁琐
解决传统jdbc存在的问题
  • 硬编码 --> 配置文件
  • 硬编码 --> 连接池
  • 手动封装结果集 --> 反射 内省
自定义持久层框架设计思路
使用端 --> 项目
  • 引入自定义持久层jar
  • 提供数据库配置信息及sql配置信息
    • sql配置信息
      • sql语句
      • 参数类型
      • 返回值类型
  • 使用配置文件提供外上述两种配置信息
  • sqlMapConfig.存入mapper.
  • mapper.
自定义持久层框架本身
  • 创建为项目工程,本质是对jdbc代码进行封装-->底层为传统的jdbc

    • 数据库配置信息
    • sql 配置信息 占位符 参数 sql语句等
    • 根据调用处传递的配置信息进行解析
      • 加载配置文件 --> 以字节输入流的形式加载 --> 存储在内存中
      • 创建Resource类,getResourceAsStream(String path)
      • 创建2个javaBean(容器对象),存放的是对配置文件解析出的内容
        • Configuration:核心配置类: 存放sqlMapperConfig.
        • MappedStrement:映射配置类:存放mapper.
      • 解析配置文件:Dom4j
        • 创建类:SqlSessionFactoryBuilder,存在方法:build(InputStream in)
        • build方法实现逻辑:
          • 使用dom4j解析配置文件,将解析结果封装到容器对象内
          • 创建sqlSessionFactory对象,生产sqlSession(会话对象),避免重复创建连接 --> 工厂模式 降低耦合
      • 创建sqlSessionFactory接口及实现类DefaultSqlSessionFactory
        • openSession方法 : 创建 sqlSession对象
      • 创建SqlSession 接口及实现类DefaultSqlSession 定义对数据库的CRUD方法
        • selectList()
        • selectOne()
        • update()
        • delete()
      • 创建Excutor接口及实现类 SimpleExcutor 实现类
        • query(Configuration conf,MappedStatement mapped,Object ...params):执行JDBC代码

    graph TBb("工程项目")subgraph 工程项目d("mapper.

mybatis 简介

  • 持久层框架

  • 基于orm:Object Relation Mapping 实体类和数据库表建立映射关联关系

  • 半自动 可以对sql进行优化 hibernate -> 全自动

  • 轻量级:启动的过程中需要的资源比较少

  • 底层: 对jdbc执行代码 进行封装 规避硬编码,频繁开闭数据源

  • environment - dataSource.type="POOLED" 指定数据源类型为连接池
  • environment - dataSource
    • property.driver 数据库驱动
    • property.url 数据库地址 注意连接编码格式
    • property.user 数据库连接用户名
    • property.password 数据库连接用密码
  • mapper 配置的四种方式:
    • mapper.resource 相对于类路径的引用
    • mapper.url 完全限定资源定位符
    • mapper.class 接口对应的全路径
    • package.name 批量加载 保证映射文件与接口同包同名

  • 传统dao层开发方式
    dao层开发方式
    mybatis 外部properties 文件
    • 设置mybatis
    • 在resources下创建外部配置文件 格式:对象.属性=值
    • 加载外部配置文件
      • 在 configutation 后第一个节点之前 引入外部配置文件 <properties resource='路径'/>
    mybatis aliasType
    • 在mybatis 配置文件中的typeAliases 节点中设置
      <typeAliases> <typeAlias type="pojo类路径的全限定名" alais="别名"></typeAlias></typeAliases>// 对于基础数据类型 mybatis 已定义了别名string ==> Stringlong ==> Longint ==> Integerdouble ==> Doubleboolean ==> Boolean
    • 当存在多个pojo时不适用以上方法,采用package模式[批量起别名]
      • package模式 不要单独指定alias属性 默认为类本身的类名,且不区分大小写
      <typeAliases> <package name="pojo类所在包的全限定名"></typeAlias> // 不要单独指定alias属性 默认为类本身的类名,且不区分大小写</typeAliases>
    动态sql
    • if 标签 判断入参是否符合条件, 不符合不拼接
    • where 标签 自动拼接where 同时去掉where后的第一个and 关键字
    • foreach 标签
      • collection array 注意便携式不要使用#{}
      • open 循环前追加
      • close 循环结束后追加
      • item 单次循环内的数据对象 生成的变量
      • separator 前后循环数据之间的分隔符
    • sql 标签 抽取sql片段
      • id sql语句的标识
      • 内容为具体的sql语句 可用于封装 分页 和 查询某张表时的前面部分
    mybatis复杂映射开发

    sqlMapConfig.

    <package name="与接口同包同名的包名"><package>
    一对一查询
    • resultMap 手动配置实体属性与表字段的映射关系 可用于mybatis 的resultMMap
      • id
      • type 按照封装对象的全路径
      <resultMap id="orderMap" type="com.lagou.pojo.Order"> <result property="id" column="id"></result> <result property="orderTime" column="orderTime"></result> <result property="total" column="total"></result> <association property="user" javaType="com.lagou.pojo.User">  <result property="id" column="uid"></result>  <result property="username" column="username"></result> </association></resultMap><!--resultMap:手动来配置实体属性与表字段的映射关系--><select id="findOrderAndUser" resultMap="orderMap"> select * from orders o,user u where o.uid = u.id</select>
    一对多查询
     <resultMap id="userMap" type="com.lagou.pojo.User">  <result property="id" column="uid"></result>  <result property="username" column="username"></result>  <collection property="orderList" ofType="com.lagou.pojo.Order">   <result property="id" column="id"></result>   <result property="orderTime" column="orderTime"></result>   <result property="total" column="total"></result>  </collection> </resultMap> <select id="findAll" resultMap="userMap">  select * from user u left join orders o on u.id = o.uid </select>
    多对多查询
    • 查询用户的同时查询出用户的角色
     <resultMap id="userRoleMap" type="com.lagou.pojo.User">  <result property="id" column="userid"></result>  <result property="username" column="username"></result>  <collection property="roleList" ofType="com.lagou.pojo.Role">   <result property="id" column="roleid"></result>   <result property="roleName" column="roleName"></result>   <result property="roleDesc" column="roleDesc"></result>  </collection> </resultMap> <select id="findAllUserAndRole" resultMap="userRoleMap">  select * from user u left join sys_user_role ur on u.id = ur.userid 		left join sys_role r on r.id = ur.roleid </select>

    mybatis 注解开发

    使用注解开发 无需编写任何配置文件

    • @Insert 增
    • @Update 删
    • @Delete 改
    • @Select 查
    • @Result 实现对结果集的封装 替代result标签节点
    • @Results 替代resultMap 标签节点
    • @One 替代单个pojo对象 替代 association 标签节点
    • @Many 如果属性为集合时 则采用 此注解
    注解一对多
    @Results({ @Result(property = "id",column = "id"), @Result(property = "orderTime",column = "orderTime"), @Result(property = "total",column = "total"), @Result(property = "user",column = "uid",javaType = User.class,  one=@One(select = "com.lagou.mapper.IUserMapper.findUserById"))})@Select("select * from orders")public List<Order> findOrderAndUser();
    注解多对多
    # IUserMapper //查询所有用户、同时查询每个用户关联的角色信息 @Select("select * from user") @Results({   @Result(property = "id",column = "id"),   @Result(property = "username",column = "username"),   @Result(property = "roleList",column = "id",javaType = List.class,    many = @Many(select = "com.lagou.mapper.IRoleMapper.findRoleByUid")) }) public List<User> findAllUserAndRole();# IRoleMapper @Select("select * from sys_role r,sys_user_role ur where r.id = ur.roleid and ur.userid = #{uid}") public List<Role> findRoleByUid(Integer uid);
    mybatis 缓存
    一级缓存

    使用同一个sqlSession 对象时,数据会被缓存
    mybatis 一级缓存过程图解

    1. 第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从
      数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。
    2. 如果中间sqlSession去执行commit操作(执行插入、更新、删除),则会清空SqlSession中的
      一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
    3. 第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直
      接从缓存中获取用户信息
    一级缓存分析

    一级缓存分析 流程图

    • 一级缓存到底是什么
      底层是一个hashMap 参照上述流程图
    • 一级缓存什么时候被创建
      缓存key创建类: Excustor.class.createCacheKey()BaseExcutor.createCacheKey() //创建一级缓存的keyCacheKey.update() - updateList.add(configuration.getEnviroment().getId());
    • 一级缓存的工作流程是怎样的
    • 一级缓存的清空 sqlSession.close() 或者执行带有事务的操作
    • 在分布式环境下也会存在脏读问题 解决:禁用一级缓存或调整缓存为statement 级别
    二级缓存
    • 原理 类似 一级缓存
    • 操作的sqlSession 执行了事务操作后 会清空二级缓存
    • 一级缓存 默认开启
    • 二级缓存需要手动配置开启
      1. 
    • 二级缓存 缓存的不是对象 而是对象中的数据
      • pojo类需要实现 Serializable 接口
      • 二级缓存的存储机制是多样的,可能存储在硬盘上 或者内存中
    • 当执行带有事务的操作后,二级缓存会被清空
    • 当基于
    • useCache 属性 false 禁用二级缓存 每次查询都发送sql去数据库查询 默认为true
    • 注解形式:@select 注解之前添加 @Options(cache="false")
    • flushCache 属性: 每次增删改操作后 清空缓存 默认true

    二级缓存整合redis

    mybatis二级缓存结构示意图

    • PerpetualCache 是mybatis默认实现缓存功能的类
    • 二级缓存的底层数据结构 还是 HashMap org.apache.ibatis.cache.impl.PerpetualCache --> private Map<Object, Object> cache = new HashMap()
    • 指定mybatis二级缓存的实现类
      在dao接口上添加注解 @CacheNamespace(implementation=PerpetualCache.class) PerpetualCache.class 更换为自定义缓存实现类如 : @CacheNamespace(implementation = RedisCache.class)
    • 二级缓存 存在的问题
      • 单服务器下 --> 没问题
      • 分布式 --> 无法实现分布式缓存
    • 分布式缓存技术
      • redis 官方提供有mybatis-redis实现类
      <dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-redis</artifactId> <version>1.0.0-beta2</version></dependency>配置配置文件 : redis.properties配置以下属性: host=localhost port=6379 connectionTimeout=5000 password=123456 database=0
      • memcached
      • ehcache
    • 二级缓存存在的脏读问题分析
      二级缓存namespace级别产生脏读问题分析
    redis-cache 分析
    • redis-cache 是如何存取值的
      1. 必须实现mybatis 的cache 接口2. 使用 jedis.hget 进行取值
    • redis-cache 使用的是哪种redis 数据结构
      • redis 的 hash 数据类型

    mybatis的插件

    • 一种形式的拓展点
      • 增加灵活性
      • 根据实际需求 自行拓展
    • mybatis 的四大组件 及允许拦截的方法
      • Executor 执行器
        • update
        • query
        • commit
        • rollback 等
      • StatementHandler sql语法构建器
        • prepare
        • parameterize
        • batch
        • update
        • query 等
      • ParameterHandler 参数处理器
        • getParameterObject
        • setParameterObject
      • ResultSetHandler 结果集处理器
        • handleResultSets
        • handleOutputParameters
    • 插件拦截器底层是对以上四个组件的拦截,采用动态代理实现
    以上组件的创建原理
    1. 每个创建出来的对象不是直接返回的,而是interceptorChain.pluginAll(parameterHandler)
    2. 获取到所有的Interceptor(拦截器)(插件需要实现的接口);调用interceptor.plugin(target);返回target包装后的对象
    3. 插件机制,我们可以使用插件为目标对象创建一个代理对象;AOP(面向切面)我们的插件可以为四大对象创建出代理对象,代理对象就可以拦截到四大对象的每一个执行;
    4. 由于使用了jdk动态代理,那么就回去执行 invoke 方法,可以在拦截器 的前置和后置处理器内做相应的处理操作
    @Intercepta({ @signature(  type=Executor.class,  method="query",  args=(MappedStatement.class,Object.class,RowBounds.class,ResultHandler.class} )})public class ExamplePlugin implements Interceptor{//省略逻辑}
    自定义mybatis 插件
    • 插件 --> 拦截器
    org.apache.ibatis.plugin.Interceptor 类# 拦截方法:只要被拦截的目标对象的目标方法被执行时,每次都会执行intercept方法Interceptor.intercept # 主要为了把当前的拦截器生成代理存到拦截器链中Interceptor.plugin# 获取配置文件的参数Interceptor.setProperties
    • 定义
    import org.apache.ibatis.executor.Executor;import org.apache.ibatis.executor.statement.StatementHandler;import org.apache.ibatis.plugin.*;import java.sql.Connection;import java.util.Properties;# 可以多个Signature @Intercepts({ @Signature(type= StatementHandler.class,method = "prepare",args = {Connection.class,Integer.class}), @Signature(type= Executor.class,method = "prepare",args = {Connection.class,Integer.class})})public class MyPlugin implements Interceptor { /*  拦截方法:只要被拦截的目标对象的目标方法被执行时,每次都会执行intercept方法  */ @Override public Object intercept(Invocation invocation) throws Throwable {  System.out.println("对方法:" + invocation.getMethod().getName() + " 进行了增强...." );  return invocation.proceed(); //原方法执行 } /*  主要为了把当前的拦截器生成代理存到拦截器链中  */ @Override public Object plugin(Object target) { Object wrap = Plugin.wrap(target, this); return wrap; } /*  获取配置文件的参数  */ @Override public void setProperties(Properties properties) { System.out.println("获取到的配置文件的参数是:"+properties); }}
    • 配置
    <plugins> <plugin interceptor="com.lagou.plugin.MyPlugin">  <property name="name" value="tom"/> <!-- 设置参数 --> </plugin></plugins>
    • 使用
    1. MyPlugin.plugin --> MyPlugin.intercept --> MyPlugin.setProperties 
    mybatis的第三方插件
    • PageHelper
      • 导入maven 依赖
      <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>3.7.5</version></dependency>
      • 在mybatis配置文件中配置PageHelper
      在 plugins 节点内 添加 plugin 并设置属性 `property` 方言 `dialect` 值设 `mysql`<plugin interceptor="com.github.pagehelper.PageHelper"> <property name="dialect" value="mysql"/></plugin>
    • 通用 mapper
      • 导入依赖
      <dependency> <groupId>tk.mybatis</groupId> <artifactId>mapper</artifactId> <version>3.1.2</version></dependency>
      • mybatis 的配置文件内配置 通用mapper 的plugin
      <plugin interceptor="tk.mybatis.mapper.mapperhelper.MapperInterceptor"> <!--指定当前通用mapper接口使用的是哪一个--> <property name="mappers" value="tk.mybatis.mapper.common.Mapper"/></plugin>
      • 实体类设置关联
      @Table(name = "user")public class User implements Serializable { @Id //对应的是注解id @GeneratedValue(strategy = GenerationType.IDENTITY)  // @GeneratedValue strategy 设置主键的生成策略 // GenerationType.IDENTITY 底层必须支持自增长 // GenerationType.SEQUENCY 底层不支持自增长 // GenerationType.TABLE 从表中取值 生成主键 // GenerationType.AUTO 自动选择合适的生成主键 private Integer id;  @Column(name="123") 当与表的字段不同时,采用@Column 保持同步 private String username; // Get Set 略}
      • 创建dao接口 继承tk.mybatis.mapper.common.Mapper传入泛型
      egg: public interface UserMapper extends Mapper<User> {}
      • 使用
        • 传统调用
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.
        • Example 方式调用
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.

    随堂测试01 自定义持久层框架

    1. 自定义持久层框架IPersistence是如何解决JDBC存在的问题:() [多选题]

      • [x] A. 用配置文件解决了硬编码问题
      • [x] B.使用了C3P0连接池解决了频繁创建释放数据库连接问题
      • [x] C.在simpleExecute中使用到了反射进行了参数的设置
      • [x] D.在simpleExecute中使用了内省进行了返回结果集的封装
    2. 在进行自定义持久层框架IPersistence优化时,主要为了解决那些问题:() [多选题]

      • [x] A.应用在Dao层,整个操作的模板代码重复
      • [x] B.调用sqlSession方法时、参数statementId硬编码
      • [ ] C.无法保证statementId的唯一性
      • [ ] D.参数存在硬编码
    3. 下列关于Configuration及MappedStatement配置类,说法正确的是:() [多选题]

      • [x] A.使用dom4j对sqlMapConfig.
      • [ ] B.使用dom4j对mapper.标签的内容均对应一个MappedStatement对象[x] C.Configuration中包含了MappedStatement的引用[ ] D.MappedStatement对象对应Mapper.

    原文转载:http://www.shaoqun.com/a/485790.html

    新蛋:https://www.ikjzd.com/w/79

    三维度:https://www.ikjzd.com/w/1312

    cima:https://www.ikjzd.com/w/1372


    目录自定义持久层框架jdbc代码基础回顾解决传统jdbc存在的问题自定义持久层框架设计思路使用端-->项目自定义持久层框架本身mybatis简介sqlMapConfig.传统dao层开发方式dao层开发方式mybatis外部properties文件mybatisaliasTypemapper.动态sqlmybatis复杂映射开发一对一查询一对多查询多对多查询mybatis注解开发注解一对多注
    乐一番:https://www.ikjzd.com/w/1562
    c88是什么:https://www.ikjzd.com/w/1017
    2020五一宁波奇异国门票多少钱?五一宁波奇e国门票价格?:http://tour.shaoqun.com/a/47141.html
    Wish API更新:编辑SKU相关信息等:https://www.ikjzd.com/home/118633
    2020香港五一天气怎么样?穿什么衣服好?:http://tour.shaoqun.com/a/45293.html

    No comments:

    Post a Comment