1.1 Mybatis概述
1.1.1 什么是 Mybatis
MyBatis 是一款优秀的持久层框架,用于简化 JDBC 开发。MyBatis 本是 Apache 的一个开源项目iBatis,2010年这个项目由 apache software foundation 迁移到了google code,并且改名为 MyBatis 。2013年11月迁移到 Github 官网:https://mybatis.org/mybatis-3/zh/index.html
持久层框架的使用占比:
持久层
负责将数据到保存到数据库的那一层代码。 以后开发我们会将操作数据库的 Java 代码作为持久层。而 Mybatis 就是对 jdbc 代码进行了封装。
框架
框架就是一个半成品软件,是一套可重用的、通用的、软件基础代码模型 。在框架的基础之上构建软件编写更加高效、规范、通用、可扩展。
1.1.2 JDBC 缺点
下面是 JDBC 代码,我们通过该代码分析都存在什么缺点:
1.存在硬编码
-
注册驱动、获取连接:上图标1的代码有很多字符串,而这些是连接数据库的四个基本信息,以后如果要将 Mysql 数据库换成其他的关系型数据库的话,这四个地方都需要修改,如果放在此处就意味着要修改我们的源代码。
-
SQL语句:上图标 2 的代码。如果表结构发生变化,SQL语句就要进行更改。这也不方便后期的维护。
2.操作繁琐
-
需要手动设置参数:上图标 3 的代码需要为 SQL 语句手动设置参数。
-
需要手动封装结果集 :上图标 4 的代码是对查询到的数据进行封装,而这部分代码是没有什么技术含量,而且特别耗费时间
1.1.3 使用 MyBatis 进行优化
-
硬编码可以配置到配置文件
-
操作繁琐的地方mybatis都自动完成
如下图所示:
1.2 MyBatis使用步骤
这里,我们借助一个需求来进行 MyBatis 的快速入门。
需求:查询user表中所有的数据
步骤:具体步骤如下所示
第一步:在数据库中创建一个 user 表,添加数据
第二步:创建模块,导入相关坐标
在这一步中别忘了在 resource 目录下添加 logback 的配置文件 logback.xml
第三步:编写 MyBatis 核心配置文件
通过核心配置文件替换连接信息解决硬编码问题,在 resources 目录下创建 mybatis 的配置文件 mybatis-config.xml ,内容如下:
第四步:编写 SQL 映射文件
统一管理 sql 语句,解决硬编码问题。在 resources 目录下创建映射配置文件 UserMapper.xml ,内容如下:
第五步:编码
在 com.itheima.pojo 包下创建 User类
在 com.itheima 包下编写 MybatisDemo 测试类
1.3 Mapper代理开发
1.3.1 Mapper代理开发概述
MyBatis基本使用方式的问题:快速入门里写的代码是 MyBatis 的基本使用方式,它也存在硬编码的问题,如下:
这里调用 selectList() 方法传递的参数是映射配置文件中的 namespace.id 值。这样写也不便于后期的维护。如果使用如下图所示的 Mapper 代理方式则不存在硬编码问题。
Mapper代理的好处
-
解决原生方式中的硬编码
-
简化后期执行 SQL
1.3.2 使用Mapper代理要求
使用Mapper代理方式,必须满足以下要求:
1. 定义与SQL映射文件同名的 Mapper 接口,并且将 Mapper 接口和 SQL 映射文件放置在同一目录下。如下图所示:
2. 设置SQL映射文件的 namespace 属性为 Mapper 接口全限定名
3. 在 Mapper 接口中定义方法,方法名就是SQL映射文件中 sql 语句的 id,并保持参数类型和返回值类型一致
1.4 核心配置文件
<?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">
<configuration>
<!--类型别名,简化sql映射文件中resultType属性值的编写-->
<typeAliases>
<typeAlias alias="User" type="com.example.User"/><!--单个类型别名的配置-->
<package name="com.example.model"/><!-- 批量配置多个类型别名 -->
</typeAliases>
<!--mybatis的全局配置-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/><!--日志设置为MyBatis提供的标准日志-->
</settings>
<!--多环境配置,default表示默认使用的环境-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/> <!--设置为JDBC的事务管理器,默认开启事务但不自动提交-->
<dataSource type="POOLED">
<!--数据库连接信息-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/使用的数据库名"/>
<property name="username" value="用户名"/>
<property name="password" value="用户名对应的密码"/>
</dataSource>
</environment>
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/使用的数据库名"/>
<property name="username" value="用户名"/>
<property name="password" value="用户名对应的密码"/>
</dataSource>
</environment>
</environments>
<!--映射文件位置-->
<mappers>
<mapper resource="com/example/mapper/UserMapper.xml"/><!--扫描指定的sql映射⽂件-->
<package name="com.example.mapper"/><!--扫描包下的所有sql映射⽂件-->
</mappers>
</configuration>
1.5 映射文件
<?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.example.dao.UserMapper">
<!-- 查询结果映射 -->
<resultMap id="userResultMap" type="User">
<id property="id" column="user_id" />
<result property="username" column="user_name"/>
<result property="password" column="hashed_password"/>
</resultMap>
<!-- 查询操作 -->
<select id="getUserById" parameterType="int" resultType="userResultMap">
SELECT * FROM users WHERE id = #{id}
</select>
<!-- 插入操作 -->
<insert id="insertUser">
INSERT INTO users (username, password, email) VALUES (#{username}, #{password}, #{email})
</insert>
<!-- 更新操作 -->
<update id="updateUser">
UPDATE users SET username = #{username}, password = #{password}, email = #{email} WHERE id = #{id}
</update>
<!-- 删除操作 -->
<delete id="deleteUser">
DELETE FROM users WHERE id = #{id}
</delete>
</mapper>
1.5.1 参数占位符
参数占位符是指在SQL语句里使用的占位符,用来表示需要动态插入的参数值。mybatis提供了两种参数占位符,分别是#{}、${},开发中建议使用 #{} ,不要使用 ${}。
-
${} :在处理 ${} 时,是把SQL中的 ${} 直接替换成变量值,然后发送给MySQL服务器执行。
-
#{} :在处理 #{} 时,首先会将SQL中的 #{} 替换为占位符 ?,接着进行预编译,然后调用PreparedStatment的set方法,这个set方法能够对参数值中的敏感字符进行转义,防止SQL注入,并能将转义后的参数值发送给MySQL,绑定到占位符上。
1.5.2 parameterType
对于有参数的 mapper 接口方法,我们在映射配置文件中应该配置 parameterType 来指定参数类型。但是该属性可以省略。
<select id="selectById" parameterType="int" resultMap="brandResultMap">
select *
from tb_brand where id = ${id};
</select>
1.5.3 SQL语句的特殊字段
以后肯定会在SQL语句中写一下特殊字符,比如某一个字段大于某个值,如下图
可以看出报错了,因为映射配置文件是 xml 类型的问题,而 > < 等这些字符在 xml 中有特殊含义,所以此时我们需要将这些符号进行转义,可以使用以下两种方式进行转义
-
转义字符:下图的 < 就是 < 的转义字符。
-
使用 <![CDATA[内容]]>
1.5.4 动态拼接SQL
1.<if> 标签
<if> 标签允许根据条件动态包含 SQL 片段,在 test 属性中定义条件。
<select id="getUserById" parameterType="int" resultType="com.example.model.User">
SELECT * FROM users
WHERE id = #{id}
<if test="username != null">
AND username = #{username}
</if>
</select>
2.<where> 标签
<where> 标签用于拼接 WHERE 子句,它会自动去除不必要的 AND 或 OR。另外,使用where标签时,需要给每个条件前都加上 and 关键字。
<select id="getUsersByCriteria" parameterType="map" resultType="com.example.model.User">
SELECT * FROM users
<where>
<if test="username != null">
AND username = #{username}
</if>
<if test="email != null">
AND email = #{email}
</if>
</where>
</select>
3. <set> 标签
<set> 标签用于拼接 UPDATE 语句的 SET 子句。
<update id="updateUser" parameterType="com.example.model.User">
UPDATE users
<set>
<if test="username != null">username = #{username},</if>
<if test="password != null">password = #{password},</if>
<if test="email != null">email = #{email},</if>
</set>
WHERE id = #{id}
</update>
4.<foreach> 标签
<foreach> 标签用于迭代集合元素,动态生成多个 SQL 片段。
-
collection 属性:mybatis 会将数组参数,封装为一个Map集合。默认:array = 数组;使用@Param注解改变map集合的默认key的名称
-
item 属性:本次迭代获取到的元素。
-
separator 属性:集合项迭代之间的分隔符。
foreach
标签不会错误地添加多余的分隔符。也就是最后一次迭代不会加分隔符。 -
open 属性:该属性值是在拼接SQL语句之前拼接的语句,只会拼接一次
-
close 属性:该属性值是在拼接SQL语句拼接后拼接的语句,只会拼接一次
<select id="getUsersByIdList" parameterType="java.util.List" resultType="com.example.model.User">
SELECT * FROM users
WHERE id IN
<foreach item="item" index="index" collection="idList" open="(" separator="," close=")">
#{item}
</foreach>
</select>
5.<sql>、<include>标签
<sql> 标签用于定义可重用的 SQL 片段,可以在多个 SQL 语句中引用。<include>标签用于在SQL语句中引用SQL片段。
<!-- 定义一个可重用的 SQL 片段 -->
<sql id="userColumns">
id, username, password, email
</sql>
<!-- 在 SELECT 语句中引用该 SQL 片段 -->
<select id="getUsers" resultType="com.example.model.User">
SELECT
<include refid="userColumns"/>
FROM users
</select>
6.<choose>、<when>、<otherwise>标签
<choose> 标签类似于 Java 中的 switch 语句,允许在多个选项中选择一个执行。每个 <when> 标签表示一个选项,最后的 <otherwise> 标签用于指定默认情况。
<select id="getUserByCriteria" parameterType="com.example.model.User" resultType="com.example.model.User">
SELECT * FROM users
<where>
<choose>
<when test="id != null">
AND id = #{id}
</when>
<when test="username != null">
AND username = #{username}
</when>
<otherwise>
AND 1=1 <!-- 默认情况,可以省略 -->
</otherwise>
</choose>
</where>
</select>
2.10 Mybatis 参数传递
Mybatis 接口方法中可以接收各种各样的参数,如下:
多个参数:以后接口参数是多个时,在每个参数上都使用 @Param 注解。这样代码的可读性更高。
单个参数:单个参数又可以是如下类型
-
POJO 类型
-
Map 集合类型
-
Collection 集合类型
-
List 集合类型
-
Array 类型
-
其他类型
2.10.1 多个参数
如下面的代码,就是接收两个参数,而接收多个参数需要使用 @param 注解,那么为什么要加该注解呢?
User select(@Param("username") String username,@Param("password") String password);
<select id="select" resultType="user">
select *
from tb_user
where
username=#{username}
and password=#{password}
</select>
上面这个问题要弄明白就必须来研究 Mybatis 底层对于这些参数是如何处理的。
1.我们在接口方法中定义多个参数,Mybatis 会将这些参数封装成 Map 集合对象,值就是参数值,Mybatis 需要在 SQL 语句中通过键得到 Map 集合中的值,而键在没有使用 @Param 注解时有以下命名规则:
-
以 arg 开头 :第一个参数就叫 arg0,第二个参数就叫 arg1,以此类推。如:
map.put("arg0",参数值1);
map.put("arg1",参数值2);
-
以 param 开头 : 第一个参数就叫 param1,第二个参数就叫 param2,依次类推。如:
map.put("param1",参数值1);
map.put("param2",参数值2);
2.在接口方法参数上使用 @Param 注解,Mybatis 会将 arg 开头的键名替换为对应注解的属性值。
2.10.2 单个参数
POJO 类型:直接使用。要求 属性名 和 参数占位符 名称 一致
Map 集合类型:直接使用。要求 map 集合的 键名 和 参数占位符 名称 一致
Collection 集合类型:Mybatis 会将集合封装到 map 集合中,如下:
-
map.put("arg0",collection集合);
-
map.put("collection",collection集合);
List 集合类型:Mybatis 会将集合封装到 map 集合中,如下:
-
map.put("arg0",list集合);
-
map.put("collection",list集合);
-
map.put("list",list集合);
Array 类型:Mybatis 会将集合封装到 map 集合中,如下:
-
map.put("arg0",数组);
-
map.put("array",数组);
其他类型:比如 int 类型,参数占位符名称叫什么都可以。尽量做到见名知意
==可以使用
@Param
注解替换map集合中默认的 arg 键名。==
2.11 Mapper注解
使用代码获取代理对象:在MyBatis中,利用编程式进行数据查询,主要就是下面几行代码。
//获取SqlSession对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
//获取Mapper代理对象
UserMapper userMapper = session.getMapper(UserMapper.class);
//利用代理对象实现增删改查
List<LwUser> userList = userMapper.listUserByUserName("孤狼1号");
使用@Mapper注解获取代理对象:上述获取 Mapper 接口的代理对象非常麻烦,我们在引入 Mybatis 和 Spring 整合的 jar 包后,可以将 Mybatis 提供的 @Mapper 注解放到 Mapper 接口的上面,将代理对象注册为 Spring 的 Bean,在使用的时候,直接从 IOC 容器中取即可。另外,不要忘了添加 @MapperScan 注解对 @Mapper 注解进行扫描。
@Mapper
public interface UserMapper extends BaseMapper<User> {
@Select("select * from tb_user")
List<User> getAll();
}
2.12 注解实现
上述都是在映射文件中编写SQL语句,我们可以使用注解替换映射文件中的SQL语句。通常,注解和配置文件可以搭配起来使用,注解完成简单功能,配置文件完成复杂功能。我们之前写的动态 SQL 就是复杂的功能。如下就是使用注解进行开发:
@Repository("userMapper")
public interface UserMapper {
// 插入用户
@Insert("INSERT INTO users (name, email) VALUES ({0}, {1})")
void addUser(@Param("name") String name, @Param("email") String email);
// 查询所有用户
@Select("SELECT * FROM users")
List<User> getAllUsers();
// 更新用户信息
@Update("UPDATE users SET name = #{name}, email = #{email} WHERE id = #{id}")
void updateUser(@Param("id") Long id, @Param("name") String name, @Param("email") String email);
// 删除用户
@Delete("DELETE FROM users WHERE id = #{id}")
void deleteUser(@Param("id") Long id);
}