1.接口绑定
接口绑定,就是在MyBatis中任意定义接口,然后把接口里面的方法和SQL语句绑定,我们直接调用接口方法就可以,这样比起原来了SqlSession提供的方法我们可以有更加灵活的选择和设置。
mybatis是如何通过代理模式实现查询的:底层使用了动态代理模式,动态创建一个代理对象并赋给接口引用。所以在MyBatis中不需要显式提供Mapper接口的实现类,这也是简单的地方。
映射文件:
注意:
接口名和sql映射文件名字保持一致并放在同一个包下
sql映射文件的namespace要定义为其绑定接口的包名.类名形式
sql标签的id属性必须与抽象方法名保持一致
返回值类型和参数类型与方法的返回值和参数保持一致
配置接口:
<!-- mybatis.xml文件中:mapper配置扫描接口 -->
<mappers>
<package name="com.xxx.mapper"/>
</mappers>
接口绑定有两种实现方式:
1.通过注解绑定,就是在接口的方法上面加上 @Select、@Update等注解,里面包含Sql语句来绑定。
2.通过xml里面写SQL来绑定。这时要指定xml映射文件里面的namespace必须为接口的全路径名。
总结:当Sql语句比较简单时候,用注解绑定, 当SQL语句比较复杂时候,用xml绑定,一般用xml绑定的比较多。
通过接口绑定解决多参数传递问题:
1.接口中定义方法
List<Emp> queryEmpBySalDeptno(double sal, int deptno); //根据薪资与部门一起查询
映射文件中提供对应的标签通过#{arg+数字}或#{param+数字}的方式
select * from emp where sal>#{arg0} and deptno=#{arg1}
select * from emp where sal>#{param1} and deptno=#{param2}
2.接口中定义方法, 参数中使用@Param 注解设定参数名用于在 SQL 语句中使用
List<Emp> queryEmpBySalDeptno(@Param("sal") double sal, @Param("dno") int deptno);
映射文件中提供对应的标签. 此时, SQL 语句中获取方式有两种, 通过#{参数名称}或#{param+数字}的
方式
select * from emp where sal>#{sal} and deptno=#{dno}
2.动态 SQL
1.if..where
where:代替恒等式 where 1=1
用于管理 where 子句. 有如下功能:
如果没有条件, 不会生成 where 关键字
如果有条件, 会自动添加 where 关键字
如果第一个条件中有 and, 去除之
select * from emp <!--where 1=1-->
<!--
where :代替恒等式 where 1=1
在有条件的时候回自动在当前select语句中拼接where关键字
在没有条件的时候,不会拼接where关键字
-->
<where>
<!--
if 标签用户做条件判断
test:用于设置判断条件,类似if语句的()
会自动帮助把第一个满足条件的if标签中的and关键字去掉
-->
<if test="ename!=null and ename!=''">
and ename = #{ename}
</if>
<if test="deptno!=0 and deptno!=null">
and deptno = #{deptno}
</if>
</where>
2.choose...when...otherwise
choose标签是按顺序判断其内部when标签中的test条件出否成立,如果有一个成立,则 choose 结束。当 choose 中所有 when 的条件都不满则时,则执行 otherwise 中的sql。
<!--choose...when...otherwise-->
<select id="queryEmpByIdName" resultType="emp">
select * from emp
<where>
<choose> <!--相当于switch-->
<when test="empno!=0"> <!--case-->
and empno=#{empno}
</when>
<when test="ename!=null and ename!=''"> <!--case-->
and ename=#{ename}
</when>
<otherwise></otherwise>
</choose>
</where>
</select>
3.set
用于维护 update 语句中的 set 子句,与where标签类似功能如下:
满足条件时, 会自动添加 set 关键字
不满足条件时, 不会生成 set 关键字
会去除 set 子句中多余的逗号
<update id="updUser" parameterType="user">
update t_user
<set>
id=#{id},<!-- 防止所有条件不成立时的语法错误 -->
<if test="username != null and username != ''">
username=#{username},
</if>
<if test="password != null and password != ''">
password=#{password},
</if>
</set>
where id=#{id}
</update>
4.trim
用于在前后添加或删除一些内容,可通过拼接实现类似set的效果。
<update id="updateEmp2" parameterType="Emp">
update emp
<!-- trim
1. prefix, 在前面添加内容
2. prefixOverrides, 从前面去除内容
3. suffix, 向后面添加内容
4. suffixOverrides, 从后面去除内容
-->
<trim prefix="set" suffixOverrides="," >
ename=#{ename},
<if test="sal!=0">
sal = #{sal}
</if>
</trim>
where empno = #{empno}
</update>
5.bind
用于对数据进行再加工, 用于模糊查询。
<select id="queryEmpByName" parameterType="string" resultType="emp">
<bind name="ename" value="'%'+ename+'%'"/>
select empno,ename,hiredate,job,sal,comm,deptno from emp where ename like #{ename}
</select>
6.sql...include(封装)+foreach
用于在 SQL 语句中遍历集合参数, 在 in 查询中使用
collection: 待遍历的集合
open: 设置开始符号
item: 迭代变量
separator: 项目分隔符
close: 设置结束符
<!--封装-->
<sql id="empField">
empno,ename,hiredate,job,sal,comm,deptno
</sql>
<!--foreach
1. collection: 待遍历的集合
2. open: 设置开始符号
3. item: 迭代变量
4. separator: 项目分隔符
5. close: 设置结束符
-->
<select id="queryEmpByIdSome" parameterType="list" resultType="emp">
select <include refid="empField" /> from emp where empno in
<foreach collection="list" open="(" item="item" separator="," close=")">
#{item}
</foreach>
</select>
3. 列名和属性名不一致问题
如果查询时使用 resultType 属性, 表示采用 MyBatis 的Auto-Mapping(自动映射)机制, 即相同的列名(结果集中的字段名)和属性名会自动匹配。
1.设置别名
较麻烦,注解绑定考虑使用。
2.resultMap
(1)不同名的字段必须手动设置映射关系
(2)同名的字段可以不设置,默认会根据自动映射机制找同名
<!--自定义结果的映射关系-->
<resultMap id="deptMap" type="Dept">
<!--id : 定义主键字段与属性的映射关系-->
<id column="deptno" property="id"/>
<!--id : 定义非主键字段与属性的映射关系-->
<result column="dname" property="name"/>
<result column="loc" property="loc"/>
</resultMap>
<!--resultMap-->
<select id="queryAll" resultMap="deptMap">
select * from dept
</select>
resultMap 的关联方式实现多表查询(一对一):
通过association标签指定对象属性的映射关系,javaType 属性表示当前对象, 可以写全限定路径或别名。
<mapper namespace="com.msb.mappers.EmpMapper">
<!--自定义结果集中的字段与javabean类中属性的映射关系-->
<resultMap id="empDept" type="Emp">
<!--主键字段与属性的映射关系-->
<id column="empno" property="empno"></id>
<!--非主键字段与属性的映射关系-->
<result column="ename" property="ename"></result>
<result column="job" property="job"></result>
<result column="mgr" property="mgr"></result>
<result column="hiredate" property="hiredate"></result>
<result column="sal" property="sal"></result>
<result column="comm" property="comm"></result>
<result column="deptno" property="deptno"></result>
<!--association : 定义javabean类型属性与字段的映射关系-->
<association property="pddDept" javaType="Dept">
<id column="deptno" property="id"></id>
<result column="dname" property="name"></result>
<result column="loc" property="loc"></result>
</association>
</resultMap>
<select id="queryEmpDept" resultMap="empDept" >
select empno,ename,job,mgr,hiredate,sal,comm,emp.deptno,dname,loc from emp join dept on emp.deptno = dept.deptno
</select>
</mapper>
resultMap 的关联方式实现多表查询(一对多)
通过collection标签指定集合属性泛型的映射关系,ofType 属性表示集合的泛型, 可以写全限定路径或别名。
<mapper namespace="com.msb.mappers.DeptMapper">
<!--自定义结果的映射关系-->
<resultMap id="deptMap" type="dept">
<!--id : 定义主键字段与属性的映射关系-->
<id column="deptno" property="id"></id>
<!--id : 定义非主键字段与属性的映射关系-->
<result column="dname" property="name"></result>
<result column="loc" property="loc"></result>
<!--属性为List集合,使用collection标签定义 ofType:集合中数据的类型-->
<collection property="empList" javaType="List" ofType="emp">
<id property="empno" column="empno" />
<result property="ename" column="ename" />
<result property="job" column="job" />
<result property="mgr" column="mgr" />
<result property="hiredate" column="hiredate" />
<result property="sal" column="sal" />
<result property="comm" column="comm" />
<result property="deptno" column="deptno" />
</collection>
</resultMap>
<select id="queryAll" resultMap="deptMap">
select dept.deptno,dname,loc,empno,ename,job,mgr,hiredate,sal,comm from dept left join emp on dept.deptno=emp.deptno
</select>
</mapper>
4. Mybatis缓存机制
一级缓存
默认开启. 线程级别的缓存, SqlSession 的缓存;
在一个 SqlSession 生命周期中有效. SqlSession 关闭,缓存清空;
在同一个 SqlSession 中, Mybatis 会把执行的方法和参数通过算法生成缓存的键值, 将键值和结果存放在一个 Map 中, 如果后续的键值一样, 则直接从 Map 中获取数据;
不同的 SqlSession 之间的缓存是相互隔离的;
用一个 SqlSession, 可以通过配置使得在查询前清空缓存;flushCache="true"
任何的 UPDATE, INSERT, DELETE 语句都会清空缓存。
二级缓存
进程级别的缓存, SqlSessionFactory 的缓存
在一个SqlSessionFactory生命周期中有效. 可以在多个SqlSession 生命周期中共享
默认关闭, 需要使用的时候, 要为某个命名空间开启二级缓存(在 mapper.xml 中配置cache)
<!-- 开启二级缓存, 要求实体类进行序列化 -->
<cache />
由于在更新时会刷新缓存,因此需要注意使用场合:查询频率很高, 更新频率很低时使用, 即经常使用 select, 相对较少使用delete, insert, update。
缓存是以namespace 为单位的,不同namespace下的操作互不影响。但刷新缓存是刷新整个
namespace 的缓存, 也就是你 update 了一个, 则整个缓存都刷新了。
最好在 「只有单表操作」 的表的 namespace 使用缓存, 而且对该表的操作都在这个 namespace中。 否则可能会出现数据不一致的情况。
二级缓存应用场景:
对于访问多的查询请求并且用户对查询结果实时性要求不高的情况下,可采用mybatis二级缓存,降低数据库访问量,提高访问速度,如电话账单查询。
根据需求设置相应的flushInterval:刷新间隔时间,比如三十分钟,24小时等。
5. 注解开发
CRUD注解:简单的考虑用注解
@Select: 类似于select标签
@Insert: 类似于insert标签
@Update: 类似于update标签
@Delete: 类似于delete标签
// 查询所有的部门信息
@Select("select deptno id, dname name, loc from dept")
public List<Dept> queryAll();
//插入一个新的部门信息
// @Insert("insert into dept values(default, #{name}, #{loc})")
@Insert("insert into dept values(#{id}, #{name}, #{loc})")
int insertDept(Dept dept);
//根据部门编号修改部门名称
@Update("update dept set dname=#{name}, loc=#{loc} where deptno=#{id}")
int updateDept(Dept dept);
//根据部门编号删除
@Delete("delete from dept where deptno=#{no}")
int deleteDept(int no);