MyBatis概述
MyBatis是一款优秀的持久层框架;MyBatis中文官网
持久层:可以立即保存在磁盘上,在这里可以理解成与数据库进行相关的操作(dao层就是数据持久层)。
MyBatis对JDBC功能进行轻量级的封装,避免了几乎所有的JDBC代码手动设置参数以及手动获取结果集的操作。
①提供了统一的数据库信息配置,统一放在一个xml文件中,读取就行;
②将sql语句提取到一个xml文件中,并提供了动态sql功能以及数据缓存;
③提供了结果自动映射封装,实现了ORM;
ORM: Object Relational Mapping 对象关系映射,将数据库中的记录与Java中对象进行关系映射。
④对JDBC原生接口进行封装,提供了一些mybatis自己的接口和类来实现。
MyBatis环境搭建
1.创建一个Maven项目;
可参考Maven搭建来完成。
2.导入MyBatis相关的依赖;
在pom.xml文件中导入mybatis,mysql依赖:
<!--mysql-connector-java-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.2</version>
</dependency>
3.创建数据库表,创建对应的model类;
4.创建一个mybatis核心全局配置文件(可以配置数据库连接信息,配置sql映射文件,或者各种常用的设置);
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config
3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--mybatis核心配置文件-->
<configuration>
<!--导入配置属性文件 该文件中存储连接数据库的信息 键值对-->
<properties resource="文件名"></properties>
<!--常用设置-->
<settings>
<!--启用日志功能,在运行时,可以将实际执行的sql细节打印到控制台-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!--开启驼峰命名映射-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- <setting name="autoMappingBehavior" value="FULL"/>-->
<!--全局开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
<!--配置类型简称-->
<typeAliases>
<package name="model类地址"/>
</typeAliases>
<!--配置连接数据库的信息-->
<environments default="development">
<environment id="development">
<!--数据库事物管理配置-->
<transactionManager type="JDBC"></transactionManager>
<!--数据源配置 type="POOLED" 使用数据库连接池-->
<dataSource type="POOLED">
<property name="driver" value=" " />
<property name="url" value=" "/>
<property name="username" value=" " />
<property name="password" value=" "/>
</dataSource>
</environment>
</environments>
<!--配置sql映射文件-->
<mappers>
<mapper resource="sql映射文件地址"></mapper>
</mappers>
</configuration>
5.创建接口(用来定义进行一系列增删改查操作的方法)
6.创建sql映射文件;
<?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="接口地址">
定义sql语句
</mapper>
7.测试
为方便测试,可以在pom.xml文件中配置单元测试插件。
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>provided</scope>
</dependency>
配置完成后,创建一个测试方法,在方法最上面标志@Test,即可直接进行测试。
@Test
public void test() {
//读取配置文件 mybatis-config.xml 全局核心配置文件名
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
//创建 SqlSessionFactory
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
//创建 SqlSession
SqlSession sqlSession = sessionFactory.openSession();
//获得接口代理对象
sqlSession.getMapper(接口.class);
//关闭sqlSession
sqlSession.close();
}
MyBatis使用接口介绍
1.SqlSessionFactory接口
使用SqlSessionFactory来创建SqlSession,一旦创建SqlSessionFactory就会在整个应用过程中始终存在。由于创建开销较大,所以没有理由去销毁在创建它,一个应用运行中也不建议多次创建。
2.SqlSession接口
SqlSession意味着创建与数据库连接会话,该接口中封装了对数据库操作的方法,与数据库完成会话后关闭,每与数据库交互一次,就需要创建一个SqlSession对象。
3.Mybatis-Dao层 Mapper 接口化开发
Mapper 接口开发方式只需要编写 Mapper接口,由 Mybatis框架创建接口的动态代理对象,使用sqlsession.getMapper(接口.class)获得代理对象。
Mapper接口开发需要遵循以下规范:
①Mapper.xml文件中的namespace与 mapper 接口的类路径相同;
<mapper namespace="mapper接口的类路径">
②Mapper接口方法名和 Mapper.xml 中定义的每个 statement 的 id 相同;
<select id="Mapper接口方法名" parameterType=" " resultMap=" ">
③Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同;
④Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同。
List<Student> findStudents(Student student);
<select id="findStudents" parameterType="Student" resultType="Student">
参数传递
1.当只有一个参数,且该参数类型为基本数据类型时,不需要做任何处理,mapper.xml中需要定义parameterType;
void method(int id); //parameterType="int"
2.当只有一个参数,且该参数类型为引用类型时,需要使用@Param()进行绑定处理;
void method(@Param("column") String column);
3.当有两个或少量参数时,需要使用@Param()进行绑定处理;
void method(@Param("acc") String account, @Param("pwd") String password);
4.当参数数量较多时,可以将参数封装在对象中。
Admin method(Admin admin);
5.在传递参数时, #{}和${}两种方式对参数进行操作有何区别?
#{ }:占位符,是经过预编译的,编译好sql语句再取值,能够防止sql注入,更安全。
<select id="login" resultType="Admin">
select * from admin where account=#{acc} and password=#{pwd}
</select>
${ }:拼接符,会传入参数字符串,将值拼接在sql中,取值以后再去编译sql语句,无法防止sql注入。
${ }主要用来动态的向sql中传入列名,比如排序 order by ${column}
<select id="find" resultType="Admin">
select * from admin order by ${column}
</select>
增删改查操作
1.新增操作
//接口中定义的方法,以新增管理员为例
void saveAdmin(Admin admin);
//Mapper.xml中定义的statement
//开启将生成的主键列,自动封装到对象中
//useGeneratedKeys="true" keyProperty="id"(对象中的id属性) keyColumn="id"(数据库中的id列)
<insert id="saveAdmin" parameterType="Admin" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
insert into admin(account,password)value (#{account},#{password})
</insert>
2.删除操作
//接口中定义的方法,以删除管理员为例
void deleteAdminById(int id);
//Mapper.xml中定义的statement
<delete id="deleteAdminById" parameterType="int">
delete from admin where id=#{id}
</delete>
3.修改操作
//接口中定义的方法,以修改管理员为例
void updateAdmin(Admin admin);
//Mapper.xml中定义的statement
<update id="updateAdmin" parameterType="Admin">
update admin set account=#{account},password=#{password} where id=#{id}
</update>
4.查询操作
(1).单表查询
//根据管理员id查询管理员信息
Admin findAdminById(int id);
//Mapper.xml中定义的statement
<select id="findAdminById" parameterType="int" resultType="Admin">
select account,password from admin where id=#{id}
</select>
(2).多表查询
以学生为例,根据学生id查询该学生的所有信息,包括宿舍信息,管理员信息;
Student findStudentById(int id);
方式一:关联查询
<!--resultMap标签可以自己自定义结果映射-->
<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>
<result column="birthday" property="birthday"></result>
<result column="oper_time" property="operTime"></result>
<!--用来封装关联的对象信息 property="dorm" 就会创建一个关联的对象,将查询到的结果保存到对象中,再将该对象set到返回的结果Student中 -->
<association property="dorm" javaType="Dorm">
<result column="dormNum" 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,
s.birthday,
s.oper_time,
d.num dormNum,
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>
方式二:嵌套查询
若使用关联查询,sql语句过于繁琐,容易出错时,可以使用嵌套查询;嵌套查询会把sql分成多次查询,一步一步进行查询。
<!--可以先查询学生信息,再根据学生信息查询宿舍信息以及管理员信息-->
<resultMap id="studentMap1" 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>
<result column="birthday" property="birthday"></result>
<result column="oper_time" property="operTime"></result>
<association property="dorm" javaType="Dorm" column="dormid" select="findDormById"></association>
<association property="admin" javaType="Admin" column="adminid" select="findAdminById"></association>
</resultMap>
<select id="findStudentById1" parameterType="int" resultMap="studentMap1">
select id,num,name,gender,birthday,dormid,adminid,oper_time from student where id=#{id}
</select>
<select id="findDormById" resultType="Dorm">
select num from dorm where id=#{dormid}
</select>
<select id="findAdminById" resultType="Admin">
select account from admin where id=#{adminid}
</select>
操作结果处理
1.简单类型输出映射
直接使用resultType:
<!--mybatis对java中常用的数据类型的全类名进行了简化-->
<select id="adminCount" resultType="int">
select count(*) from admin
</select>
2.对象映射
若返回的数据为一个对象时,mybatis会自己创建给定类的对象,并将查询到的结果自动封装到该对象中,进行返回。但自动封装是有条件的:
①开启了全局的自动结果映射 默认情况是单张表开启的;
<setting name="autoMappingBehavior" value="FULL"/>
②数据库列名与属性名字相同 不相同无法映射 可以定义别名使其相同;
③开启驼峰命名自动映射,由于数据库命名规则与Java不同,java中使用标准驼峰命名,数据库中使用下划线连接命名(admin_gender与adminGender可自动映射);
<setting name="mapUnderscoreToCamelCase" value="true"/>
3.特殊结果处理定义resultMap
//根据id查询宿舍信息(该宿舍中的入住学生信息)
Dorm findDormById(int id);
关联查询结果映射:
<!--结果映射-->
<resultMap id="dormMap" type="Dorm">
<!--手动映射 column为数据库中的列名,property为Java类中的属性名
id 标签映射主键,result标签映射非主键-->
<id column="id" property="id"></id>
<result column="num" property="num"></result>
<!--映射的属性为一个对象-->
<!--用来封装关联的对象信息 property="dorm" 就会创建一个关联的对象,将查询到的结果保存到对象中,再将该对象set到返回的结果Student中 -->
<association property="admin" javaType="Admin">
<result column="account" property="account"></result>
</association>
<!--映射的属性为一个集合-->
<collection property="students" javaType="list" ofType="Student">
<result column="snum" property="num"></result>
<result column="name" property="name"></result>
</collection>
</resultMap>
<!--resultMap 引用了 dormMap-->
<select id="findDormById" parameterType="int" resultMap="dormMap">
SELECT
d.id,
d.num,
s.num snum,
s.name,
a.account
FROM
dorm d
LEFT JOIN admin a
ON d.adminid = a.id
LEFT JOIN student s
ON d.id = s.dormid
WHERE d.id = #{id}
</select>
嵌套查询结果映射:
<resultMap id="dormMap1" type="Dorm">
<id column="id" property="id"></id>
<result column="num" property="num"></result>
<!--select属性指定关联查询对象的Statement ID-->
<!--column属性将查询到的值传入select指定的Statement中-->
<association property="admin" javaType="Admin" column="adminid" select="findAdmin"></association>
<collection property="students" javaType="list" ofType="Student" column="id" select="findStudents"></collection>
</resultMap>
<select id="findDormById1" parameterType="int" resultMap="dormMap1">
select id,num,adminid from dorm where id=#{id}
</select>
<select id="findAdmin" resultType="Admin">
select account from admin where id=#{adminid}
</select>
<select id="findStudents" resultType="Student">
select num,name from student where dormid=#{id}
</select>
注解方式
常用注解标签:(简单操作可使用注解标签)
@Insert : 插入 sql , 和 xml insert sql 语法完全一样
@Select :查询 sql, 和 xml select sql 语法完全一样
@Update :更新 sql, 和 xml update sql 语法完全一样
@Delete : 删除 sql, 和 xml delete sql 语法完全一样
@Param : 入参
@Results : 设置结果集合
@Result : 结果
使用时,将sql语句写到注解标签中即可:
@Select("select account,password from admin where id=#{id}")
Admin findAdminById(int id);
动态SQL
在JDBC或者其他类似的框架中,需要根据不同的条件对sql语句进行拼接,根据sql语句的格式在拼接时需要注意是否添加了必要的空格,是否删掉了最后一个列名的逗号等等,这很令人痛苦,而动态sql解决了该问题。
动态SQL:一般是根据用户输入或者外部条件动态组合sql语句块,MyBatis可以在xml映射文件中,以标签的方式,完成逻辑判断和动态拼接sql的功能。
①where + if 标签
<!--
where标签,当where标签内的if有成立的,就会动态的添加where关键字,如果where后面有and,or这种关键字会被动态删除
if 标签,test="条件"-->
<select id="findStudents1" parameterType="Student" resultType="Student">
select * from student
<where>
<if test="num!=null">
and 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|or" 覆盖掉指定的开头的关键字-->
<select id="findStudents1" parameterType="Student" resultType="Student">
select * from student
<trim prefix="where" prefixOverrides="and|or">
<if test="num!=null">
and num=#{num}
</if>
<if test="name!=null">
and name=#{name}
</if>
<if test="gender!=null">
and gender=#{gender}
</if>
</trim>
</select>
③choose,when,otherwise标签
<!--只选择一个 如同Java中的if else if else -->
<select id="findStudents1" parameterType="Student" resultType="Student">
select * from student
<choose>
<when test="num!=null">
num=#{num}
</when>
<when test="name!=null">
and name=#{name}
</when>
<otherwise>
gender=#{gender}
</otherwise>
</choose>
</select>
④set标签
<!-- set关键字 可以动态的添加set关键字,可以去除最后的逗号也可以使用trim prefix="set" suffixOverrides=","-->
<update id="updateStudent" parameterType="Student">
update student
<set>
<if test="num!=null">
num=#{num},
</if>
<if test="name!=null">
name=#{name},
</if>
<if test="gender!=null">
gender=#{gender}
</if>
</set>
where id=#{id}
</update>
⑤foreach标签
<!--foreach 标签 可以遍历集合数组 collection="list" collection="array"-->
<delete id="deleteStudents" parameterType="list">
delete from student where id in
<foreach collection="list" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
特殊符号处理
在 mybatis中的 xml 文件中,存在一些特殊的符号,比如:<、>、"、&、<>等,正常书写mybatis会报错,需要对这些符号进行转义。具体转义如下所示:
除了可以使用上述转义字符外,还可以使用<![CDATA[ ]]>来包裹特殊字符。
缓存机制
缓存:将一些数据临时存储,为了更快的获取到数据。
mybatis为减轻数据库压力,提高数据库性能,提供了两种缓存机制:
1.一级缓存
SqlSession级别的缓存,缓存的数据只在SqlSession内有效。
mybatis默认开启了一级缓存,是在同一个SqlSession中如果有两次相同的查询,只发送一次sql。mybatis将查询到的数据默认会缓存到SqlSession中。
由于以及缓存是默认开启的,无法关闭,但可以手动清空缓存:
方式一:sqlSession.clearCache(); 清除一级缓存中的数据
方式二:sqlSession.close();
方式三:中间执行了新增,修改,删除等操作会清空缓存(防止脏读);
一级缓存声明周期:
生:创建sqlSession ,可以通过以上三种方式手动销毁。
2.二级缓存
SqlSessionFactory级别的缓存,只有一个,sqlSession1查询到的数据保存在sqlSessionFactory中,即使sqlSession1销毁,sqlSession2也可以拿到该数据。
开启二级缓存:
①全局核心配置文件设置开启
<!--全局开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
②model类实现序列化接口
public class 类名 implements Serializable {
......
}
③配置映射文件(更加细致的配置二级缓存)
<!--在Mapper.xml中更加细致的配置二级缓存
eviction="FIFO" 先进先出:按对象进入缓存的顺序来移除它们
size="512" 最多可以存储结果对象或列表的 512 个引用
readOnly="true" 返回的对象被认为是只读的
flushInterval="60000" 每隔 60 秒刷新
-->
<cache eviction="FIFO" size="512" readOnly="true" flushInterval="60000"/>