1、MyBatis引入
1. JDBC优缺点
-
优点:简单 直接
-
缺点:
-
冗余代码多
-
面向对象开发和 面向关系的存储之间使用JDBC技术需要手动处理
-
SQL语句写死在Java程序中,不灵活。改SQL的话就要改Java代码。违背开闭原则OCP
-
频繁的手动给?占位符赋值
-
频繁的手动处理结果集,并手动把结果集封装为对象
-
2. ORM思想
-
ORM: Object Relational Mapping 对象关系映射 主要目标是把面向对象开发中的对象 映射到基于SQL的关系型数据库中。
-
O(Object):Java虚拟机中的Java对象
-
R(Relational):关系型数据库
-
M(Mapping):将Java虚拟机中的Java对象映射到数据库表中⼀⾏记录,或是将数据库表中 ⼀⾏记录映射成Java虚拟机中的⼀个Java对象
-
3. Hibernate缺点
-
从方法=》SQL语句翻译过程,执行效率降低
-
SQL语句不灵活
-
涉及到很多关联 一对一、一对多、多对多等,学习成本高
2、MyBatis简介
-
MyBatis本质上就是对JDBC的封装,通过MyBatis完成CRUD。
-
MyBatis在三层架构中负责持久层的,属于持久层框架
-
Mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,开发时只需要关注SQL 语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。程序员直接编写原生态sql,可以严格控制sql执行性能,灵活度高
-
MyBatis 可以使用 XML 或注解来配置和映射原生信息,将 POJO(实体类)映射成数据库中的记录,避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集
-
通过xml 文件或注解的方式将要执行的各种 statement 配置起来,并通过java对象和 statement中sql的动态参数进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果 映射为java对象并返回。(从执行sql到返回result的过程)
-
MyBatis缺点:
-
SQL语句必须要由开发人员自己编写,对开发人员的要求较高
-
SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库
-
-
MyBatis特点:
-
⽀持定制化 SQL、存储过程、基本映射以及⾼级映射
-
避免了⼏乎所有的 JDBC 代码中⼿动设置参数以及获取结果集
-
⽀持XML开发,也⽀持注解式开发。【为了保证sql语句的灵活,所以mybatis⼤部分是采⽤ XML⽅式开发。】
-
将接⼝和 Java 的 POJOs(Plain Ordinary Java Object,简单普通的Java对象)映射成数据库中的 记录
-
体积⼩好学:两个jar包,两个XML配置⽂件
-
完全与sql解耦合
-
提供了基本映射标签和高级映射标签
-
提供了XML标签,⽀持动态SQL的编写
-
......
-
-
MyBatis中的配置文件:
-
属性文件:mybatis-config.xml 和数据库连接的信息 可选
-
映射文件:SQL语句 。| 注解
-
3、MyBatis的分页机制
-
MyBatis自带的分页采用RowBounds 即行范围 ,底层 采用的是可滚动的结果集实现的,查询所有数据,然后部分显示
2. 我们也可以自己 写分页语句
3. 开发 中也可以 使用第三方组件pageHelper等
4、SQL映射文件
-
SQL映射文件中的namespace必须和Mapper层的
包名+接口
名字一致
在SQL映射⽂件中标签的namespace属性可以翻译为命名空间,这个命名空间主要是 为了防⽌sqlId冲突的
2. SQL语句的id必须和方法名字保持一致
5、类和表不一致的处理
在某些情况下我们的数据库中的表名和字段名不一定和我们的类名和属性名一致,在这种情况下对于添加操作和查询操作有以下处理方法
1. 添加
-
当传递对象类型参数时,#{填写的是类中属性,而不是表中字段}
2. 查询
-
起别名
-
使用<resultMap>提供对应关系
6、Mapper方法中参数处理
-
对象
这⾥需要注意的是:#{} ⾥⾯写的是属性名字。这个属性名其本质上是:set/get⽅法名去掉set/get之后 的名字
2.Map
这种⽅式是⼿动封装Map集合,将每个条件以key和value的形式存放到集合中。然后在使⽤的时候通过# {map集合的key}来取值
3.@Param
<!--
当传递多个参数:MyBatis默认给参数指定名字 :
arg0===>参数1 param1===>参数1
arg1===>参数2 param2===>参数2
arg2===>参数3 param====>参数3
...
如果我们不想使用默认的参数的命名: 可以使用 @Param注解指定参数
@Target(该注解适用的程序元素的种类(类、属性Field、方法、构造方法、PARAMETER。))
@Rentention(保留 RUNTIME)
public @interface Param{
}
底层: org.apache.ibatis.reflection.ParamNameResovler负责MyBatis参数处理
-->
<select id="querySome" resultMap="rMap_emp">
<!--select * from tb_emp limit #{arg0},#{arg1}-->
<!-- select * from tb_emp limit #{param1},#{param2}-->
select * from tb_emp limit #{start},#{pageSize}
</select>
7、#{}和${}
-
#{}是预编译处理,${}是字符串替换。
-
Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
-
Mybatis在处理${}时,就是把${}替换成变量的值。
-
使用#{}可以有效的防止SQL注入,提高系统安全性。
-
#底层相当于使用的PreparedStatement,支持?占位符,效率和安全性比较高。 推荐使用。
-
$底层相当于使用普通的Statement进行字符串拼接,不推荐使用。
8、获取主键
1. 使用JDBC自带开关
2. 同一个连接下进行:select last_insert_id()
3. 获取String类型的主键
执行结果:
9、动态SQL
1. if和where
<select id="queryByCondition1" resultMap="resultMap">
select * from tb_emp
<!-- where标签是专门负责where子句动态生成的
所有条件都为空时,where标签保证不会⽣成where⼦句。
⾃动去除某些条件前⾯多余的and或or-->
<where>
<!--
1. if标签中test属性是必须的。s
2. if标签中test属性的值是false或者true。
3. 如果test是true,则if标签中的sql语句就会拼接。反之,则不会拼接。
4. test属性中可以使用的是:
当使用了@Param注解,那么test中要出现的是@Param注解指定的参数名。@Param("brand"),那么这里只能使用brand
当没有使用@Param注解,那么test中要出现的是:param1 param2 param3 arg0 arg1 arg2....
当使用了POJO,那么test中出现的是POJO类的属性名。
5. 在mybatis的动态SQL当中,不能使用&&,只能使用and。
-->
<if test="ename != null and ename != ''">
and e_ename like concat('%',#{ename},'%')
</if>
<if test="job != null and job != ''">
and e_job = #{job}
</if>
</where>
</select>
2. choose、when、otherwise
<select id="queryByCondition2" resultMap="resultMap">
select * from tb_emp
<where>
<!--
只有⼀个分⽀会被选择!!!相当于if(){}else if(){}else{}
-->
<choose>
<when test="ename != null and ename != ''">
and e_ename like concat('%',#{ename},'%')
</when>
<otherwise>
<if test="job != null and job != ''">
and e_job = #{job}
</if>
</otherwise>
</choose>
</where>
</select>
3. trim
<select id="queryByCondition" resultMap="resultMap">
select * from tb_emp
<!--
prefix:在trim标签中的语句前添加内容
suffix:在trim标签中的语句后添加内容
prefixOverrides:前缀覆盖掉(去掉)
suffixOverrides:后缀覆盖掉(去掉)
-->
<!-- prefix="where" 是在trim标签所有内容的前面添加 where -->
<!-- suffixOverrides="and|or" 把trim标签中内容的后缀and或or去掉 -->
<trim prefix="where" prefixOverrides="and |or">
or 1=1
<if test="ename != null and ename != ''">
and e_ename like concat('%',#{ename},'%')
</if>
<if test="job != null and job != ''">
and e_job = #{job}
</if>
</trim>
</select>
4. set
<update id="updateEmp" parameterType="emp">
update tb_emp
<!--
主要使⽤在update语句当中,⽤来⽣成set关键字,同时去掉最后多余的“,”
⽐如我们只更新提交的不为空的字段,如果提交的数据是空或者"",那么这个字段我们将不更新
会删掉额外的逗号
-->
<set>
<if test="ename != null and ename != ''">e_ename = #{ename},</if>
<if test="job != null and job != ''">e_job = #{job},</if>
<if test="sal != 0">e_sal = #{sal},</if>
<if test="comm != 0">e_comm = #{comm},</if>
<if test="hiredate != null">e_hiredate = #{hiredate},</if>
<if test="deptno != null and deptno != ''">e_deptno = #{deptno},</if>
<if test="mgr != null and mgr != ''">e_mgr = #{mgr},</if>
</set>
where e_empno = #{empno}
</update>
5. foreach
-
批量添加
insert into xx(f1,f2..)values(),(),()
<insert id="addEmps1">
<!--
foreach标签的属性:
collection:指定数组或者集合
item:代表数组或集合中的元素
separator:循环之间的分隔符
open: foreach循环拼接的所有sql语句的最前面以什么开始。
close: foreach循环拼接的所有sql语句的最后面以什么结束。
-->
insert into tb_emp(e_ename, e_job, e_sal, e_comm, e_hiredate, e_deptno, e_mgr) values
<foreach collection="emps" item="emp" separator=",">
(#{emp.ename},#{emp.job},#{emp.sal},#{emp.comm},#{emp.hiredate},#{emp.deptno},#{emp.mgr})
</foreach>
</insert>
insert into xx(f1,f2...) (select...)union all (select....)
<select id="queryByEmpnos" resultMap="resultMap">
select * from tb_emp
<foreach collection="empnos" item="empno" separator="," open="where e_empno in (" close=")">
#{empno}
</foreach>
</select>
-
批量查询
<select id="queryByEmpnos" resultMap="resultMap">
select * from tb_emp
<foreach collection="empnos" item="empno" separator="," open="where e_empno in (" close=")">
#{empno}
</foreach>
</select>
10、映射
1. 一对一(association)
<resultMap id="rMap_emp1" type="emp">
<id property="empno" column="e_empno"></id>
<result property="ename" column="e_ename"></result>
<result property="job" column="e_job"></result>
<result property="sal" column="e_sal"></result>
<result property="hiredate" column="e_hiredate"></result>
<result property="mgr" column="e_mgr"></result>
<result property="comm" column="e_comm"></result>
<result property="deptno" column="e_deptno"></result>
<!--一对一关联采用:assocication property:属性 javaType:属性的类型-->
<association property="dept" javaType="com.etoak.emp.pojo.Dept">
<id property="deptno" column="deptno"></id>
<result property="dname" column="dname"></result>
<result property="loc" column="loc"></result>
</association>
</resultMap>
<select id="queryEmpByNoWithDept" resultMap="rMap_emp1">
select
e.e_empno, e_ename, e_job, e_sal, e_comm, e_hiredate, e_deptno, e_mgr,
d.deptno, dname, loc
from tb_emp e left join dept d on e.e_deptno = d.deptno
where e.e_empno=#{empono}
</select>
2. 一对多(collection)
<resultMap id="rMap_dept" type="dept">
<id property="deptno" column="deptno"></id>
<result property="dname" column="dname"></result>
<result property="loc" column="loc"></result>
<!--一对一关联采用:assocication property:属性
javaType:属性的类型
ofType:集合中元素的类型 collection元素必选 属性 -->
<collection property="emps" javaType="java.util.Set" ofType="emp">
<id property="empno" column="e_empno"></id>
<result property="ename" column="e_ename"></result>
<result property="job" column="e_job"></result>
<result property="sal" column="e_sal"></result>
<result property="hiredate" column="e_hiredate"></result>
<result property="mgr" column="e_mgr"></result>
<result property="comm" column="e_comm"></result>
<result property="deptno" column="e_deptno"></result>
</collection>
</resultMap>
<select id="queryDeptByNoWithEmps" resultMap="rMap_dept">
select
d.deptno, dname, loc,
e.e_empno, e_ename, e_job, e_sal, e_comm, e_hiredate, e_deptno, e_mgr
from dept d left join tb_emp e on e.e_deptno = d.deptno
where d.deptno=#{no}
</select>
3. 问题场景
一对多的场景下分页查询一方,级联获取多方
<resultMap id="resultMap5" type="dept">
<id column="deptno" property="deptno"></id>
<result column="dname" property="dname"></result>
<result column="loc" property="loc"></result>
<!-- javaType:属性的类型 ofType:属性中元素的类型 -->
<collection property="emps" javaType="list" ofType="emp" >
<id column="e_empno" property="empno"></id>
<result column="e_ename" property="ename"></result>
<result column="e_job" property="job"></result>
<result column="e_sal" property="sal"></result>
<result column="e_comm" property="comm"></result>
<result column="e_hiredate" property="hiredate"></result>
<result column="e_deptno" property="deptno"></result>
<result column="e_mgr" property="mgr"></result>
</collection>
</resultMap>
<select id="querySomeDeptWithEmps" resultMap="resultMap5">
select e_empno, e_ename, e_job,
e_sal, e_comm, e_hiredate, e_deptno, e_mgr, deptno,dname, loc
from dept d
left join tb_emp e on e.e_deptno = d.deptno
limit #{start},#{pageSize}
</select>
执行结果:
<resultMap id="rMap_dept1" type="dept">
<id property="deptno" column="deptno"></id>
<result property="dname" column="dname"></result>
<result property="loc" column="loc"></result>
<!--一对一关联采用:assocication property:属性
javaType:属性的类型
ofType:集合中元素的类型 collection元素必选 属性
select:通过发送子查询的方式查询数据
column:列 传递到子查询中的参数 主键【子查询根据外键查询】
fetchType:加载一方时是否立刻获取多方数据
eager:立刻获取
lazy:延迟加载,只有用到一方中的多方数据了,才去获取。-->
<collection property="emps" javaType="java.util.Set"
ofType="emp" select="queryEmpByDeptno" column="deptno"
fetchType="lazy">
</collection>
</resultMap>
<select id="querySomeDeptWithEmps" resultMap="rMap_dept1">
select
d.deptno, dname, loc
from dept d limit #{start},#{pageSize}
</select>
<select id="queryEmpByDeptno" resultMap="rMap_emp">
select * from tb_emp where e_deptno=#{nosdfsdfs}
</select>
执行结果:
11、分页插件
第⼀步:引⼊依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.3.2</version>
</dependency>
第二步:在mybatis-config.xml⽂件中配置插件
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="helperDialect" value="mysql"/>
</plugin>
</plugins>
第三步:启用分页
执行结果: