mybatis基础

使用原生jdbc的问题
  1. 数据库连接, 使用时就创建,不使用就立即释放,对数据库进行频繁地链接开启和关闭,造成数据库资源浪费,影响数据库性能。
    解决方案: 使用数据库连接池管理数据库连接。
    2.将sql语句硬编码到java代码中,如果sql语句修改,需要重新编译java代码,不利于系统维护。
    设想:将sql 语句配置到xml文件中。
    3.向preparedStatement中设置参数,设置占位符号位置,以及对应的参数值属于硬编码,不利于系统维护。
    设想 将sql语句及占位符号和全部参数配置在xml中。
    4.从resultset中遍历结果集数据时存在硬编码,将获取表的字段进行成硬编码,不利于系统维护。
    设想:将查询结果集,自动映射成java对象。

mybatis和hibernate的区别和应用场景

hibernate:是一个ORM框架(对象关系映射),不需要程序写sql, sql语句自动生成了。
对sql语句进行优化、修改比较困难。
mybatis:专注sql本身,需要程序员自己编写sql语句,sql修改、优化比较方便。mybatis是一个不完全的ORM框架,虽然程序员自己写sql,mybatis也可以实现映射(输入映射、输出映射)。
应用场景:
适用于需求变化比较多的项目,比如:互联网项目。

mybatis 开发dao的方法

SqlSessionBuilder作为工具类创建SqlSessionFactory
SqlSessionFactory使用单例模式
SqlSession是线程不安全的,在SqlSession实现类中除了有接口中方法还有数据域属性。

总结原始dao开发问题
  1. statement 的id需要在代码中调用,属于硬编码
  2. 调用sqlsession方法时传入的变量,由于sqlsession方法使用泛型,即使变量类型传入错误,在编译阶段也不报错
  3. dao接口实现类方法中存在大量模板方法,设想能否将这些代码提取出来。

mapper 代理方法

程序员只需要实现mapper接口(相当于dao接口)
程序员还需要编写mapper.xml映射文件
需要遵循一个规范

  1. mapper.xml 中namespace 就是mapper.java 的全限定名
  2. mapper.xml 中statement 的id 就是 mapper.java 的方法名一致
  3. mapper.xml 中statement 的parameterType 指定输入参数类型和mapper.java的方法输入,参数类型一致。
  4. mapper.xml 中statement 的resultType 指定输出结果的类型和mapper.java的返回方法值类型一致。
mapper 接口方法参数只能有一个是否影响系统开发

mapper接口方法参数只能有一个,系统是否不利于扩展维护?
解决
系统框架中,dao层的代码是被业务层公用的。
即使mapper接口只有一个参数,可以使用包装类型的pojo满足不同的业务方法的需求。
注意
持久层方法参数可以包装类型,但是service 方法中建议不要使用包装类型(不利于业务层可扩展性)。

将sql连接信息写入db.properties文件中
mybatis 配置

settings

typeAliases

用于配置单个类的别名或者整个包的别名

typeHandlers

实现java类和jdbc类型的相互转化,由mybatis的一系列类型handler实现。

mapper 配置

单个映射文件加载使用
<mapper class="com.vee.mybatis.mapper.UserMapper"></mapper>
批量加载
<package name="com.vee.mybatis.mapper"></package>
使用条件
使用package进行批量加载,要求与使用class一样需要将mapper接口和xml映射文件名称保持一致,且在一个目录中。

Vo包装pojo类
  • 通过创建子类对pojo 类进行扩展
  • 创建vo类包装pojo类可以实现多表联合查询
  • 使用Vo 类可以利用成员中的pojo类或者pojo扩展类,实现多参数查询。
resultType

使用resultType 进行输出映射,只要查询出来的列名和Pojo中的属性名一致,该列才可以映射成功。
如果查询出来的列名和pojo中的属性名全部不一致,没有创建pojo对象。
只要查询出来的列名和pojo中的属性有一个一致,就会创建pojo对象。

resultMap

如果查询出来的列名和pojo的属性名不一致,通过定义一个resultMap对列名和pojo属性名之间作一个映射关系。

  • 定义resultMap
  • 使用resultMap作为statement的输出映射类型

动态sql

mysql核心 对sql语句进行灵活操作,对sql进行灵活拼接组装

例如:
对查询条件进行判断, 如果输入参数不为空才进行查询条件拼接。
使用它可以防止空指针异常

    <select id="findUserList4" parameterType="UserQueryVo" resultType="UserCustom">
        SELECT * FROM USER
        <where>
          <if test="userCustom.uid!=0">
              AND uid = #{userCustom.uid}
          </if>
          <if test="userCustom.uname!=null">
              AND uname LIKE '%${userCustom.uname}%'
          </if>
        </where>
  </select>
sql 片段

将上边实现的动态sql 判断代码块抽取出来,组成一个sql 片段。其他的statement中就可以引用sql片段。

  • sql 片段可以提高sql代码的可重用性
  • sql 片段建议基于单表
  • sql 片段不用包含where防止使用多个sql片段时where 重复
foreach

向sql传递数组或List,mybatis 使用foreach解析
例如如下的sql语句
select * from user where id=1 or id=2 or id=10
或者
select * from user where id IN(1,2,10)
这就需要在输入参数类型中添加List<Integer> ids

