MyBatis
MyBatis简介
什么是MyBatis
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注SQL本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。
Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatemnt)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。
总之,Mybatis对JDBC访问数据库的过程进行了封装,简化了JDBC代码,解决JDBC将结果集封装为Java对象的麻烦。
下图是MBatis架构图
(1)mybatis-config.xml是Mybatis的核心配置文件,通过其中的配置可以生成SqlSessionFactory,也就是SqlSession工厂
(2)基于SqlSessionFactory可以生成SqlSession对象
(3)SqlSession是一个既可以发送SQL去执行,并返回结果,类似于JDBC中的Connection对象,也是Mybatis中至关重要的一个对象。
(4)Executor是SqlSession底层的对象,用于执行SQL语句
(5)MapperStatement对象也是SqlSession底层的对象,用于接收输入映射(SQL语句中的参数),以及做输出映射(即将SQL查询的结果映射成相应的结果)
为什么要使用MyBatis
思考:在开始之前,思考下如何通过JDBC查询Emp表中的所有记录,并封装到一个List集合中返回。(演示:准备数据、导包、导入JDBC程序)
1、使用传统方式JDBC访问数据库:`
(1)使用JDBC访问数据库有大量重复代码(比如注册驱动、获取连接、获取传输器、释放资源等,执行一次SQL语句就需要连接一次数据库);
(2)JDBC自身没有连接池,会频繁的创建连接和关闭连接,效率低;
(3)SQL是写死在程序中,一旦修改SQL,需要对类重新编译;
(4)对查询SQL执行后返回的ResultSet对象,需要手动处理,有时会特别麻烦(未尝试过,不过会有这样的现象);
…
2、使用mybatis框架访问数据库:
(1)Mybatis对JDBC对了封装,可以简化JDBC代码;
(2)Mybatis自身支持连接池(也可以配置其他的连接池),因此可以提高程序的效率;
(3)Mybatis是将SQL配置在mapper文件中,修改SQL只是修改配置文件,类不需要重新编译。
如果是简单语句可以通过注解加配置文件
(4)对查询SQL执行后返回的ResultSet对象,Mybatis会帮我们处理,转换成Java对象。
底层会根据相对应的POJO对象的getXxx()方法或者根据POJO对象的属性(数据库表的列名需与POJO对象的属性名一致)将返回的ResultSet对象封装到POJO对象中
…
总之,JDBC中所有的问题(代码繁琐、有太多重复代码、需要操作太多对象、释放资源、对结果的处理太麻烦等),在Mybatis框架中几乎都得到了解决!!
MyBatis快速入门
1.先准备一个数据库,数据库中的表中有数据
2.创建一个Maven工程
3.在pom.xml中导入相关依赖
<dependencies>
<!-- junit单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.8</version>
</dependency>
<!-- 整合log4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.4</version>
</dependency>
</dependencies>
4.添加mybatis-config.xml文件
1、在src/main/resources目录下,创建mybatis-config.xml文件
<?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值应该保证唯一
在程序中通过[ namespace + id ]定位到要执行哪一条SQL语句
-->
<mapper namespace="EmpMapper">
<!-- 通过select、insert、update、delete标签声明要执行的SQL -->
<!-- 练习1: 查询emp表中的所有员工信息
resultType指定查询的结果将会封装到什么类型中
即使最终返回的结果是集合(List<Emp>),resultType也只需要指定集合中的泛型即可!
-->
<select id="findAll" resultType="com.tedu.pojo.Emp">
select * from emp
</select>
</mapper>
这个是通过配置xml文件来执行SQL语句的方式
MyBatis中的占位符
在增删改查操作中,SQL语句中的值是写死在SQL语句中的,而在实际开发中,此处的值往往是用户提交过来的值,因此这里我们需要将SQL中写死的值替换为占位符。
在mybatis中占位符有两个,分别是 #{}
占位符 和 ${}
占位符:
#{}:
相当于JDBC中的问号(?)占位符,是为SQL语句中的参数值进行占位,大部分情况下都是使用#{}占位符; 并且当#{}占位符是为字符串或者日期类型的值进行占位时,在参数值传过来替换占位符的同时,会进行转义处理(在字符串或日期类型的值的两边加上单引号)
例如:查询指定name的员工信息,SQL语句为:
select * from emp where name=#{name}
其实就等价于JDBC中:
select * from emp where name=?,
如果传过来的参数值为:张三,那么最终执行的SQL语句为:
-- 在参数替换占位符的同时进行了转义处理(在值的两边加上了单引号)
select * from emp where name='张三'
${}
:是为SQL片段进行占位,将传过来的SQL片段直接拼接在${}占位符所在的位置,不会进行任何的转义处理。(由于是直接将参数拼接在SQL语句中,因此可能会引发SQL注入攻击问题)
那么如果我们在传递的时候不是一个参数值,而是一个SQL片段呢?
例如:在查询时,我们想动态的传递查询的列:
select #{columns} from emp
此时传递过来的应该是一个SQL片段,不同于上面的参数值,如果此时还用#{}
,也会像上面一样被转译处理:
select 'id,name,job' from emp
这不是我们希望看到的!
如果不想让传过来的SQL片段被转译处理,而是直接拼接在SQL语句中,那么这里可以使用${}
,例如:
select ${columns} from emp
拼接之后:
select id,name,job from emp
使用哪种方式要看你实际SQL语句
动态SQL标签
if、where标签
-
<if>
标签:是根据test属性
中的布尔表达式的值,从而决定是否执行包含在其中的SQL片段。如果判断结果为true,则执行其中的SQL片段;如果结果为false,则不执行其中的SQL片段 -
<where>
标签:用于对包含在其中的SQL片段进行检索,在需要时可以生成where关键字,并且在需要时会剔除多余的连接词(比如and或者or)
练习:根据薪资查询员工信息(if标签)
<if>
标签:是根据test属性
中的布尔表达式的值,从而决定是否执行包含在其中的SQL片段。如果判断结果为true,则执行其中的SQL片段;如果结果为false,则不执行其中的SQL片段
在mapper文件中编写SQL语句:
<!-- 练习: 根据薪资查询员工信息
* 如果没有参数, 则不执行where子句, 默认查询所有员工:
* select * from emp
* 如果参数中只有minSal(即minSal不为null), 则:
* ... where salary > minSal
* 如果参数中只有maxSal(即maxSal不为null), 则:
* ... where salary < maxSal
* 如果参数有 minSal、maxSal(即minSal、maxSal不为null), 则:
* ... where salary > minSal and salary < maxSal -->
<select id="findAllBySal" resultType="com.tedu.pojo.Emp">
select * from emp
where 1=1
<if test="minSal != null">
and salary>#{minSal}
</if>
<if test="maxSal != null">
and salary <![CDATA[ < ]]> #{maxSal}
</if>
</select>
练习:根据薪资查询员工信息(where标签)
<where>
标签:用于对包含在其中的SQL片段进行检索,在需要时可以生成where关键字,并且在需要时会剔除多余的连接词(比如and或者or)
<!-- 练习: 根据薪资查询员工信息
* 如果没有参数, 则不执行where子句, 默认查询所有员工:
* select * from emp
* 如果参数中只有minSal(即minSal不为null), 则:
* ... where salary > minSal
* 如果参数中只有maxSal(即maxSal不为null), 则:
* ... where salary < maxSal
* 如果参数有 minSal、maxSal(即minSal、maxSal不为null), 则:
* ... where salary > minSal and salary < maxSal -->
<select id="findAllBySal" resultType="com.tedu.pojo.Emp">
select * from emp
<where>
<if test="minSal != null">
and salary>#{minSal}
</if>
<if test="maxSal != null">
and salary <![CDATA[ < ]]> #{maxSal}
</if>
</where>
</select>
foreach元素
foreach标签:可以对传过来的参数数组或集合进行遍历,以下是foreach标签上的各个属性介绍:
属性 | 属性描述 |
---|---|
collection | 必需,值为遍历的集合类型,例如:如果参数只是一个数组或List集合,则collection的值为array或list;如果传的是多个参数,用map封装,collection则指定为map中的key。 |
item | 必需,若collection为数组或List集合时,item表示其中的元素,若collection为map中的key,item表示map中value(集合或数组)中的元素 |
open | 可选,表示遍历生成的SQL片段以什么开始,最常用的是左括号’(’ |
close | 可选,表示遍历生成的SQL片段以什么结束,最常用的是右括号’)’ |
separator | 可选,每次遍历后给生成的SQL片段后面指定间隔符 |
练习: 根据员工的id批量删除员工信息
在mapper文件中编写SQL语句:
<!-- 练习14: 根据员工的id批量删除员工信息
delete from emp where id in (1,3,5,7)
collection: 如果传的参数仅仅是一个数组或者List集合, collection可以指定为
array或list; 如果传的是多个参数,用map封装,collection则指定为map中的key
open: 指定生成的SQL片段以什么符号开始
close: 指定生成的SQL片段以什么符号结束
item: 指定变量接收数组或集合中的元素
separator: 指定一个间隔符, 在将数组或集合中的每个元素拼接到SQL片段之后,
在后面拼接一个间隔符
-->
<delete id="deleteByIds">
delete from emp where id in
<foreach collection="array" open="(" item="id" separator="," close=")">
#{id}
</foreach>
</delete>