mybatis笔记1 mybatis应用

mybatis是什么

ORM-对象关系数据库映射
Mybatis是⼀个半⾃动化的持久层框架,sql映射框架。

使用

配置文件

SqlMapConfig

在这里插入图片描述
最重要是事务管理器和数据源的配置

mapper.xml

简单示例:

<mapper namespace="com.lagou.mapper.OrderMapper">
	<resultMap id="orderMap" type="com.lagou.domain.Order">
		<result column="uid" property="user.id"></result>
		<result column="username" property="user.username"></result>
		<result column="password" property="user.password"></result>
		<result column="birthday" property="user.birthday"></result>
	</resultMap>
	<select id="findAll" resultMap="orderMap">
		select * from orders o,user u where o.uid=u.id
	</select>
</mapper>

详细示例

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
//namespace:命名空间 与mapper.java文件名对应
<mapper namespace="com.sinosoft.mapper.AccOpenBankMapper">
	//resultMap 封装结果集用于将查询的结果映射到实体类,Mybatis最强大的功能。
	//可以省略配置的情况:实体类中属性没有其他实体类的引用,全是Java自带的类,并且属性名与数据库字段名一样
	//适用情况:	1.实体类的属性名与数据库字段名不同
			2.实体类中属性有其他实体类的引用,即关联关系
	<resultMap id="BaseResultMap" type="com.sinosoft.entity.AccOpenBank">
		<id column="BANKCODE" jdbcType="VARCHAR" property="BANKCODE" />
	        <result column="BANKNAME" jdbcType="VARCHAR" property="bankName" />
	        <result column="CREATEDATE" jdbcType="DATE" property="createDate" />
	</resultMap>
	
	//select 这类标签对应数据库的四种基本语句:select,delete,update,insert
	//id:与Mapper.java文件中的抽象方法名相同 
	//parameterType:可以省略(经测试,1.java基本类型的封装类,2.Date,String这样的Java自带类,3.只包含1和2的属性的实体类 是没有问题的)
	//resultType:返回的结果集
	<select id="qryAccOpenBankByMybatis" parameterType="com.sinosoft.dto.AccOpenBankDTO" resultType="com.sinosoft.dto.AccOpenBankDTO">
		select 
		bankCode||bankName  bankCode,
		//include:引用其他SQL语句块
		<include refid="queryCondition"/>  
		<include refid="orderStatement"/> 
	</select>
	//SQL:提取出公共的SQL语句块并定义ID,用include标签通过ID引用
	<sql id="queryCondition">
		//where:相当于SQL语句中的where
		<where>
			//相当于SQL语句中的if,可以去重(针对and和or)
			//test:这个属性比较坑
			//bankCode!=null 不为null
			//bankCode!='' 不等于空字符串
			//and 
			//or
			//集合.size 集合的长度
			//bankCode=='yyy'.toString() 不支持bankCode=='yyy'这种写法 但是 bankCode==12是可以的,虽然bankCode是个字符创 于字符串比较 
			<if test="bankCode!=null and bankCode!=''">
				//一些超长的SQL语句可能报错,<![CDATA[  ]]>可以表示转化成文本,可以处理这个错误
				// ||用于连接表达式和字符串成一个整体
				<![CDATA[ and bankCode like '%'||#{bankCode}||'%' ]]>
			</if>
			<if test="bankName!=null and bankName!=''">
				<![CDATA[ and bankName like '%'||#{bankName}||'%' ]]>
			</if>
		</where>
	</sql>
	
	<sql>	//foreach:便利集合用的标签 
		//item:遍历集合时,每次集合中的元素的变量名
		//index:循环的下标
		//collection:最奇葩的是这个
			//方法中只有一个集合参数,无论它的变量名是什么,都用list。但当它是作为一个对象的属性传入时就可以用属性名调用。真无语...
		//open="(" :以"("开始
		//separator="," : 分隔符是","
		//close=")" : 以")"结束
		<foreach item="item" index="index" collection="list" open="(" separator="," close=")">
			#{item}
		</foreach>
	</sql>
	
	<sql id="orderStatement">
		<if test="sort!= null and sort!=''">
			order by ${sort} ${order}
		</if>
	</sql>
	<select id="findById" parameterType="String" resultType="com.sinosoft.dto.AccOpenBankDTO">
		//* 最好不要使用:1.sql执行效率问题 2.返回值可能绑定的有问题
		select * from accOpenBank where bankCode = #{bankCode}
	</select>
</mapper>
resultMap
关联关系
一对一
<resultMap id="orderMap" type="com.lagou.domain.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.domain.User">
		<result column="uid" property="id"></result>
		<result column="username" property="username"></result>
		<result column="password" property="password"></result>
		<result column="birthday" property="birthday"></result>
	</association>
</resultMap>
一对多
<resultMap id="userMap" type="com.lagou.domain.User">
<result column="id" property="id"></result>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="birthday" property="birthday"></result>
<collection property="orderList" ofType="com.lagou.domain.Order">
	<result column="oid" property="id"></result>
	<result column="ordertime" property="ordertime"></result>
	<result column="total" property="total"></result>
</collection>
</resultMap>
<select id="findAll" resultMap="userMap">
	select *,o.id oid from user u left join orders o on u.id=o.uid
</select>
多对多
<resultMap id="userRoleMap" type="com.lagou.domain.User">
	<result column="id" property="id"></result>
	<result column="username" property="username"></result>
	<result column="password" property="password"></result>
	<result column="birthday" property="birthday"></result>
	<collection property="roleList" ofType="com.lagou.domain.Role">
		<result column="rid" property="id"></result>
		<result column="rolename" property="rolename"></result>
	</collection>
</resultMap>
<select id="findAllUserAndRole" resultMap="userRoleMap">
	select u.*,r.*,r.id rid from user u left join user_role ur on u.id=ur.user_id inner join role r on ur.role_id=r.id
</select>
sql
占位符
  • #{} 预编译,防止sql注入
  • ${} 字符串连接,不能防sql注入
动态sql
  • if
  • choose , when, otherwise
  • trim, where, set
  • foreach
原理

在这里插入图片描述

执行
  1. SqlResource
    该接⼝含义是作为sql对象的来源,通过该接⼝可以获取sql对象。其唯⼀的实现类是XmlSqlResource,表示通过xml⽂件⽣成sql对象。
  2. Sql
    该接⼝可以⽣成sql语句和获取sql相关的上下⽂环境(如ParameterMap、 ResultMap等),有三个实现类: RawSql表示为原⽣的sql语句,在初始化即可确定sql语句; SimpleDynamicSql表示简单的动态sql,即sql语句中参数通过 p r o p e r t y property property⽅式指定,参数在sql⽣成过程中会被替换,不作为sql执⾏参数; DynamicSql表示动态sql,即sql描述⽂件中包含isNotNull、 isGreaterThan等条件标签。
  3. SqlChild
    该接⼝表示sql抽象语法树的⼀个节点,包含sql语句的⽚段信息。该接⼝有两个实现类: SqlTag表示动态sql⽚段,即配置⽂件中的⼀个动态标签,内含动态sql属性值(如prepend、 property值等); SqlText表示静态sql⽚段,即为原⽣的sql语句。每条动态sql通过SqlTag和SqlText构成相应的抽象语法树。
  4. SqlTagHandler
    该接⼝表示SqlTag(即不同的动态标签)对应的处理⽅式。⽐如实现类IsEmptyTagHandler⽤于处理标签, IsEqualTagHandler⽤于处理标签等。
  5. SqlTagContext
    ⽤于解释sql抽象语法树时使⽤的上下⽂环境。通过解释语法树每个节点,将⽣成的sql存⼊SqlTagContext。最终通过SqlTagContext获取完整的sql语句。
设计模式

解释器模式: 初始化过程中构建出抽象语法树,请求处理时根据参数对象解释语法树,⽣成sql语句。
⼯⼚模式: 为动态标签的处理⽅式创建⼯⼚类(SqlTagHandlerFactory),根据标签名称获取对应的处理⽅式。
策略模式: 将动态标签处理⽅式抽象为接⼝,针对不同标签有相应的实现类。解释抽象语法树时,定义统⼀的解释流程,再调⽤标签对应的处理⽅式完成解释中的各个⼦环节。

mapper方法不能重载

原因:mapper接口的方法 -> xml方法 接⼝全限名+⽅法名 定位到 MappedStatement

缓存

一级缓存

  • 默认开启
  • 存储结构:hashMap
  • 范围:SqlSession
  • 判断相同:
  1. statementId
  2. sql(boundSql.getSql())
  3. 参数
  4. 结果集中的结果范围
  • 失效场景:
  1. SqlSession.close()
  2. SqlSession.clearCache()
  3. SqlSession中执行了更新操作(insert,delete,update)
问题
  1. 多个mapper是否有问题,需确认
  2. 查出后修改值,再查是否是修改后的值,id:1->2,需确认
  3. 注意事务隔离级别,分布式环境下,读完有缓存,另一个session更新了数据库,再读是取得缓存,内容是更新前的。如果是读已提交,则完全没有问题。如果是可重复读,则需要看业务场景。

二级缓存

默认不开启

  • 存储结构:hashMap
  • 范围:mapper.namespace,是跨sqlSession的
  • 失效场景:
  1. 被缓存淘汰策略淘汰
二级缓存的缺点

namespace的缓存范围,如果一个表有多个mapper.xml文件,就会导致数据不一致的问题
一般我们在需要跨sqlsession范围使用缓存,就考虑使用缓存中间件了,比如redis

二级缓存整合redis

延迟加载

延迟加载是什么

一对一,一对多,多对多的关联关系中,对主表的查询时不一起查询关联表,等待需要的时候再查询。

延迟加载使用

<settings>
    <setting name="lazyLoadingEnabled" value="true"/><!-- 开启延迟加载,默认为false:不开启 -->
    <setting name="aggressiveLazyLoading" value="false"></setting><!-- 侵略式延迟加载,默认为true:开启 -->
</settings>
<mapper namespace="cn.xh.dao.UserDao">
	<resultMap id="categoryMap" type="cn.xh.pojo.Category">
        <id column="cid" property="cid"></id>
        <result column="cname" property="cname"></result>

        <collection property="books" column="cid" select="findBookWithLazy"></collection>
    </resultMap>
    
    <select id="findCategoryWithLazingload" resultMap="categoryMap">
        select * from category
    </select>

    <select id="findBookWithLazy" parameterType="int" resultType="cn.xh.pojo.Book">
        select * from book where cid = #{cid}
    </select>
</mapper>

延迟加载的方式

  • 直接加载
  • 侵入式延迟加载:执行对主加载对象的查询时,不会执行对关联对象的查询。但当要访问主加载对象的详情属性时,就会马上执行关联对象的select查询。
  • 执行对主加载对象的查询时,不会执行对关联对象的查询。访问主加载对象的详情时也不会执行关联对象的select查询。只有当真正访问关联对象的详情时,才会执行对关联对象的 select 查询。

Mybatis与Hibernate的对比

综述

  • Hibernate 是全自动完全的ORM框架,对数据库结构提供了较为完整的封装
  • mybatis 是半自动轻量级ORM框架,主要是sql和pojo的映射

开发难度和学习成本

Hibernate 单表操作简单,复杂操作(对象的状态管理,关联关系映射,hql,动态查询,sql优化)难,整体学习成本高
mybatis 有插件或者mybatis plus支持下,单表操作简单,复杂操作也不难,整体学习成本低

缓存

Hibernate 缓存比较完善
mybatis 不太行

插件

  • 开源框架都会提供插件或其他形式的拓展点,供开发者⾃⾏拓展。
  • MyBatis提供的是插件机制,即4大组件的拦截器
  • 可以实现分⻚、分表,监控等功能
  • 常用插件:分页插件,通用Mapper插件

可以拦截的方法

  • 执⾏器Executor (update、 query、 commit、 rollback等⽅法);
  • SQL语法构建器StatementHandler (prepare、 parameterize、 batch、 updates query等⽅ 法);
  • 参数处理器ParameterHandler (getParameterObject、 setParameters⽅法);
  • 结果集处理器ResultSetHandler (handleResultSets、 handleOutputParameters等⽅法);

使用

  • 继承Interceptor接口,并注解声明器连接的类,方法等
  • 配置到sqlMapConfig.xml中
@Intercepts({
	@Signature(
		type = Executor.class,
		method = "query",
		args={MappedStatement.class,Object.class,RowBounds.class,ResultHandler.class}
	)
})
public class ExeunplePlugin implements Interceptor {
	//省略逻辑
}
<plugins>
	<plugin interceptor="com.lagou.plugin.ExamplePlugin">
	</plugin>
</plugins>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值