映射器:接口+xml文件(或注解)。
在映射器中可以配置参数,各类的SQL语句,存储过程,缓存,联级等,并通过建议的映射规则指定的POJO或者其他对象上。
虽然映射器可以用注解完成,但是应用不广,原因有3:
1.面对复杂性,SQL会显得无力,尤其长SQL
2.注解的可读性较差
3.功能上,求是了xml上下文相互引用的功能
一.映射器的四种引入方式
1.用文件路径引入映射器
<mappers>
<mapper resource="xxxMapper.xml"/>
</mappers>
2.用包名引入映射器
<mappers>
<package name="com.x.x.xx"/>
</mappers>
3.用类注册引入映射器
<mappers>
<mapper class="com.x.x.xx.类名"/>
</mappers>
4.用userMapper.xml引入映射器
<mappers>
<mapper url="路径/xxxMapper.xml"/>
</mappers>
二.映射器配置元素
select | 查询语句 | 可自定义参数,返回结果集等 |
insert | 插入语句 | 执行后返回整数,代表插入的条数 |
update | 更新语句 | 执行后返回整数,代表更新的条数 |
delete | 删除语句 | 执行后返回整数,代表删除的条数 |
sql | 定义一部分SQL,其他地方引用 | 复用 |
resultMap | 用来描述家在对象 | 提供映射规则 |
cache | 给定命名空间的缓存配置 | |
cache-ref | 其他命名空间缓存配置的引用 |
三.select
常用的有id,parameterType,resultType,resultMap,缓存flushCache,useCache
四.insert
主键回填(userGeneratedKeys="true" keyProperty="用来匹配主键的属性")
<insert id="insertRole" parameterType="role" userGeneratedKeys="true" keyProperty="id">
insert into t_role(role_name,note) values(#{roleName},#{note})
<insert>
自定义主键(selectKey元素),例:角色表为空,id=1;角色表不为空,id=id+3
<insert id="insertRole" parameterType="role">
<selectKey keyProperty="id" resultType="long" order="BEFORE">
select if (max(id))=null,1,max(id)+3) from t_role
</selectKey>
insert into t_role(id,role_name,note) values(${id},#{roleName},#{note})
</insert>
五.update
执行完返回整数,表示影响的数量
六.delete
执行完返回整数,表示影响的数量
七.sql
定义一条SQL的一部分,方便其他SQL引用,代码复用。例如:列名
<mapper namespace="usernp">
<!--<sql> + <include> 查询user表中id是131的记录 -->
<sql id="columns">
id,name,addr,age
</sql>
<select id="findOne" resultType="User">
select
<include refid="columns"/>
from user where id = #{id}
</select>
</mapper>
八.resultMap
resultMap定义映射规则,级联的更新,定制类型转换器等。但是目前Mybatis只支持resultMap的查询,不支持更新,保存,级联。
- Map存储(不推荐)
- POJO存储(推荐!)
(一)级联
级联分为三种,一对一,一对多,鉴别器。级联不是必须的,好处:获取关联数据方便快捷;缺点:级联过多会增加系统的复杂度,降低性能。当级联超过3层,就不使用级联!
- 一对一:学生证和学生,人和身份证
- 一对多:班主任和学生,顾客和账单
- 鉴别器:根据某些条件决定采用实现类级联的方案,比如体检表根据性别去区分。
- 没有多对多!多对多可以换成一对多的多次使用!
<resultMap type="cn.tedu.jk.domain.Contract" id="contractRM" autoMapping="true">
<--一对一级联
task和另一张表task_id级联
-->
<association property="task" colum="task_id" select="com.ssm.mapper.TaskMapper.getTask"/>
<--一对多级联
employeeTaskList表和另一张表的task_id级联
-->
<collection property="employeeTaskList" colum="task_id"
select="com.ssm.mapper.EmployeeTaskMapper.getEmployeeTaskByEmpId"/>
<--鉴别器
column代表使用哪个字段进行鉴别
-->
<discriminator javaType="long" colum="sex">
<case value="1" resultMap="maleHealthFormMapper"/>
<case value="2" resultMap="femaleHealthFormMapper"/>
</discriminator>
</resultMap>
(二)级联的N+1问题
N+1:现有N个关系完成级联,再加一个关联关系,成了N+1,所有都被执行,但是有很多不需要的被查找,造成浪费、
延迟加载:一次性把常用的级联查询出来,不常用的级联数据不查找,等需要时再取出
<settings>
<!--lazyLoadingEnabled延迟加载全局开关-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--aggressiveLazyLoading层级加载开关-->
<setting name='aggressiveLazyLoading" value="true"/>
</settings>
九.一级缓存/二级缓存(cache)
一级缓存是在SqlSession上的缓存,二级缓存是在SqlSessionFactory上的缓存。
当下次SQL和参数都没变化,且缓存不超时,则直接从缓存提取,不会再次运行。
当使用同一个sqlSession时,查询到的数据可能是一级缓存;而当使用同一个mapper是,查询到的数据可能是二级缓存。
(一)一级缓存
一级缓存不用开启,默认就是开启状态。
(二)二级缓存
Mybatis二级缓存是sqlSessionFactory级别的。更准确的说Mybatis二级缓存是在同一个namespace下共享。
对于访问多的查询请求且用户对查询结果实时性要求不高,此时可采用mybatis二级缓存技术降低数据库访问量,提高访问速度。
使用场景有一定的局限性:作用区域是一个mapper下,当mapper文件中的增删改时,对任意一个商品信息变化会将所有商品信息的缓存数据全部清空。严重影响效率。
将来都用分布式redis。
①在核心配置文件sqlMapConfig.xml中
<!--开启二级缓存-- >
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
②在UserMapper中添加<cache />
<?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">
<mapper namespace="cn.tedu.dao.UserInfoDao">
<!-- 开启当前mapper级别的二级缓存 -->
<cache></cache>
</mapper>
③序列化实体类
注意:PO对象必须序列化,当内存不够用时,二级缓存支持写到磁盘,所以PO类必须序列化。如果没有序列化,它底层会报错。
十.SQL中有特殊字符
当SQL中有特殊字符<,mybatis不能正常解析时,用<![CDATA[??]]>括起来就解决了
<![CDATA[ and age<=#{age} ]]>
十一.自动映射/驼峰映射
setting中配置,autoMappingBehavior--自动映射(一般设置为RARTIAL);mapUnderscoreToCamelCase--驼峰映射(true)
因为阿里规约,所以强制使用驼峰映射!!!
sqlMapConfig.xml中配置settings:
<settings>
<setting name="mapUnderscoreToCamelCase" value="true" />
</settings>
下面的resultMap中主键需要单独写映射,其余的开启autoMapping="true",就可以自动映射了
resultMap配置autoMapping="true"
<resultMap type="cn.tedu.jk.domain.Contract" id="contractRM" autoMapping="true">
<id property="id" column="CONTRACT_ID"/>
</resultMap>
十二.传递参数
- 使用map接口传递多个参数,不推荐(可读性差)
- 使用注解传递多个参数@Param,推荐,n<5
- 使用JavaBean传递多个参数,推荐,n>5
- 混合使用(2+3),推荐
十三.#和$
两种方式都可以获取参数的值。
推荐能用#不用$:
- #{ } (推荐!)
- 相当于JDBC中的PreparedStatement ,是经过预编译的,是安全的。
- 会为参数自动拼接引号。
- 执行SQL效果:select * from user where userId=”1” and pwd=”2”
- ${ }:
- 相当于JDBC中的Statement ,未经过预编译,仅仅是取变量的值,是不安全的,存在SQL注入。
- 执行SQL效果:select * from user where userId=1 and pwd=2
十四.模糊查询
1.参数中直接加入%%
如果要发起一条SQL,where name like #{name},name手动传入%大%这里就容易发生SQL注入问题
2.Sql中用CONCAT函数
<!-- where 2-->
<select id="findone2" resultType="User" parameterType="map">
select <include refid="cols"></include> from user
<where>
id=#{id} and addr=#{addr} and name like concat(concat('%',#{name}),'%')
</where>
</select>
---------------------
部分引用:https://blog.csdn.net/Malsako/article/details/78252104