MyBatis(前身IBatis)定义:MyBatis 是一款优秀的持久层框架(ORM框架)
MyBatis支持的操作方式:
1.支持XML的操作方式(MyBatis 3.1 之前)【主流的操作方式】。
2.使用注解实现数据的操作(MyBatis 3.1 之后)
简单来说 MyBatis 是更简单完成程序和数据库交互的工具,也就是更简单的操作和读取数据库工具。
-- 创建数据库
drop database if exists mycnblog;
create database mycnblog DEFAULT CHARACTER SET utf8mb4;
-- 使用数据数据
use mycnblog;
-- 创建表[用户表]
drop table if exists userinfo;
create table userinfo(
id int primary key auto_increment,
username varchar(100) not null,
password varchar(32) not null,
photo varchar(500) default '',
createtime datetime default now(),
updatetime datetime default now(),
`state` int default 1
) default charset 'utf8mb4';
-- 创建文章表
drop table if exists articleinfo;
create table articleinfo(
id int primary key auto_increment,
title varchar(100) not null,
content text not null,
createtime datetime default now(),
updatetime datetime default now(),
uid int not null,
rcount int not null default 1,
`state` int default 1
)default charset 'utf8mb4';
-- 创建视频表
drop table if exists videoinfo;
create table videoinfo(
vid int primary key,
`title` varchar(250),
`url` varchar(1000),
createtime datetime default now(),
updatetime datetime default now(),
uid int
)default charset 'utf8mb4';
-- 添加一个用户信息
INSERT INTO `mycnblog`.`userinfo` (`id`, `username`, `password`, `photo`, `createtime`, `updatetime`, `state`) VALUES
(1, 'admin', 'admin', '', '2021-12-06 17:10:48', '2021-12-06 17:10:48', 1);
-- 文章添加测试数据
insert into articleinfo(title,content,uid)
values('Java','Java正文',1);
-- 添加视频
insert into videoinfo(vid,title,url,uid) values(1,'java title','http://www.baidu.com',1);
s框架




耐心等待项目加载完成即可。

前置条件:添加插件Editstarters:




之后导入包即可。
注意事项:
经过以上操作,就创建了SSM(Spring Boot/Spring MVC/MyBatis)项目,但此时因为没有配置连接的数据库服务器地址,如果此时启动项目,就会报错
此步骤需要进行两项设置,数据库连接字符串设置和 MyBatis 的 XML 文件配置。
如果是 application.yml 添加如下内容:
#配置数据库的连接字符串
spring:
datasource:
url: jdbc:mysql://127.0.0.1/mycnblog?characterEncoding=utf8mb4
username: root
password: 12345678
driver-class-name: com.mysql.cj.jdbc.Driver

