配置
首先在 pom.xml 引入 mybatis 依赖:
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
同时导入你要使用的数据库的驱动的依赖,这里以 MySQL 为例,我们要使用MySQL数据库就需要导入MySQL驱动的依赖:
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
接下来就是配置数据库的信息:
这里以MySQL 为例:
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
把要操作的数据库的信息全部配置好,url、用户名、密码、驱动
创建实体类
根据我们要操纵的表创建好对应的实体类
回忆数据库建表规范:
我们使用统一使用小写字母,并且每一个单词之间使用下划线进行分割: user_name
回忆Java 构建 类 的规范
类名使用大驼峰的方式:UserInfo
属性和方法均使用小驼峰的方式: userName
我们根据表名和表内部的字段名来构建与之对应的类
其他配置:
我们还可以配置打印 mybatis 的日志信息
mybatis:
configuration: # 配置打印 MyBatis⽇志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
注解
@Select
@Insert
@Update
@Delete
示例:
@Select("select * from user_info where id = #{id}")
List<UserInfo> selectById(Integer id);
@Insert("insert into user_info (username, `password`, age) values (#{username}, #{password}, #{age})")
Integer insertUserInfo(UserInfo userInfo);
@Update("update user_info set age = 100 where id = #{id}")
Integer updateById(Integer id);
@Delete("delete from user_info where id = #{id}")
Integer deleteById(Integer id);
问题来了:
因为数据库的建表规范和Java的搭建类的规范是不相同的,那么我们如何让类的属性和数据库的表的字段进行一一匹配呢?
我们先来看看使用注解这种情况如何解决:
很简单,我们可以在 pom.xml 文件中导入 驼峰自动转换,只要你遵守上面的规范,spring 就会自动帮你进行转换。
mybatis:
configuration:
map-underscore-to-camel-case: true #配置驼峰自动转换
第二种方式就是起别名,但是很麻烦,每次写 sql 语句都要起别名,将别名和类的属性对应起来:
@Select("select id, username, `password`, age, gender, phone, delete_flag as deleteFlag, " +
"create_time as createTime, update_time as updateTime from user_info")
public List<UserInfo> queryAllUser();
第三种方式是结果映射,通过 @Results 注解将 每一个表字段 和 类属性一一对应起来
@Results(id = "baseMap", value = {
@Result(column = "delete_flag", property = "deleteFlag"),
@Result(column = "create_time", property = "createTime"),
@Result(column = "update_time", property = "updateTime")
})
如果想要使用上面 baseMap 的注解的话,我们使用 @ResultMap 来指定 即可
@ResultMap(value = "baseMap")
@Select("select * from user_info where id = #{id}")
List<UserInfo> selectById(Integer id);
XML
使用 XML 来书写 sql 语句的时候,我们首先要进行配置:
在 pom.xml 上配置好 xml 的路径,这样 spring 才会进行扫描
mybatis:
mapper-locations: classpath:mapper/*Mapper.xml
在 rescources 文件夹下创建好 xml 文件:
在 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="org.example.mybatis_demo.mapper.UserInfoMapperXML">
//在此处写 sql 语句
<mapper>
namespace 是与之对应 的 类,就是 这个 XML 文件是为哪一个类提供 sql 语句的
其他建议:由于使用 xml 的方式来创建 sql 语句,为了方便我们知道这条 sql 语句对应的是哪一个方法,这里推荐使用 mybatis X 插件:
点击小鸟图标就会自动跳转到另一个与之对应的语句
示例:
<select id="selectByName" resultType="org.example.mybatis_demo.model.UserInfo">
select * from user_info where username = #{name}
</select>
<insert id="insertUserInfo">
insert into user_info (username, `password`, age, gender, phone, delete_flag)
values (#{username}, #{password}, #{age}, #{gender}, #{phone}, #{deleteFlag})
</insert>
还是一样的问题来了:
因为数据库的建表规范和Java的搭建类的规范是不相同的,那么我们如何让类的属性和数据库的表的字段进行一一匹配呢?
我们来看XML 这种方式如何解决?
第一种解决方式还是和上面的一样,我们可以在 pom.xml 文件中导入 驼峰自动转换,只要你遵守上面的规范,spring 就会自动帮你进行转换。
mybatis:
configuration:
map-underscore-to-camel-case: true #配置驼峰自动转换
第二种方式就是起别名,但是很麻烦,每次写 sql 语句都要起别名,将别名和类的属性对应起来。
第三种方式是结果映射,通过 resultMap 将 每一个表字段 和 类属性一一对应起来
<resultMap id="BaseMap" type="com.example.demo.model.UserInfo">
<id column="id" property="id"></id>
<result column="delete_flag" property="deleteFlag"></result>
<result column="create_time" property="createTime"></result>
<result column="update_time" property="updateTime"></result>
</resultMap>
如果想要使用上面 BaseMap 的注解的话,我们需要进行指定
<select id="queryAllUser" resultMap="BaseMap">
select id, username,`password`, age, gender, phone, delete_flag, create_time, update_time
from user_info
</select>
#{} 和 ${}
先观察:使用 #{}
@Select("select * from user_info where id = #{id}")
List<UserInfo> selectById(Integer id);
通过观察 mybatis 日志,我们会发现 #{} 会变成 ? 进行占位
如果我们使用 ${} ,数值就会直接替换
@Select("select * from user_info where id = ${id}")
List<UserInfo> selectById(Integer id);
结论:#{} 是 预编译 sql,${} 是运行时 sql,预编译 sql 的性能会更好一些
${} 会存在 sql 注入的风险
但是并不是说 ${} 就没有任何用处,例如有一个业务的场景需要用到 数据库的排序操作,这时候我们需要传入 desc 或者 asc 字符串的时候,#{} 就不能完成,因为 #{} 是不可以成为 sql 命令的一部分,只能作为数据来进行填充,这时候只能使用 ${} 来进行,只要我们对外界不提供输入字符串的功能,而是使用类似按键的功能让用户选择升序还是降序排序,这样我们就可以避免 ${} 产生的 sql 注入问题
使用 like 关键字来进行模糊查询的时候,#{} 会报错,这时候需要使用 ${} ,为了避免产生sql 注入,我们可以使用内置函数 concat 来处理
@Select("select id, username, age, gender, phone, delete_flag, create_time,
update_time " +
"from user_info where username like concat('%',#{key},'%')")
List<UserInfo> queryAllUserByLike(String key);
动态 SQL
在传入数据库的数据,有些字段可以是非必填,这时候我们需要灵活地处理我们的 sql 语句,那么就需要用到动态 sql
这里使用 XML 的方式来进行演示:
< if > :和 if 语句一样的效果,如果满足 if 条件,就会添加 if 标签内部的 sql
<insert id="insertUserInfo2">
insert into user_info (
<if test="username!=null">
username,
</if>
<if test="password!=null">
`password`,
</if>
<if test="age!=null">
age,
</if>
<if test="gender!=null">
gender
</if>
) values (
<if test="username!=null">
#{username},
</if>
<if test="password!=null">
#{password},
</if>
<if test="age!=null">
#{age},
</if>
<if test="gender!=null">
#{gender}
</if>
)
</insert>
但是上面的语句有个问题,如果 gender 为空,但是前面的字段不为空, 那 sql 就会出现问题,你会发现多了一个逗号,这是一个非法的 sql:
insert into user_info username, `password`, age, values (?, ?, ?, )
这时候就要用到 tirm 标签:
<trim prefixOverrides="," suffixOverrides="," prefix="(" suffix=")">
<if test="username!=null">
username,
</if>
<if test="password!=null">
`password`,
</if>
<if test="age!=null">
age,
</if>
<if test="gender!=null">
gender
</if>
</trim>
prefixOverrides 表示去除前面的什么符号
suffixOverrides 表示去除后面什么符号
prefix 最前面使用什么符号
suffix 最后面使用什么符号
这样我们就可以避免 if 标签出现的 逗号问题了
where 标签:
如果有 where 语句的话,可以使用 where 标签来包裹,但是注意在删除,更新这两个操作的时候必须添加 where 语句,避免误删或者全部更新整张表,因此在删除和更新上面我们不推荐使用 where 标签
<select id="selectAllById" resultType="org.example.mybatis_demo.model.UserInfo">
select * from user_info
<where>
id = #{id}
</where>
</select>
foreach 标签:通常用来遍历数组、链表等多数据的情况
<foreach collection="list" separator="or" item="user">
id = #{user}
</foreach>
collection 指你要遍历的数组或者链表的名称(自定义,你自己随意,这里没有任何约束)
separator 表示每组数据使用什么来进行分割
item 表示每一个元素的名称(自定义,你自己随意,这里没有任何约束)
set 标签,这是使用在 更新语句上的标签
<update id="updateUserInfoByName">
update user_info
<set>
<if test="password!=null">
`password` = #{password},
</if>
<if test="gender!=null">
gender = #{gender},
</if>
<if test="deleteFlag!=null">
delete_flag = #{deleteFlag}
</if>
</set>
where
username = #{username}
</update>
include 和 sql 标签:
在编写 sql 语句,如果我们发现有很多sql 片段重复出现,我们可以使用 sql 标签来进行封装:
例如我们的更新语句频繁书写,我们使用 sql 来对 前半部分的 sql 片段来进行封装,使用 id 来指定该片段的名称,便于后续的调用
<sql id="update1">
update user_info
</sql>
然后我们使用 include 标签来进行 sql 标签内的 sql 语句的调用:通过 id 来绑定 sql 标签的内容
<update id="updateById2">
<include refid="update1"></include>
<set>
<if test="password!=null">
`password` = #{password},
</if>
<if test="age!=null">
age = #{age}
</if>
</set>
where id = #{id}
</update>
这里不推荐使用 注解的方式来写 动态sql ,因为会变成一坨,书写的时候还没有提示:
使用注解和上面的 XML 是一样的语法:
@Select("<script>select id, username, age, gender, phone, delete_flag,
create_time, update_time" +
" from userinfo" +
" <where>" +
" <if test='age != null'> and age = #{age} </if>" +
" <if test='gender != null'> and gender = #{gender} </if>" +
" <if test='deleteFlag != null'> and delete_flag = #
{deleteFlag} </if>" +
" </where>" +
"</script>")
List<UserInfo> queryByCondition(UserInfo userInfo);
十分建议使用 XML 的方式来写动态sql