1.1 框架
对于程序员来说,框架是一套资源,这套资源中包含jar包、文档。还有一些包含源码、代码示例等。这套资源从相关的官网上可以下载。一般是以压缩文件的形式出现。
1.1.1 Mybatis的下载
MyBatis可以在github官网下载
1.1.2 Mybatis的Jar包
MyBatis框架的解压目录中只有一个Jar包,它是Mybatis的核心Jar包。另外还有个lib目录,其中存放者MyBatis所依赖的Jar包。所以,使用MyBatis需要将其核心Jar包和lib下的所有Jar包导入。
1.2 MyBatis概述
1.2.1 MyBatis简介
MyBatis是一个优秀的基于Java的持久层框架,它内部封装了JDBC,使开发者只需要SQL语句本身,而不用再花费精力去处理注入注册驱动等。
MyBatis通过xml或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中SQL的动态参数进行映射生成最终执行的SQL语句,最后由MyBatis框架执行SQL并将结果映射成java对象并返回。
1.2.2 MyBatis与Hibernate
hibernate和mybatis是当前流行的ORM框架。hibernate对数据库结构提供了较为完整的封装。mybatis主要着力点在于java对象与SQL之间的映射关系。
Hibernate框架是提供了全面的数据库封装机制的”全自动”ORM,即实现了POLP和数据库表之间的映射,以及SQL的自动生成和执行。
相比于此,MyBaties只能算作是”半自动”ORM。
因为MyBaties需要程序员自己去编写SQL语句,程序员可以结合数据库自身的特点灵活控制SQL语句。因此能够实现比Hibernate等全自动ORM框架更高的查询效率,能够完成复杂查询。
1.2.3 MyBatis体系结构
1.3 MyBatis工作原理
连接数据库的四要素再mybatis.xml中,SQL语句在mapper.xml文件中。Mybatis.xml进行注册。上图就展示了ORM框架。
1.4 第一个Mybatis程序
需求:实现Student信息写入到DB中
1.4.1基本程序
项目:primary
(1)导入Jar包
除了需要导入MyBatis的核心Jar包及依赖Jar包外,还需要导入MySql的驱动Jar包,JUnit测试的Jar包。核心Jar包与依赖Jar包,均在MyBatis框架的解压目录下。
(2)定义实体类
(3) 配置mapper.xml
dtd文件的设置,使得提示信息从本地获取。上图中的http://mybatis.org/dtd/mybatis-3-mapper.dtd,该地址放到下图中的key栏中,本地的dtd文件地址添加到下图中的location栏中。
(4) 配置MyBatis.xml
environments中default属性的作用:指定 所用environment的id。
(5) 获得SqlSession对象,对数据操作
1.5 通过阅读源码解决一些问题
1.5.1 在上图中创建SqlSessionFactory时有一个参数时inputStream,为什么不需要关闭?
1.5.2 SqlSession的创建:给一群变量赋初始值
1.5.3
增删改的操作都是调用的update操作
1.5.4 SqlSession的提交
1.5.5 SqlSession的关闭:如果做了提交,就不会执行回滚;如果不提交就会执行回滚。
1.6 给类的全名起别名,添加到配置文件中
2.1 单表的CURD(增删改查)的基本操作
2.2 把没有id的对象插入到数据库后直接获得id赋值到该对象。通过在insert语句后加select @@identity。
2.3 删除、更改时的mapper配置文件
查询时,SqlSession所调用的方法以及返回类型(这里是列表)。
2.4 模糊查询 & #{}、${}
Statement和preparedstatement之间的关系和区别
关系:PreparedStatement继承自Statement,都是接口
区别:PreparedStatement可以使用占位符,是预编译的,批处理比Statement效率高
PreparedStatement能够防止SQL注入,PreparedStatement执行效率高。#{}就相当于PreparedStatement,使用的是动态参数。而${}就相当于Statement,使用的是字符串拼接
3.1 解决属性和数据库字段名不一致的情况
1、给数据库字段名起别名为持久化类的属性名
2、使用resultMap来映射数据库字段名称和持久化类属性名的不同
3.2 Mapper动态代理
使用Mapper的动态代理,可以直接省略Dao层的实现类,只需要借助Dao层的接口来实现。使用接口的全名作为mapper的命名空间namespace的名字,使用接口中方法名作为mapper中SQL语句的id。这样直到了namespce和Mapper中SQL语句的id就直接可以知道要执行那句SQL语句了。
3.3 多查询条件无法整体接收问题
1、使用Map封装,传参数为封装好的Map,在SQL语句中的#{}中放Map中d对应的key值,即#{key}
2、使用参数的索引号
4.1 动态SQL
动态SQL,主要用于解决查询条件不确定的情况:在程序运行期间,根据用户提交的查询条件进行查询。提交的查询条件不同,执行的SQL语句不同,若将每种情况都逐一的写出来将出现大量的情况,所以此时就用到了动态SQL。
4.2 <if/>标签 和 <where/>标签
注意事项:
对于该标签的执行,当test的值为true时,会将其包含的SQL片段拼接到其所在的SQL语句中。
示例:
在上图代码中如果使用 “where 1 = 1 “ 的话,在数据量特别大的情况下会大大降低执行效率。所以引出了<where />标签。
4.3 <choose />标签
该标签中只可以包含<when /> 和 <otherwise />,可以包含多个<when /> 与一个<otherwise />。它们联合使用,完成java中的开关语句switch...case功能。
示例:
4.4 <foreach /> 标签
传递数组、列表、自定义类的列表参数的用法:
<select id="selectStudentsByForEach" resultMap="studentMapper">
select tid,tname,tage,tscore
from student
<!-- 传递的是Integer数组,先判断数组是否为空 -->
<if test="array.length > 0">
where tid in
<foreach collection="array" item="myid" open="(" close=")" separator=",">
#{myid}
</foreach>
</if>
</select>
<select id="selectStudentsByForEach2" resultMap="studentMapper">
select tid,tname,tage,tscore
from student
<!-- 传递的是Integer列表,先判断列表是否为空 -->
<if test="list.size > 0">
where tid in
<foreach collection="list" item="myid" open="(" close=")" separator=",">
#{myid}
</foreach>
</if>
</select>
<select id="selectStudentsByForEach3" resultMap="studentMapper">
select tid,tname,tage,tscore
from student
<!-- 传递的是Student列表,先判断列表是否为空 -->
<if test="list.size > 0">
where tid in
<foreach collection="list" item="stu" open="(" close=")" separator=",">
#{stu.id}
</foreach>
</if>
</select>
4.5 SQL片段
5.1 关联关系
当查询内容涉及到具有关联关系的多个表时,就需要使用关联查询。根据表与表间的关联关系不同,关联查询分为四种:
1、一对一关联查询
2、一对多关联查询
3、多对一关联查询
4、多对多关联查询
外键一定是定义在多方表中。
5.2 一对多 & 多对一关联查询
1、通过多表连接查询和多次单表查询实现一对多查询,注意当使用多次单表查询的时候可以使用延迟加载,所以这种方法用的比较多。
|
|
2、通过多表连接查询和多次单表查询实现多对一查询,注意当使用多次单表查询的时候可以使用延迟加载,所以这种方法用的比较多。
|
|
5.3 自关联查询
所谓自关联是指,自己即充当一方,又充当多方。
递归调用实现自关联查询,查询某id的所有子孙类。
*beans实现类
public class NewsLabel {
private Integer id;
private String name;
private Set<NewsLabel> children;
}
*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">
<mapper namespace="edu.sdut.dao.INewsLabelDao">
<!-- 查询指定栏目的所有子孙栏目 -->
<resultMap type="NewsLabel" id="newsLabelMapper">
<id column="id" property="id"/>
<result column="name" property="name"/>
<!-- 自查询,使用递归调用查询,select属性再次执行下面的
select语句,递归调用 -->
<collection property="children" ofType="NewsLabel"
select="selectChildrenByParent"
column="id"/>
</resultMap>
<select id="selectChildrenByParent" resultMap="newsLabelMapper">
select id,name from newslabel
where pid=#{xxx}
</select>
</mapper>
附加一个SQL表
5.4 多对多关联查询
多对多是两个一对多构成的。建立一张中间表。其中中间表是多方。有外键的一定是多方。
6.1 延迟加载
MyBatis中的延迟加载,也称为懒加载,是指再关联查询的时候按照设置延迟规则推迟对关联对象的select查询。延迟加载可以有效的减少数据库压力。
需要注意的是,Mybatis的延迟加载只是对关联对象的查询有延迟设置,对于主加载对象都是直接执行查询语句的。
6.2 关联对象加载机制
MyBatis根据对关联对象查询的select语句的执行机制,分为三种:直接加载、侵入式延迟加载与深度延迟加载。
直接加载:执行完对主加载对象的select语句,马上执行对关联对象的select查询。
侵入式延迟:执行对主加载对象的查询时,不会执行对关联对象的查询。当当要访问主加载对象的详情的时候,就会马上执行关联对象的selec查询。即对关联对象的查询,侵入到了主加载对象的详情访问中。或者说:将关联对象的详情进入到了主加载对象的详情中,即将关联对象的详情作为主加载对象的一部分执行。
深度延迟:执行对主加载对象的查询时,不会执行关联对象的查询。访问主加载对象的详情也不会执行关联对象的select查询。只有当真正访问关联对象的详情的时候,才会执行对关联对象的select查询。
需要注意的是,延迟加载的应用要求。关联对象的查询与主加载对象的查询必须是分别进行的select语句。不能是使用多表连接所进行的select查询。因为多表连接查询,其实质实对一张表的查询,对由多个表连接后形成的一张表的查询。会一次性将多张表所有信息查询出来。
MyBatis中对于延迟加载设置,可以应用到一对一、一对多、多对一、多对多的所有关联关系查询中。
在主配置文件中配置:
7.1 查询缓存
查询缓存的使用,主要是为了提高查询访问速度,将用户对同一数据的重复查询过程简化,不再每次均从数据库查询获取结果数据,从而提高访问速度。
MyBatis的查询缓存机制,根据缓存区的作用域域声明周期,可划分为两种:一级缓存与二级缓存。
MyBatis查询缓存的作用域是根据映射文件mapper的namespace划分的,相同namespace的mapper查询数据存放在同一个缓存区域。不同namespace下的数据互不干扰。无论是一级缓存还是二级缓存,都是按照namespace进行分别存放的。
但是一、二缓存的不同之处在于,SqlSession一旦关闭,则SqlSession中的数据将不存在,即一级缓存就不复存在,而二级缓存的生命周期会与整个应用程序同步,与SqlSessioin是否关闭无关。换句话说,一级缓存是在同一线程(同一SqlSession)间共享的,而二级缓存是在不同线程(不同SqlSession)间共享数据。
7.2 一级缓存
MyBatis一级缓存是基于org.apache.ibatis.cache.impl.PerpetualCache类的HashMap本地缓存,其中Map的value是结果对象,Map的key是SQL的ID+SQL语句,其和hibernate的查询依据不同,Hibernate的查询依据是查询对象的ID。其作用域是SqlSession,在同一个SqlSession中两次执行相同的sql查询语句,第一次执行完毕后,会将查询结果写入到查询缓存中,第二次会从缓存中直接获取数据,而不再到数据库中进行查询,从而提高查询效率。缓存查询的依据是SQL的ID,而不是结果对象的ID。
当一个SqlSession结束后,该SqlSession中的一级缓存也就不存在了。MyBatis默认一级缓存是开启状态的,且不能关闭。
7.3 增删改对一级缓存的影响
增删改操作都会刷新(清空)一级缓存。(无论是否提交)
7.4 内置二级缓存
由于Mybatis从缓存中读取数据的依据与SQL的id有关,而非查询出的对象,所以,使用二级缓存的目的,不是在多个查询间共享数据结果(所有查询中只要查询结果中存在改对象的,就直接从缓存中读取,这是对查询结果的共享,Hibernate中的缓存就是为了在多个查询间共享数据结果,但MyBatis的不是),而是为了防止同一查询(相同的SqlID,相同的SQL语句)的反复执行。
MyBatis内置的二级缓存为org.apache.ibatis.cache.impl.PerpetualCache。
7.4.1 内置二级缓存的开启
1、在持久化类中实现序列化Serializable接口
2、在mappper中配置
7.4.2 增删改对二级缓存的影响
增删改同样也会清空二级缓存
对于二级缓存的清空,实质上是对key所查找key对应的value置为null,而并非将<key,value>对,即Entry对象删除
从DB中进行select查询的条件是:
1)缓存中根本就不存在key对应的Entry对象
2)缓存中存在该key所对应的Entry对象,但value为null
7.4.3 二级缓存的使用原则
(1)多个namespace不要操作同一张表
由于二级缓存中的数据是基于namespace的,即不同namespace中的数据互不干扰。若某个用户在某个namespace下表执行了增删改操作,该操作只会引发当前namespace下的二级缓存的刷新,而对其他的namespace下的二级缓存没有影响,这样的话,其他二级缓存中的数据依然是未更新的数据,也就出现了多个namespace中数据不一致的现象。
(2)不要再关联关系表上执行增删改操作
一个namespace一般是对同一个表进行操作的,若表间存在关联关系,也就意味着同一个表可能会出现多个namespace中,这样就存在一个风险,若某一个namespace中对一个表进行增删改操作时影响到了其关联表的数据,而着个关联表的数据的修改只会刷新当前namespace下的二级缓存,而对另一namespace下的二级缓存数据没有影响。这也是多个namespace下对同一张表的操作。
(3)查询多余修改时使用二级缓存
在查询操作远远多于增删改的情况下可以使用二级查询。因为任何增删改都将刷新二级缓存。
7.4.4 增删改时不刷新二级缓存
在<insert flushCache = “false”>修改 ,对一级缓存不起作用。
7.5 MyBatis注解式开发
mybatis的注解,主要是用于替换映射文件的,而映射文件中无非存放着增删改查的SQL映射标签。所以,mybatis注解,就是要替换映射文件中的SQL标签。
注解的基础语法
注解后没有分号的
注解首字母是大写的,因为注解与类、接口是同一级别的。一个注解,后台对应着一个@Interface
在同一语法单元上,同一注解只能使用一次
在注解与语法单元之间可以隔若干空行、注释等非代码内容