#配置MyBatis中XML保存路径
mybatis:
mapper-locations: classpath:mybatis/**Mapper.xml
MyBatis模式就是两个文件:@Mapper文件(定义方法,没有方法实现)+XML会实现@Mapper的方法体
import com.example.demo.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper {
//查询方法定义完成
public UserInfo getUserById(Integer id);
}
<?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.demo.mapper.UserMapper">
<!-- 查询单条数据(根据id) -->
<select id="getUserById" resultType="com.example.demo.model.UserInfo">
<!-- select * from userinfo where id=#{id}-->
select * from userinfo where id=#{id}
</select>
</mapper>

@Autowired属于Spring注解,而@Mapper属于MyBatis的注解,Spring中的@Autowired不能识别MyBatis的@Mapper注解,所以会报错。
解决方案:
将Service里的Spring的@Autowireg更换为JDK的@Resource
方法实现:
<!-- 用户添加(返回受影响的行数) -->
<insert id="add">
insert into userinfo(username,password)
values(#{username},#{password})
</insert>
<!-- 用户添加(返回自增 ID) -->
<insert id="add2" useGeneratedKeys="true" keyProperty="id">
insert into userinfo(username,password) values(#{username},#{password})
</insert>

方法声明:
//添加用户(返回受影响行数)
public int add(String username,String password);
// 添加用户 2(返回的自增 ID)
public void add2(UserInfo userInfo);

方法声明:
//修改操作(根据id修改username,返回受影响的行数)
public int update(@Param("id") int id, @Param("username") String username);
方法实现:
<!-- 根据 id 修改 username(返回受影响的行数) -->
<update id="update">
update userinfo set username=#{username} where id=#{id}
</update>
方法声明:
//删除方法(返回受影响的行数)
public int del(@Param("id") int id);
方法实现:
<!-- 根据 id 进行删除(返回受影响的行数) -->
<delete id="del">
delete from userinfo where id=#{id}
</delete>
@Transactional 注解
@Transactional // 单元测试的类或者方法上加上此注解,表示当前测试的数据不会影响数据库
@Test
void del() {
int result = userMapper.del(3);
Assertions.assertEquals(1, result);
}
}
#{}:预编译处理,不存在安全问题。
${}:字符直接替换,存在安全问题。
${}使用场景:当传递的是一个SQL语句时(而非一个某个参数的值),那么只能使用 ${}的形式,比如传递排序的 desc 或者是 asc 的时候,他是一个SQL语句,而非某一个参数的值。
<!-- 根据时间排序查询所有的用户 -->
<select id="getAllOrderByCreateTime" resultType="com.example.demo.model.UserInfo">
select * from userinfo order by createtime ${order}
</select>

预编译处理是指:MyBatis 在处理#{}时,会将 SQL 中的 #{} 替换为?号,使用 PreparedStatement 的set 方法来赋值。
直接替换:是MyBatis 在处理 ${} 时,就是把 ${} 替换成变量的值。
使用 ${sort} 可以实现排序查询,而使用 #{sort} 就不能实现排序查询了,因为当使用 #{sort} 查询时,如果传递的值为 String 则会加单引号,就会导致 sql 错误。
方法声明:
//登录方法
public UserInfo login(@Param("username") String username,
@Param("password") String password);
方法实现(使用${}):
<!-- 登录方法 -->
<select id="login" resultType="com.example.demo.model.UserInfo">
select * from userinfo where username='${username}' and password='${password}'
</select>

| 结论:用于查询的字段,尽量使用 #{} 预查询的方式。 |
如图配置即可:

1.like 使用 #{} 报错
<!-- 根据用户名称进行模糊查询 -->
<select id="getUserByLikeName" resultType="com.example.demo.model.UserInfo">
select * from userinfo where username like '%#{username}%'
</select>
2.使用 ${}
<!-- 根据用户名称进行模糊查询 -->
<select id="getUserByLikeName" resultType="com.example.demo.model.UserInfo">
select * from userinfo where username like '%${username}%'
</select>
可以实现功能,但是存在安全风险。
3.可以考虑使用 mysql 的内置函数 concat() 来处理,实现代码如下:
<!-- 根据用户名称进行模糊查询 -->
<select id="getUserByLikeName" resultType="com.example.demo.model.UserInfo">
select * from userinfo where username like concat('%',#{username},'%')
</select>
对于 查询标签来说至少需要两个属性:
| 结果映射属性:结果映射有两种实现标签:resultMap和 resultType |
绝大数查询场景可以使用 resultType 进行返回,它的优点是使用方便,直接定义到某个实体类即可。
resultMap 使用场景:
字段名和属性名不同的情况

程序中属性如下:

mapper.xml 代码如下:
<select id="getAll" resultType="com.example.demo.model.ArticleInfo">
select * from articleinfo
</select>
查询出来name=NULL;
resultMap 的使用如下:

resultMap 基础使用:


一对一映射要使用 association标签,具体实现如下(一篇文章只对应一个作者):
ArticleMapper.xml实现代码:
<resultMap id="BaseMap" type="com.example.demo.model.ArticleInfo">
<id column="id" property="id"></id>
<result column="title" property="name"></result>
<result column="content" property="content"></result>
<result column="createtime" property="createtime"></result>
<result column="updatetime" property="updatetime"></result>
<result column="uid" property="uid"></result>
<result column="rcount" property="rcount"></result>
<result column="state" property="state"></result>
<!-- 一对一关系的实现 -->
<association property="userInfo"
resultMap="com.example.demo.mapper.UserMapper.BaseMap"
columnPrefix="u_">
</association>
</resultMap>
<select id="getAll2" resultMap="BaseMap">
select a.*,u.id u_id,u.username u_username,u.password u_password from articleinfo a left join userinfo u on
a.uid=u.id
</select>
以上使用 association标签,表示一对一的结果映射:

<resultMap id="BaseMap" type="com.example.demo.model.UserInfo">
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="photo" property="photo"></result>
<result column="createtime" property="createtime"></result>
<result column="updatetime" property="updatetime"></result>
<result column="state" property="state"></result>
<collection property="alist"
resultMap="com.example.demo.mapper.ArticleMapper.BaseMap"
columnPrefix="a_">
</collection>
</resultMap>
实体类:

一对多需要使用 collection 标签,用法和 association相同,如下所示:
<resultMap id="BaseMap" type="com.example.demo.model.UserInfo">
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="photo" property="photo"></result>
<result column="createtime" property="createtime"></result>
<result column="updatetime" property="updatetime"></result>
<result column="state" property="state"></result>
<!-- 一对多关系的实现 -->
<collection property="alist"
resultMap="com.example.demo.mapper.ArticleMapper.BaseMap"
columnPrefix="a_">
</collection>
</resultMap>
<select id="getAll2" resultMap="BaseMap">
select u.*,a.id a_id,a.title a_title,a.content a_content
from userinfo u
left join articleinfo a
on u.id=a.uid
</select>
动态 sql 是Mybatis的强大特性之一,能够完成不同条件下不同的 sql 拼接。
注册分为两种字段:必填字段和非必填字段,那如果在添加用户的时候有不确定的字段传入,程序应该如何实现呢?
这个时候就需要使用动态标签 < if> 来判断了,比如添加的时候性别 sex 为非必填字段,具体实现如下:
<!-- 用户添加(使用动态 sql if -->
<insert id="add3">
insert into userinfo(username,
<if test="photo!=null">
photo,
</if>
password)
values(#{username},
<if test="photo!=null">
#{photo},
</if>
#{password})
</insert>



之前的插入用户功能,只是有一个 sex 字段可能是选填项,如果有多个字段,一般考虑使用 < trim> 标签结合 < if> 标签,对多个字段都采取动态生成的方式。
trim 标签中有如下属性:
<!-- 用户添加(使用动态 sql trim + if -->
<insert id="add4">
insert into userinfo
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="username!=null">
username,
</if>
<if test="password!=null">
password,
</if>
<if test="photo!=null">
photo
</if>
</trim>
values
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="username!=null">
#{username},
</if>
<if test="password!=null">
#{password},
</if>
<if test="photo!=null">
#{photo}
</if>
</trim>
</insert>

在以上 sql 动态解析时,会将第一个 trim 部分做如下处理:
<!-- 根据名称或密码查询用户列表(使用动态 sql where) -->
<select id="getListByNameOrPwd" resultMap="BaseMap">
select * from userinfo
<where>
<if test="username!=null">
username=#{username} and
</if>
<if test="password!=null">
password=#{password}
</if>
</where>
</select>
情景一:当不传递任何参数时,传递的SQL如下:

情景二:传递前面一个参数

使用以下代码可以解决这个问题:
<!-- 根据用户名称进行模糊查询 -->
<select id="getUserByLikeName" resultType="com.example.demo.model.UserInfo">
select * from userinfo where username like concat('%',#{username},'%')
</select>
<!-- 根据名称或密码查询用户列表(使用动态 sql where) -->
<select id="getListByNameOrPwd" resultMap="BaseMap">
select * from userinfo
<where>
<if test="username!=null">
username=#{username}
</if>
<if test="password!=null">
and password=#{password}
</if>
</where>
</select>

情景三:传递后面一个参数
<!-- 根据用户名称进行模糊查询 -->
<select id="getUserByLikeName" resultType="com.example.demo.model.UserInfo">
select * from userinfo where username like concat('%',#{username},'%')
</select>
<!-- 根据名称或密码查询用户列表(使用动态 sql where) -->
<select id="getListByNameOrPwd" resultMap="BaseMap">
select * from userinfo
<where>
<if test="username!=null">
username=#{username}
</if>
<if test="password!=null">
and password=#{password}
</if>
</where>
</select>


也可以使用< trim>标签进行该写代码:
<!-- 根据名称或密码查询用户列表(使用动态 sql where) -->
<select id="getListByNameOrPwd" resultMap="BaseMap">
select * from userinfo
<trim prefix="where" prefixOverrides="and">
<if test="username!=null">
username=#{username}
</if>
<if test="password!=null">
and password=#{password}
</if>
</trim>
</select>
根据传入的用户对象属性来更新用户数据,可以使用< set>标签来指定动态内容。
<update id="updateById">
update userinfo
<set>
<if test="username!=null">
username=#{username},
</if>
<if test="password!=null">
password=#{password},
</if>
<if test="photo!=null">
photo=#{photo}
</if>
</set>
where id=#{id}
</update>
执行的SQL:

set标签已经自动将最后一个逗号删除掉
使用< trim>标签也可以实现< set>的功能:
<update id="updateById">
update userinfo
<trim prefix="set" suffixOverrides=",">
<if test="username!=null">
username=#{username},
</if>
<if test="password!=null">
password=#{password},
</if>
<if test="photo!=null">
photo=#{photo}
</if>
</trim>
where id=#{id}
</update>
对集合进行遍历时可以使用该标签。< foreach>标签有如下属性:
接口方法:
public int delByIds(List<Integer> ids);
删除SQL:
<delete id="delByIds">
delete from userinfo where id in
<foreach collection="ids" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</delete>