<select id="findUserList6" parameterType="UserQueryVo2" resultType="UserCustom">
        SELECT * FROM USER
        <where>
            <if test="userCustom!=null">
                AND uname LIKE '%${userCustom.uname}%'
            </if>
            <if test="ids!=null">
                <foreach collection="ids" item="id" open="and (" close=")" separator="or">
                    uid=#{id}
                </foreach>
            </if>
        </where>
    </select>
一对一查询

做法:
对需要用到的属性较多的类进行扩展,并将另一个类的属性作为字段传入。
使用resultMap
在<resultMap>标签中使用<association>标签映射关联查询单个对象的信息,property:将要关联查询的用户信息映射到Orders中哪个属性。

总结:
resultType:如果没有查询结果的特殊要求建议使用resultType,如果pojo中没有包括查询出来的列名,需要在扩展类中增加列名对应的属性。
resultMap:需要单独定义resultMap,如果对查询结果有特殊要求(列名和pojo中的属性名称不匹配)可以使用resulMap对属性名和列名进行映射。

一对多查询

做法:

  • 使用resultType,创建新pojo包含必要属性,然后返回pojo列表
  • 使用resultMap,扩展类添加List<>属性,需要使用<resultMap>内部标签中的<collection>标签。
多对多查询

resultMap的可以在pojo中有List的时候在<resultMap>标签中定义collection,并且collection中可以递归定义collection,但是这种场景往往使用resultType ,利用扩展的pojo类也可以很轻松地完成功能。
但是有的需求确实需要一个类有一个list成员,这个时候还是resultMap适合。

resultMap和resultType小结

resultType和resultMap都可以完成高级结果映射,如果没有特殊要求,使用resultType方便,resultType和resultMap 的一个主要区别就是要应用场景不同resultType主要是查询明细使用,resultMap主要是层级查询使用,比如查询用户信息,如何点击查询订单,再去查询订单信息,resultMap可以实现延迟加载,而resultType没有该功能。

延迟加载

resultMap 可以实现高级映射(使用association、实现一对一和一对多映射),association、collection具备延迟加载功能。
延迟加载:先从单表查询、需要时再从关联表去关联查询,大大提高数据库性能。
使用resultMap 中的association 去实现延迟加载。
select 属性可以执行statement的id

  • mybatis默认没有开启延迟加载,需要在sqlMapConfig.xml中setting 配置。
设置项 描述 允许值 默认值
lazyLoadingEnable Globally enables or disables lazy loading. When enabled, all relations will be lazily loaded. true/false false
aggressiveLazyLoading(不常用) When enabled, any method call will load all the lazy properties of the object. Otherwise, each property is loaded on demand true/false false (true in ≤3.4.1)
  • 不使用延迟加载也是可以完成功能的,延迟加载的本质是延迟sql查询的时间,那么使用不同的mapper也可以完成先单表查询,然后一些属性按需查询的功能

resultType、resultMap、延迟加载使用场景总结

  • 延迟加载:
    mybatis 提供 的延迟加载功能用于service 层,要在对象消亡之前进行
  • resultType
    将明细记录映射到对应的pojo中
  • resultMap
    使用association 和collection 完成一对一和一对多的高级映射
  • association
    将关联查询信息映射到一个pojo类中。
  • collection
    将关联查询信息映射到到一个list集合中。

查询缓存

使用缓存可以将热数据放在内存中,用户查询数据就不用从磁盘上查询,这样可以提高查询效率,解决了高并发系统的性能问题。
mybatis中二级缓存生存与sqlSession中,二级缓存是mapper级别的缓存,二级缓存是可以被不同的sqlSession共享的。

一级缓存

第一次查询一个sql,sql 查询结果写入sqlsession 缓存中,同一个sqlsession 再次发出相同的sql ,就从缓存中取。如果两次中间出现了commit曹组,本sqlsession 中的一级缓存区域就全部清空。
mybatis默认支持一级缓存,但是mybatis 和spring整合后进行mapper代理开发,不支持一级缓存。mybatis 和spring整合,spring按照mapper模板去生成mapper代理对象,模板中在最后统一关闭了sqlsession。

二级缓存

二级缓存的范围是mapper级别(mapper同一个命名空间),mapper以命名空间为单位创建缓存数据结构。

  • mybatis开启二级缓存
    <setting name="cacheEnabled" value="true">
    还需要在mapper.xml中添加<cache />标签。
  • 查询结果映射的pojo需要序列化,实现java.io.serializable接口。
    二级缓存可以将内存的数据写到磁盘,存在对象的序列化和反序列化,所以要实现java.io.serializable 接口。
  • 一个sqlSession 在close的时候会将缓存写入到公有缓存(磁盘上)。
  • 单个statement 禁止使用二级缓存。
    <select id="findUserList6" parameterType="UserQueryVo" resultType="UserCustom" useCache="false">
    对于变化频率较高的select,需要禁用二级缓存,即该sql使用二级缓存。
  • 如果sqlSession进行commit操作,刷新缓存(全局清空)。
    设置statement 的flushCache 是否刷新缓存,默认值是true,设置为false可能导致脏读。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容