MyBatis
一、动态sql
动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
1.if
用于进行条件判断, test 属性用于指定判断条件. 为了拼接条件, 在 SQL 语句后强行添加 1=1 的恒成立条件。
映射文件(EmpMapper.xml)下:
<!--if 当你传过来的参数符合下面if的条件时,则安下面的语句来执行,如果都不符合,就执行where 1=1-->
<select id="selectIf" resultType="Emp" parameterType="Emp">
select * from emp where 1 = 1
<if test="empno != null and empno != '' ">and empno=#{empno}
</if>
<if test="ename != null and ename != '' ">and ename=#{ename}
</if>
</select>
接口下:
Emp selectIf(Emp emp);
测试类下:
Emp emp = new Emp();
emp.setEmpno(7369);
Emp emp1 = mapper.selectIf(emp);
System.out.println(emp1);
2.where
用于管理 where 子句. 有如下功能:
- 如果没有条件, 不会生成 where 关键字
- 如果有条件, 会自动添加 where 关键字
- 如果第一个条件中有 and, 去除之
映射文件(EmpMapper.xml)下:
<select id="selectWhere" resultType="Emp" parameterType="Emp">
select * from emp
<where>
<if test="empno != null and empno != ''"> and empno=#{empno} </if>
<if test="ename != null and ename != ''"> and ename=#{ename} </if>
</where>
</select>
接口下:
Emp selectWhere(Emp emps);
测试类下:
Emp emp = new Emp();
emp.setEmpno(7369);
emp.setEname("SMITH");
Emp emp1 = mapper.selectWhere(emp);
System.out.println(emp1);
3.choose…when…otherwise
这是一套标签, 功能类似于 switch…case…
映射文件(DeptMapper.xml)下:
<!--根据部门地址和部门的名称查询部门信息-->
<select id="selectByNameLoc" parameterType="Dept" resultType="Dept">
select * from dept
<where>
<choose>
<when test="dname!=null and dname!=''">
and dname = #{dname}
</when>
<when test="loc!=null and loc!=''">
and loc = #{loc}
</when>
<otherwise>
and 1=1
</otherwise>
</choose>
</where>
</select>
接口下:
List<Dept> selectByNameLoc(Dept dept);
测试类下:
//根据部门地址或者部门的名称查询部门信息
List<Dept> depts = mapper.selectByNameLoc(new Dept(null,"AAAA","ABC"));
depts.forEach(System.out::println);
4.set
用于维护 update 语句中的 set 子句,功能如下:
- 满足条件时, 会自动添加 set 关键字
- 会去除 set 子句中多余的逗号
- 不满足条件时, 不会生成 set 关键字
映射文件(DeptMapper.xml)下:
<!-- 根据部门编号修改部门信息-->
<update id="updateBySomething" parameterType="Dept">
update dept
<set>
deptno=#{deptno},
<if test="dname!=null and dname!=''">
dname=#{dname},
</if>
<if test="loc!=null and loc!=''">
loc=#{loc},
</if>
</set>
where deptno=#{deptno}
</update>
接口下:
Integer updateBySomething(Dept dept);
测试类下:
Integer integer = mapper.updateBySomething(new Dept(43, null, "ABC"));
System.out.println(integer);
5.trim
用于在前后添加或删除一些内容
- prefix, 在前面添加内容
- prefixOverrides, 从前面去除内容
- suffix, 向后面添加内容
- suffixOverrides, 从后面去除内容
映射文件(DeptMapper.xml)下:
<!-- 根据部门编号修改部门信息-->
<update id="updateDeptByDeptno" parameterType="Dept">
update dept
/*由于加了后缀 bb 所以无法正常执行 */
<trim prefix="set" prefixOverrides="a" suffix="bb" suffixOverrides=",">
dname=#{dname},
</trim>
where deptno=#{deptno}
</update>
接口下:
Integer updateDeptByDeptno(Dept dept);
Integer integer = mapper.updateDeptByDeptno(new Dept(43, "ZZZZ", "ABC"));
System.out.println(integer);
6.bind
用于对数据进行再加工, 用于模糊查询
映射文件(DeptMapper.xml)下:
<!--查询部门名称中包含 'A' 的部门信息-->
<select id="selectLike" parameterType="Dept" resultType="Dept">
select * from dept
<where>
<if test="dname!=null and dname!=''">
<bind name="dname" value="'%' + dname + '%'" />
dname like #{dname}
</if>
</where>
</select>
接口下:
List<Dept> selectLike(Dept dept);
测试类下:
List<Dept> depts = mapper.selectLike(new Dept(null, "A", null));
System.out.println(depts.size());
7.foreach
用于在 SQL 语句中遍历集合参数, 在 in 查询中使用
- collection: 待遍历的集合
- open: 设置开始符号
- item: 迭代变量
- separator: 项目分隔符
- close: 设置结束符
前面已用过。
8.sql…include
sql用于提取 SQL 语句, include用于引用 SQL 语句
映射文件(DeptMapper.xml)下:
<!--公共的sql片段,可以被其他的标签包含-->
<sql id="pubsql">
deptno dno, dname,loc
</sql>
<!--查询所有的部门信息-->
<select id="selectAll" resultType="Dept">
select
/* 包含指定的sql片段*/
<include refid="pubsql" />
from dept
</select>
接口下:
List<Dept> selectAll();
测试类下:
List<Dept> depts = mapper.selectAll();
depts.forEach(System.out::println);
二、Mybatis缓存机制(了解)
mybatis 也提供了对缓存的支持,分为一级缓存和二级缓存。但是在默认的情况下,只开启一级缓存。
1.一级缓存
- 默认开启. 线程级别的缓存, SqlSession 的缓存;
- 在一个 SqlSession 生命周期中有效. SqlSession 关闭,缓存清空;
- 在同一个 SqlSession 中, Mybatis 会把执行的方法和参数通过算法生成缓存的键值,将键值和结果存放在一个 Map 中,如果后续的键值一样,则直接从 Map 中获取数据;
- 不同的 SqlSession 之间的缓存是相互隔离的;
- 用一个 SqlSession,可以通过配置使得在查询前清空缓存;flushCache=“true”
- 任何的 UPDATE, INSERT, DELETE 语句都会清空缓存。
2.二级缓存
- 进程级别的缓存, SqlSessionFactory 的缓存
- 在一个SqlSessionFactory生命周期中有效. 可以在多个SqlSession 生命中期中共享.
- 默认关闭, 需要使用的时候, 要为某个命名空间开启二级缓存(在 mapper.xml 中配置cache).
- 由于在更新时会刷新缓存,因此需要注意使用场合:查询频率很高,更新频率很低时使用,即经常使用 select, 相对较少使用delete, insert, update。
- 缓存是以 namespace 为单位的,不同 namespace 下的操作互不影响。但刷新缓存是刷新整个 namespace 的缓存,也就是你 update 了一个,则整个缓存都刷新了。
- 最好在「只有单表操作」的表的 namespace 使用缓存,而且对该表的操作都在这个namespace 中。否则可能会出现数据不一致的情况。
映射文件(DeptMapper.xml)下:
<!-- 开启二级缓存, 要求实体类进行序列化 -->
<cache/>
三、列名和属性名不一致问题
1. 给列取别名
查询时, 可以通过列别名的方式将列名和属性名保持一致,继续使用自动映射, 从而解决该问题. 但是较为麻烦。
2.使用resultMap
resultMap用于自定义映射关系, 可以由程序员自主制定列名和属性名的映射关系. 一旦使用resultMap, 表示不再采用自动映射机制。
映射文件(EmpMapper.xml)下:
<resultMap id="empmap" type="Emp">
<!-- 需要写全了-->
<!--id标签一般就用于主键,不用也行 property:类的属性名 column:表中的字段名-->
<id column="empno" property="no"/>
<result column="ename" property="ename"/>
<result column="mgr" property="mgr"/>
</resultMap>
<select id="selectAllMap" resultMap="empmap" >
select * from emp
</select>
接口中:
List<Emp> selectAllMap();
测试类下:
List<Emp> empList = mapper.selectAllMap();
empList.forEach(System.out::println);
四、关系映射查询
1.resultMap 的关联方式实现多表查询(一对一|多对一)
映射文件(EmpMapper.xml)下:
<!-- emp对象和表的映射-->
<!--查询员工信息以及其对应的部门信息-->
<resultMap id="empdept" type="Emp">
<!--id标签一般就用于主键,不用也行 property:类的属性名 column:表中的字段名-->
<id property="no" column="empno"/>
<result column="ename" property="ename"/>
<result column="mgr" property="mgr"/>
<!--emp 类中的dept属性 和表对象的映射-->
<association property="deptInfo" javaType="Dept">
<id property="dno" column="deptno"/>
<result property="dname" column="dname"/>
</association>
</resultMap>
<select id="selectEmpDeptInfo" resultMap="empdept">
select * from emp join dept on emp.deptno=dept.deptno
</select>
接口下:
List<Emp> selectEmpDeptInfo();
测试类下:
// 查询员工信息以及其对应的部门信息
List<Emp> emps = mapper.selectEmpDeptInfo();
emps.forEach(System.out::println);
2.resultMap 的关联方式实现多表查询(一对多)
collection:集合。
映射文件(DeptMapper.xml)下:
<!--查询所有部门信息以及其对应的员工信息-->
<resultMap id="deptemp" type="Dept">
<!--id标签一般就用于主键,不用也行 property:类的属性名 column:表中的字段名-->
<id property="dno" column="deptno"/>
<result property="dname" column="dname"/>
<result property="loc" column="loc"/>
<!-- collection:集合 property:哪个属性是集合 ofType:集合中放的是什么对象 javaType:集合类型-->
<collection property="emps" ofType="Emp" javaType="list">
<id property="no" column="empno"/>
<result property="ename" column="ename"/>
</collection>
</resultMap>
<select id="selectDeptEmp" resultMap="deptemp">
select * from dept join emp on dept.deptno=emp.deptno
</select>
接口下:
List<Dept> selectDeptEmp();
测试类下:
List<Dept> depts = mapper.selectDeptEmp();
depts.forEach(System.out::println);
五、注解开发
使用注解一般用于简化配置文件. 但是, 注解有时候也不是很友好(有时候反而更麻烦), 例如动态 SQL等,所以注解和配置文件可以配合使用。
一些简单的增删改查,使用注解更简单,此时不需要映射文件。
/*@Select -> 用来进行查询*/
@Select("select * from emp")
List<Emp> selectAll();
@Update("update emp set ename=#{ename} where empno=#{no}")
Integer updateEmp(Emp emp);
@Delete("delete from emp where empno=#{non}")
Integer deleteEmp(Integer no);
@Insert("insert into emp(empno, ename) values(#{param1}, #{param2})")
Integer insertEmp( Integer empno, String ename);
SqlSession session = MyBatisUtils.getSession(true);
EmpMapper mapper = session.getMapper(EmpMapper.class);
/*List<Emp> emps = mapper.selectAll();
emps.forEach(System.out::println);*/
/*Emp emp = new Emp();
emp.setNo(8899);
emp.setEname("ABCD");
System.out.println(mapper.updateEmp(emp));*/
System.out.println(mapper.insertEmp(8899,"LiLei"));