目录
Mybatis概述
Mybatis原是apache的一个开源项目,2010年转投谷歌,从ibatis3.0开始改名为Mybatis。
Mybatis是一款优秀的持久层框架,几乎避免了所有的JDBC代码手动设置参数以及手动获取结果集的操作,是对jdbc进行轻量级的封装,提供统一的数据库信息配置,统一放在一个xml文件中。
Mybatis将sql提取到一个xml文件中,提供动态sql以及数据缓存的功能,提供了结果自动映射封装,是一个ORM(Object Relational Mapping 对象映射关系)实现的数据持久层的框架。
ORM指的是将数据中的记录与Java的POJO(Plain Old Java Objects,普通的Java对象)进行关系映射,提供自己定义的类和接口来实现功能。
Mybatis 中文官网 https://mybatis.org/mybatis-3/zh/getting-started.html
Mybatis搭建
1.创建一个maven项目,添加mybatis,mysql依赖的jar
创建maven项目
添加mybatis,mysql依赖
2.创建一个数据库表,以及一个对应的Java模型(Model)类
3.在resources中创建mybatis全局配置文件,配置数据库连接信息,配置sql映射文件
4.在resources中新建一个mapper目录下创建sql映射文件,定义与接口方法名相同的查询语句
5.创建一个访问接口,定义一个方法
//在接口中定义方法
public interface AdminDao{
}
6.测试mybatis
读取配置文件
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
创建SqlSessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
创建SqlSession
SqlSession sqlSession = sessionFactory.openSession();
获得接口代理对象
sqlSession.getMapper(接口.class);
sqlSession .close();//关闭
API接口说明
SqlSessionFactory 接口:使用SqlSessionFactory 来创建SqlSession,一旦创建SqlSessionFactory 就会一直存在,由于创建开销较大,所以一般只创建一次。
Mybatis-Dao层Mapper接口化开发
Mapper接口开发方式只需要编写Mapper接口,由Mybatis框架创建接口的动态代理对象,使用sqlSession.getMapper(接口.class);获得代理对象
Mapper接口开发需要遵循以下规范:
- Mapper.xml文件中的namespace与mapper接口的类名相同
- Mapper接口方法名和Mapper.xml中定义的statement的id相同
- Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameType的类型相同
- Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
Mybatis日志
Mybatis内置的日志工厂提供日志功能,具体实现:
<setting>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<setting>
参数传递
单个参数直接传递
User selectUsers(int id);
多个参数使用@Param("id")绑定
User selectUsers(@Param("id")int id,@Param("name")String name);
<select id="selectUsers" resultType="User">
select id,username,password from users where id = #{id} and username = #{name}
</select>
如果传入一个复杂的对象,就需要使用parameterType参数进行类型定义,例如:
void insertUser(User user);
<insert id="insertUser" parameterType="User">
insert into users(id,username,password)values(#{id},#{username},#{password})
</insert>
增删改查
<!--
useGeneratedKeys="true" 取出数据生成的主键
keyColumn="id" 指定主键列
keyProperty="id" 指定主键对应的属性
-->
<insert id="savaAdmin1" parameterType="Admin" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
insert into admin(account,password) value (#{account},#{password})
</insert>
#{ }和${ }的区别
#{参数名}:采用预编译的方式传值,防止sql注入,更加安全,一般用于sql传值
selec * from admin where id = #{id}
${参数名}:如果用于sql传值,需要加单引号,即'${参数名}',使用字符串拼接的方式,先取值,然后去编译sql语句,$方式无法防止sql注入,不安全,一般用于动态向sql中传列名
-- 传列名
select * from goods order by ${排序的列的名字}
-- 传值
select * from admin where id = '${id}'
修改
<update id="唯一标识" parameterType="参数类型">
update admin set account = #{account},password = #{password} where id = #{id}
</update>
删除
<delete id="唯一标识" parameterType="参数类型">
delete from admin where id = #{id}
</delete>
查询
<select id="唯一标识" parameterType="参数类型" resultType="返回结果集类型">
select * from admin where id = #{id}
</select>
结果处理
简单类型输出映射
<select id="findAdminInfoCount" resultType="int">
select count(*) from admin
</select>
对象映射
如果数据库中的列名和类中的属性名相同时,mybatis会自动将查询结果封装到Java对象中。当Java中使用标准的驼峰命名,数据库使用下划线连接命名时,比如Java中命名为adminGender,数据库中命名为admin_gender,则可以开启全局自动转换
设置实现自动转换
<setting name="mapUnderscoreToCamelCase" value="true"/>
特殊处理定义resultMap
定义及使用resultMap
<!--
当数据库列名与类中属性名不同时或者特殊情况下,我们可以自己进行手动映射 resultMap Mapping(映射),当然如果只是想让类中属性名和数据库列名相同,我们可以采用起别名的方式。
id="唯一标识"
type="最终返回的类型"
-->
<resultMap id="adminMap" type="Admin">
<id column="id" property="id"></id>
<result column="gender" property="adminGender"></result>
</resultMap>
<select id="findAdmins1" resultMap="adminMap">
select id,account,password,gender from admin
</select>
resultMap的id属性是resultMap的唯一标识,id标签映射主键,result标签映射非主键,property设置类中的属性名称,column映射查询结果的列名称。
注意:本例中输出映射使用的是resultMap,不是resultType。select中的resultMap要与resultMap中id的名字相同。
多表关联处理结果集
resultMap元素中association,collection元素
association的使用
<!--使用学生表关联宿舍表和管理员表例子-->
<resultMap id="studentMap" type="Student">
<id column="id" property="id"></id>
<result column="num" property="num"></result>
<result column="name" property="name"></result>
<result column="gender" property="gender"></result>
<!--把学生关联的宿舍和操作人信息分别封装到宿舍对象和管理员对象中 属于嵌套映射-->
<association property="dorm" javaType="Dorm">
<result column="dnum" property="num"></result>
</association>
<association property="admin" javaType="Admin">
<result column="account" property="account"></result>
</association>
</resultMap>
<select id="findStudentById" parameterType="int" resultMap="studentMap">
SELECT
s.id,
s.num,
s.name,
s.gender,
d.num dnum,
a.account
FROM
student s
LEFT JOIN dorm d
ON s.dormid = d.id
LEFT JOIN admin a
ON s.adminid = a.id
WHERE s.id = #{id}
</select>
collection的使用(处理一对多关联)
<!--一个宿舍住了多个学生,需要将多个学生进行映射-->
<resultMap id="dormMap" type="Dorm">
<id column="id" property="id"></id>
<result column="num" property="num"></result>
<collection property="students" javaType="list" ofType="Student">
<result column="name" property="name"></result>
<result column="snum" property="num"></result>
</collection>
</resultMap>
<select id="findDorms" resultMap="dormMap">
SELECT
d.id,
d.num,
s.name,
s.num snum
FROM
dorm d
LEFT JOIN student s
ON d.id = s.dormid
</select>
嵌套查询
<!--一对多-->
<resultMap id="dormMap" type="Dorm">
<id column="id" property="id"></id>
<result column="num" property="num"></result>
<collection property="students" javaType="list" ofType="Student" select="findStudentsByDormId" column="id">
</collection>
</resultMap>
<select id="findDorms" resultMap="dormMap">
select id,num from dorm
</select>
<select id="findStudentsByDormId" parameterType="int" resultType="Student">
select id,num,name,gender from student where dormid = #{id}
</select>
select:指定关联查询对象的映射,collection中的select的id和第二个select的id要相同。
column="id":关联查询时,将第一个select语句查询到的id传入第二个select语句,并将查询结果映射到Dorm的students属性中。
注解方式
常用注解方式
@Insert:插入sql
@Select:查询sql
@Update:更新sql
@Delete:删除sql
以上同xml 的增删改查语句的语法完全一样
@Param:入参
@Results:设置结果集合
@Result:结果
以删除为例
@Delete("delete from admin where id = #{id}")
int deleteAdminById(int id);
Mybatis动态SQL
Mybatis的一个强大的特性之一是它的动态sql。
Mybatis中用于实现动态sql的主要元素有:if,where,trim,set,choose(when,otherwise),foreach。
if,where元素(标签)
<!--
if 条件判断
where 动态根据where标签内的if是否成立,动态添加where关键字,还可以去除条件前面的关键字(and/or)
-->
<select id="findStudents" parameterType="Student" resultType="Student">
select id,num,name,gender from student
<where>
<if test="num != 0">
num = #{num}
</if>
<if test="name != null">
and name = #{name}
</if>
<if test="gender != null">
and gender = #{gender}
</if>
</where>
</select>
trim元素(标签)
<!--
trim
prefix="where" 有条件成立时,添加指定的关键字
prefixOverrides="and" 覆盖指定的关键字 前缀
suffixOverrides 后缀
-->
<select id="findStudents" parameterType="Student" resultType="Student">
select id,num,name,gender from student
<trim prefix="where" prefixOverrides="and">
<if test="num != 0">
num = #{num}
</if>
<if test="name != null">
and name = #{name}
</if>
<if test="gender != null">
and gender = #{gender}
</if>
</trim>
</select>
choose,when,otherwise元素(标签)
<!--
choose
when
otherwise
多个条件选择一个
可以有多个when,也可以没有otherwise
-->
<select id="findStudents" parameterType="Student" resultType="Student">
select id,num,name,gender from student
<trim prefix="where" prefixOverrides="and">
<choose>
<when test="num != 0">
num = #{num}
</when>
<when test="name != null">
and name = #{name}
</when>
<otherwise>
and gender = '男'
</otherwise>
</choose>
</trim>
</select>
set元素(标签)
<!--
set标签使用场景
学生线上请假:相当与新增了请假的记录,请假人 请假时间 请假原因
老师批请假:相当于修改记录 如果是长假还需要更高的领导再次审批相当于二次修改
set 可以动态根据是否有条件成立添加set,可以去除最后的逗号
-->
<update id="updateStudent" parameterType="Student">
update student
<trim prefix="set" suffixOverrides=",">
<if test="num != 0">
num = #{num},
</if>
<if test="name != null">
name = #{name},
</if>
<if test="gender != null">
gender = #{gender},
</if>
</trim>
where id = #{id}
</update>
foreach元素(标签)
主要用于sql语句中的in条件中,可以在SQL语句中进行迭代一个集合/数组。foreach元素的属性主要有item,index,collection,open,separator,close。index表示每次迭代到的位置。
<!--
collection:数组或集合
item:循环遍历出数组的元素
open:表示该语句以什么开始
close:表示以什么符号作为分隔符
separator:表示以什么结束
批量删除 一次可以删除多个,将多个id存放在数组或者集合中
-->
<delete id="deleteStudent">
delete from student where id in
<foreach collection="array" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</delete>
特殊符号处理
在mybatis的xml文件中存在一些特殊的符号,比如:<、>、"、&、<>等,正常书写mybatis会报错,需要对这些字符进行转义。
特殊字符 | 转义字符 |
< | < |
> | > |
" | " |
' | ' |
& | & |
除了可以使用上边的转义字符外,还可以使用<![CDATA[]]>来包裹字符。如下所示:
<if test="id != null">
AND <![CDATA[id <> #{id}]]>
</if>
Mybatis一级缓存&二级缓存
缓存
缓存是指可以进行高速数据交换的存储器。
作用:为了减去数据库的压力,提高查询性能。
实现的原理:从数据库中查询出来的对象在使用完后不销毁,而是存储在内存(缓存)中,当再次需要获取该对象时,直接从内存(缓存)中获取,不再像数据库执行select语句,从而减少对数据库的查询次数,因此提高了数据库的性能。
Mybatis有一级缓存和二级缓存。一级缓存的作用域是同一个SqlSession,在同一个sqlSession中两次执行相同的sql语句,第一次执行完后会将数据库查询到的数据封装到对象中,也就是写道缓存(内存),第二次会从缓存中获取数据,不去数据库查询,从而提高查询效率。当一个sqlSession结束后它的一级缓存也就不存在了。Mybatis默认开启一级缓存。
二级缓存是多个sqlSession共享的,其作用域是同一个namespace,不同的sqlSession两次执行相同namespace下的sql语句且向sql中传递参数也相同,也就是最终执行的sql语句相同,第一次执行完后会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据不再从数据库查询,从而提高查询效率。Mybatis默认没有开启二级缓存,需要在setting全局参数中配置开启二级缓存。
一级缓存
Mybatis 对缓存提供支持,但是在没有配置的默认情况下,它只开启一级缓存,一级缓存只是相对于同一个 SqlSession 而言。所以在参数和 SQL 完全一样的情况下,我们使用同一个SqlSession 对象调用一个 Mapper 方法,往往只执行一次 SQL,因为使用 SelSession 第一次查询后,MyBatis 会将其放在缓存中,以后再查询的时候,如果没有声明需要刷新,并且缓存没有超时的情况下, SqlSession 都会取出当前缓存的数据,而不会再次发送 SQL 到数据库。
声明刷新就是以下这种,开启以后就会重新从数据库中查询。
<!--
delete,insert,update,select标签中都可以使用flushCache="true"
-->
<delete id="deleteStudent1" flushCache="true">
delete from student where id <![CDATA[ > ]]> 10
<if test="id > 10"></if>
</delete>
一级缓存的生命周期
- sqlSession调用close()方法,销毁SqlSession对象,会释放一级缓存 。
- sqlSession调用clearChche()方法,会清空一级缓存,但sqlSession对象仍可以使用。
- SqlSession中执行了任何一个增删改操作,都会清空缓存数据,但仍可以使用该对象。
二级缓存
二级缓存的作用域是namespace,所有可以理解为区域是根据mapper划分。
每次查询会先从缓存区域查找,如果找不到则从数据库中查询,并将查询的数据写入缓存。Mybatis内部存储缓存使用一个HashMap,key为hashCode+sqlId+Sql语句,value为从查询出来映射生成的Java对象。
sqlSession执行insert,update,delete等操作commit提交后清空缓存区域,防止脏读(读到已经不存在的数据)。
配置二级缓存
第一步:在Mybatis全局配置文件中启用二级缓存
<!--全局的开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
第二步:对象序列化
将所有的POJO类实现序列化接口java.io.Serializable
第三步:配置映射文件
在Mapper映射文件中添加<cache />,表示此mapper开启二级缓存。
当SqlSession关闭时,会将数据存入到二级缓存。