Mybatis是一款优秀的持久层(dao) 框架,对JDBC程序进行封装,用于简化JDBC的开发
第一部分 快速入门
之前用图形化界面操作数据库,是在图形化界面中编写SQL语句 并发送到数据库服务器来执行,数据库再将结果返回给图形化界面工具
而Mybatis就是在Java程序中编写SQL语句来操作数据库
其实很简单就三步:
- 准备工作(创建springboot工程、数据库表user、实体类User)
- 引入Mybatis的相关依赖,配置Mybatis
- 编写SQL语句(注解/XML)
②也被称为数据库连接的四要素,由于我们现在使用的是SpringBoot整合Mybatis所以这部分的信息我们就需要配置在springboot默认的配置文件application.properties当中
spring.application.name=springboot-mtbatis-quickstart
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis
spring.datasource.username=root
spring.datasource.password=123456
把它放进去就好了~
③编写SQL语句这里我们用注解方式,先定义一个Mapper层(类似于Dao层)写一个UserMapper的接口,在接口上方加上@Mapper(表示这是一个mybatis的mapper接口,在运行时会自动生成实现类(动态代理对象),并将此对象交给IOC容器管理)
再在接口中定义一个方法用来查询所有用户的信息,返回值就设为一个List的集合就可以,集合的泛型就是User;再在这个方法的头上加上Select注解,来指定当前要执行的是一个查询操作,在该注解的value值中就可以来编写SQL语句(@Select @Insert@Delete@Update),到此入门的三步就完成了!
package com.itheima.Mapper;
import com.itheima.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper // 表示这是一个mybatis的mapper接口,在运行时会自动生成实现类(动态代理对象),并将此对象交给IOC容器管理
public interface UserMapper {
// 查询所有用户信息
@Select("select * from user")
public List<User> list();
}
接下来就要做一个测试,在test文件下有个测试类
注意这一步!前面的userMapper是一个接口,这里为什么可以直接定义一个他的对象,接口不是不能实例化吗?
答:这就是在Mapper层中的UserMapper接口上方为什么要加@Mapper注解的关键!
它在运行时会自动生成实现类(一个动态代理对象),并将此对象交给IOC容器管理(就是前面学过的控制反转 类似 @Component这个操作 在IOC容器中就有了这个bean对象),所以在测试类中可以定义UserMapper对象,并且记住在其上方加上@Autowired注解表示依赖注入。(不熟的话可以去看之前的一篇笔记 《 请求响应系统优化实践:基于IOC与DI的分层解耦案例分析》 )
然后再testListUser方法中直接调用userMapper中的list方法即可,并用stream结合lambda表达式 简洁遍历输出结果,完成测试!
撒花~
第二部分 配置MySQL提示
配置好后,user可能会爆红,是因为Idea和数据库没有建立连接,不识别表信息,所以我们在Idea中去配置MySQL数据库连接
连好数据库后,在注解中写SQL语句就会出现提示了,写错了也会马上爆红
第三部分 JDBC介绍(以及跟Mybatis的对比)
• JDBC: (Java DataBase Connectivity), 就是使用Java语言操作关系型数据库的一套API 即接口。
是各个数据库厂商去实现这套接口,提供数据库驱动jar包。
比如上面快速入门程序里xml中引入的依赖就有mysql的驱动:
<!--mysql驱动包-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
原始的JDBC的存在的弊端:
①代码量很大,注册驱动、获取连接 在项目开发中是比较容易变动的,特别是获取连接 在开发时会连接开发数据库,在测试时会连接测试数据库,在生产阶段会连接生产数据库,url、username、password都会变!
而这部分都是被写死在代码中(也称为硬代码),一旦变化,就要重新编译,打包之后才能运行。
②在封装解析结果时,要一个字段一个字段的解析,还要考虑字段类型该调用什么方法,字段一多就愈发的繁琐。
③在执行SQL语句前,先要获取连接,sql语句执行完毕之后,又要关闭连接;在项目中去频繁的获取连接会导致 资源浪费、性能降低!
我们再来对比在Mybatis中是如何解决这些问题的!!
①首先关于硬编码,Mybatis是将数据库连接的四要素直接配置在application.properties中,可以直接在它里面操作就好~
②在解析结果时,Mybatis它会自动的将查询的结果封装到这个集合当中,查询返回的每一条记录都会封装为一个user对象,所有的user对象又会封装到一个List集合中,整个过程自动化进行
③关于频繁获取连接、释放连接 浪费资源问题,Mybatis中,在application.properties中每条配置语句前面都有spring.datasource前缀,springboot底层就会自动的采用数据库连接池技术来统一管理和分配这些连接
其实指的就是JDBC中的Connection对象那一行,有了连接池之后,每一次在执行sql语句的时候,我们只需要从连接池中获取一个连接去执行sql语句,完毕后,再将这个连接还给连接池,那这样就可以做到连接的复用,后面会再讲数据库连接池技术。
没有对比就没有伤害,Mybatis完胜!在我们使用springboot整合mybatis进行数据库操作时,我们主要关注两点:一个就是在application.properties中关于mybatis的相关配置;另一个方面就是mapper接口以及我们所定义的SQL语句~
第四部分 数据库连接池
● 数据库连接池是个容器,负责分配、管理数据库连接(Connection)
● 它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个
● 释放空闲时间超过最大空闲时间的连接,来避免因为没有释放连接而引起的数据库连接遗漏
优势:
①资源的重用
②提升系统响应速度 (创建连接是比较耗时的,但数据库连接池支持连接复用)
③避免数据库连接遗漏
在Java中默认使用的是Hikari追光者连接池,我们在下面可以看到HikariDataSource的源码实现了DataSource这个官方的标准接口
除了Hikari还有Druid(德鲁伊):
● Druid连接池是阿里巴巴开源的数据库连接池项目
● 功能强大,性能优秀,是java语言最好的数据库连接池之一
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>
并在application中加上:spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
第五部分 XML映射文件
在前面的Mybatis中配置SQL语句都是用的注解,现在我们要使用xml配置文件
三点规范:
● XML映射文件的名称与Mapper接口名称一致,并且将XML映射文件和Mapper接口放置在相同包下(同包同名)。注意在resources中建包时用 / 而不是 . :
● XML映射文件的namespace属性为Mapper接口全限定名一致。
xml文件中前面是一段约束,在Mybatis中文网-入门下方就有 直接粘贴进来就好,namespace这儿是要填接口的全类名!
● XML映射文件中sql语句的id与Mapper 接口中的方法名一致,并保持返回类型一致。
在原来我们是通过Mapper接口中写的方法上方写注解来执行SQL语句,现在用这个xml文件是,在调用这个接口代理对象(比如EmpMapper接口的empMapper对象)中的list方法时,根据方法名list去EmpMapper.xml文件中快速匹配到sql语句,并执行。
所以:使用注解来映射简单语句会使代码显得更加简洁, 但对于稍微复杂一点的语句, Java 注解不仅力不从心, 还会让你本就复杂的SQL语句更加混乱不堪。 因此, 如果你需要做一些很复杂的操作, 最好用XML 来映射语句
第六部分 Mybatis动态SQL
随着用户的输入或外部条件的变化而变化的SQL语句,我们称为动态SQL
比如下图我们的XML中配置的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="com.itheima.Mapper.EmpMapper">
<select id="list" resultType="com.itheima.pojo.Emp">
select * from emp where name like concat('%',#{name},'%') AND gender=#{gender} AND
entrydate between #{createdTime} and #{updatedTime}
</select>
</mapper>
Mapper接口里的方法参数也与之对应:
接着我们在测试类中调用list方法,正常传递每一个参数:
@SpringBootTest
class SpringbootMybatisQuickstartApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
public void testList(){
List<Emp> empList = empMapper.list("张", (short)1, LocalDate.of(2010, 1, 1),
LocalDate.of(2020, 1, 1 ) );
System.out.println(empList);
}
}
会得到正确的查询结果:
但是当只想输入部分参数的时候就会出现问题,比如把gender字段传null值:
哦豁就查不到了因为我们在xml写的SQL语句已经将其固定死了,所以就有了动态SQL这个说法其实很简单就是在Mybatis中提供了一个if标签做条件判断,当然还有很多标签。
我们先来看 标签:
:用于判断条件是否成立。使用test属性进行条件判断,如果条件为true,则拼接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="com.itheima.Mapper.EmpMapper">
<select id="list" resultType="com.itheima.pojo.Emp">
select *
from emp
where
<if test="name != null" >
name like concat('%', #{name}, '%')
</if>
<if test="gender != null" >
and gender = #{gender}
</if>
<if test=" begin != null and end != null" >
and entrydate between #{begin} and #{end}
</if>
order by update_time desc
</select>
</mapper>
这下我们在来尝试查询 只输入name含有“张”,其他都为null:
查出来了!!
看这里也能发现,我们输入几个参数他就有几个查询条件
我们再来测试 查询输入 name为张、性别为男、其余为null的:
也是成功查询到了!
再来试试name为null时、性别为男、其他也为null时:
这里会发现是报SQL语法的错,观察我们的SQL语句和<if标签>,如果name为null跳过第一个,gender有值,SQL语句就变成了 select * from emp where and gender = ...
所以这里where后面就直接是and了,语法就有问题!
所以就又有了一个标签来替代where这个关键字:将所有的查询条件包裹起来,有两个作用:①如果某个条件成立就会生成where关键字,若一个条件都不满足就不会生成 ②会自动帮你去除多余的and或者or
<?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="com.itheima.Mapper.EmpMapper">
<select id="list" resultType="com.itheima.pojo.Emp">
select *
from emp
<where>
<if test="name != null" >
name like concat('%', #{name}, '%')
</if>
<if test="gender != null" >
and gender = #{gender}
</if>
<if test=" begin != null and end != null" >
and entrydate between #{begin} and #{end}
</if>
</where>
order by update_time desc
</select>
</mapper>
再来测试一下:
成功啦~查到了所有男同胞们的信息
再来试试四个值都不传递(理想结果就应该是select * from emp order by update_time desc; 没有where!):
结果不出所料~它没有加上where关键字
跟where用法一模一样的还有一个set标签,它会自动去除多余的逗号,如果条件符合就会自动加上set关键字(一般都是配合Update使用)
<mapper namespace="com.itheima.Mapper.EmpMapper">
<update id="update2">
update emp
<set>
<if test="username!= null">username = #{username},</if>
<if test="name!= null">username = #{name},</if>
<if test="gender!= null">gender = #{gender},</if>
<if test="image!= null">image = #{image},</if>
<if test="job!= null">job = #{job},</if>
<if test="entrydate!= null">entrydate = #{entrydate},</if>
<if test="update_time!= null">update_time = #{update_time},</if>
where id = #{id}
</set>
</update>
再同理在我们要实现批量删除操作时,我们还会用到一个标签是来遍历:
<!--批量删除员工8,9,10-->
<delete id="deleteByIds">
<!--
collection代表要遍历的集合
item指遍历出来的元素
separator代表遍历元素的分隔符
open 和 close 分别代表最开始的符号和结束的符号
-->
delete from emp where id in
<foreach collection="ids" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</delete>
在测试类中写一个方法来测试一下看是否能成功删除:
@Test
public void testDeleteByIds(){
List<Integer> ids = Arrays.asList(13,14,15);
empMapper.deleteByIds(ids);
}
可以发现成功的删除了13.14.15的信息
在动态SQL标签中,还有一个和的使用
比如在这个select语句中,前面标红的这两条代码都是重复的,就会存在项目后期修改字段,改动就会很大,所以就可以把重复的代码抽取到标签中,并且为这个select片段起一个id名
<sql id="commonSelect">
select id,username,name,gender,image,job,entrydate,deptId,createTtime,updateTime from emp
</sql>
在原代码处用引用进来
<sql id="commonSelect">
select id,username,name,gender,image,job,entrydate,dept_id,create_time,update_time
</sql>
<select id="list" resultType="com.itheima.pojo.Emp">
<include refid="commonSelect"/>
from emp
<where>
<if test="name != null" >
name like concat('%', #{name}, '%')
</if>
<if test="gender != null" >
and gender = #{gender}
</if>
<if test=" begin != null and end != null" >
and entrydate between #{begin} and #{end}
</if>
</where>
</select>
这样就大大提升了代码的复用性。
Mybatis入门大概就差不多了,还要多实际练练才熟练!