一. Mybatis 是什么
二. 使用Mybatis 进行简单查询
2.1 创建数据库和表
-- 创建数据库
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);
2.2 添加 MyBatis 框架支持
2.2.1 老项目中添加 MyBatis
<!-- 添加 MyBatis 框架 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<!-- 添加 MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
<scope>runtime</scope>
</dependency>
也可以使用 EditStarters 插件
2.2.2 新项目中添加MyBatis
2.3 配置连接字符串和 Mybatis
2.3.1 配置连接字符串
在application.yml 中添加以下内容:
# 数据库连接配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/mycnblog?characterEncoding=utf8&useSSL=false
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
2.3.2 配置 Mybatis 中的 XML 路径
# 配置 mybatis xml 的⽂件路径,在 resources/mapper 创建所有表的 xml ⽂件
mybatis:
mapper-locations: classpath:mapper/**Mapper.xml
2.4 添加业务代码
2.4.1 添加实体类
用户实体类
import java.time.LocalDateTime;
import lombok.Data;
@Data
public class Userinfo {
private int id;
private String username;
private String password;
private String photo;// 头像
private LocalDateTime createtime;
private LocalDateTime updatetime;
private int state;
}
2.4.2 添加 mapper 接口
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface UserMapper {
public List<User> getAll();
}
2.4.3 添加 UserMapper.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="com.example.demo.mapper.UserMapper">
</mapper>
<?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">
<select id="getAll" resultType="com.example.demo.model.User">
select * from userinfo
</select>
</mapper>
· <mapper>标签:需要指定 namespace 属性,表示命名空间,值为 mapper 接⼝的全限定 名,包括全包名.类名。
2.4.4 添加 Service
服务层代码:
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
@Service
public class UserService {
@Resource
private UserMapper userMapper;
public List<User> getAll() {
return userMapper.getAll();
}
}
2.4.5 添加 Controller
控制层代码:
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private UserService userService;
@RequestMapping("/getall")
public List<User> getAll(){
return userService.getAll();
}
}
三. 增删改操作
<insert>标签:插入语句
<update>标签:修改语句
<delete>标签:删除语句
3.1 增加用户操作
controller 实现代码:
@RequestMapping(value = "/add",method = RequestMethod.POST)
public Integer add(@RequestBody User user){
return userService.getAdd(user);
}
Integer add(User user);
mapper.xml
<insert id="add">
insert into userinfo(username,password,photo,state)
values(#{username},#{password},#{photo},1)
</insert>
特殊添加:返回自增 id
默认情况下返回的是受影响的行数,如果想要返回自增id,实现如下:
controller:
@RequestMapping(value = "/add2", method = RequestMethod.POST)
public Integer add2(@RequestBody User user) {
userService.getAdd2(user);
return user.getId();
}
mapper 接口:
@Mapper
public interface UserMapper {
// 添加,返回⾃增id
void add2(User user);
}
mapper.xml 实现如下:
<!-- 返回⾃增id -->
<insert id="add2" useGeneratedKeys="true" keyProperty="id">
insert into userinfo(username,password,photo,state)
values(#{username},#{password},#{photo},1)
</insert>
1)useGeneratedKeys:这会令 MyBatis 使⽤ JDBC 的 getGeneratedKeys ⽅法来取出由数据库内部⽣成的主键(⽐如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的⾃动递增字段),默认值:false。
2)keyColumn:设置⽣成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第⼀列的时候,是必须设置的。如果⽣成列不⽌⼀个,可以⽤逗号分隔多个属性名称。
3)keyProperty:指定能够唯⼀识别对象的属性,MyBatis 会使⽤ getGeneratedKeys 的返回值或 insert 语句的 selectKey ⼦元素设置它的值,默认值:未设置(unset)。如果⽣成列不⽌⼀个,可以⽤逗号分隔多个属性名称。
3.2 修改用户操作
controller:
@RequestMapping("/update")
public Integer update(Integer id, String name) {
return userService.update(id, name);
}
mapper.xml 实现代码:
<update id="update">
update userinfo set username=#{name} where id=#{id}
</update>
3.3 删除用户操作
<delete id="delById">
delete from userinfo where id=#{id}
</delete>
四. 查询操作
4.1 单表查询
根据用户 id 查询用户信息。
Controller 实现代码:
@RequestMapping("/getuser")
public Userinfo getUserById(Integer id) {
return userService.getUserById(id);
}
Mapper.xml 实现代码:
<select id="getUserById" resultType="com.example.demo.entity.Userinfo">
select * from userinfo where id = #{id}
</select>
4.1.1 参数占位符 #{} 和 ${}
1) #{}: 预编译处理
2) ${}: 字符直接替换
4.1.2 ${} 优点
<select id="getListByOrder" resultType="com.example.demo.entity.Userinfo">
select * from userinfo order by id ${order}
</select>
4.1.3 SQL 注入问题
当我们使用 ${} 实现登录的 mapper.xml 时:
<select id="login" resultType="com.example.demo.entity.Userinfo">
select * from userinfo where username='${username}' and password='${password}'
</select>
单元测试代码如下:
输入错误密码:“' or 1='1”
数据库中的admin用户
运行代码
我们发现使用错误的密码居然成功登录了,这无疑是一个巨大的漏洞
原因也不难发现:我们使用 ${} 来接收密码,使有心之人利用 ${} 的弊端改变了这条 sql 的语义
结论:1)用于查询的字段,尽量使用 #{} 预查询的方式
2)在不得不使用 ${} 时,一定要保证其中的参数是可穷举的,并且使用 if 进行判断,进 而确保没有 sql 注入的风险
4.1.4 like 查询
<select id="getListByName" resultType="com.example.demo.entity.Userinfo">
select * from userinfo where username like '%#{username}%'
</select>
<select id="getListByName" resultType="com.example.demo.entity.Userinfo">
select * from userinfo where username like concat('%',#{username},'%')
</select>
4.2 多表查询
4.2.1 返回类型:resultType
绝⼤数查询场景可以使⽤ resultType 进⾏返回,如下代码所示:
<select id="getUserById" resultType="com.example.demo.entity.Userinfo">
select * from userinfo where id = #{id}
</select>
4.2.2 返回字典映射:resultMap
resultMap 使用场景:
1)字段名称和程序中的属性名不同的情况,可使⽤ resultMap 配置映射;
![](https://i-blog.csdnimg.cn/blog_migrate/4c8b9c20139d3270751fe69298d7d945.png)
mapper.xml:
<resultMap id="BaseMap" type="com.example.demo.model.User">
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="password" property="pwd"></result>
</resultMap>
<select id="getUserById" resultMap="com.example.demo.mapper.UserMapper.BaseMap">
select * from userinfo where id=#{id}
</select>
4.2.3 多表查询
当我们需要提供一个书籍的 id 而查询到书籍的信息和作者的 username 时:
1)构建一个书籍信息的实体类对象:
import lombok.Data;
import java.io.Serializable;
@Data
public class Articleinfo implements Serializable {
private final long serializableId = 1L;
private int id;
private String title;
private String content;
private String createtime;
private String updatetime;
private int uid;
private int rcount;
private int state;
}
2)再构建一个需要提供给前端的实体类对象,其中包含另一个表的部分数据:
注意:这个实体类需要继承上一个实体类,并重写 toSpring 方法,lombook 不会执行父类的 toSpring 方法。
import com.example.demo.entity.Articleinfo;
import lombok.Data;
import java.io.Serializable;
@Data
public class ArticleinfoVO extends Articleinfo implements Serializable {
private final long serializableId = 1L;
private String username;
@Override
public String toString() {
return "ArticleinfoVO{" +
"username='" + username + '\'' +
"} " + super.toString();
}
}
mapper.xml 代码 :
<select id="getById" resultType="com.example.demo.entity.vo.ArticleinfoVO">
select a.*,u.username from articleinfo a
left join userinfo u on u.id=a.uid
where a.id=#{id}
</select>
单元测试:
@SpringBootTest
class ArticleMapperTest {
@Autowired
private ArticleMapper articleMapper;
@Test
void getById() {
ArticleinfoVO articleinfoVO = articleMapper.getById(1);
System.out.println(articleinfoVO);
}
}
代码执行结果:
五. 动态SQL使用
5.1 <if>标签
<insert id="insert" parameterType="org.example.model.User" useGeneratedKeys="true" keyProperty="id">
insert into user(
username,
password,
nickname,
<if test="sex != null">
sex,
</if>
birthday,
head
) values (
#{username},
#{password},
#{nickname},
<if test="sex != null">
#{sex},
</if>
#{birthday},
#{head}
)
</insert>
注意:text 中的 sex 是属性而非字段
5.2 <trim>标签
之前的插⼊⽤户功能,只是有⼀个 sex 字段可能是选填项,如果所有字段都是⾮必填项,就考虑使⽤
<insert id="insert" parameterType="org.example.model.User" useGeneratedKeys="true" keyProperty="id">
insert into user
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="username != null">
username,
</if>
<if test="password != null">
password,
</if>
<if test="nickname != null">
nickname,
</if>
<if test="sex != null">
sex,
</if>
<if test="birthday != null">
birthday,
</if>
<if test="head != null">
head,
</if>
<if test="createTime != null">
create_time,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="username != null">
#{username},
</if>
<if test="password != null">
#{password},
</if>
<if test="nickname != null">
#{nickname},
</if>
<if test="sex != null">
#{sex},
</if>
<if test="birthday != null">
#{birthday},
</if>
<if test="head != null">
#{head},
</if>
<if test="createTime != null">
#{createTime},
</if>
</trim>
</insert>
5.3 <where>标签
传⼊的⽤户对象,根据属性做 where 条件查询,⽤户对象中属性不为 null 的,都为查询条件。如 user.username 为 "a",则查询条件为 where username="a":
List<User> selectByCondition(User user);
<select id="selectByCondition" parameterType="org.example.model.User" resultMap="BaseResultMap">
select id, username, password, nickname, sex, birthday, head, create_time
from user
<where>
<if test="username != null">
and username=#{username}
</if>
<if test="password != null">
and password=#{password}
</if>
<if test="nickname != null">
and nickname=#{nickname}
</if>
<if test="sex != null">
and sex=#{sex}
</if>
<if test="birthday != null">
and birthday=#{birthday}
</if>
<if test="head != null">
and head=#{head}
</if>
<if test="createTime != null">
and create_time=#{createTime}
</if>
</where>
</select>
5.4 <set>标签
int updateById(User user);
<update id="updateById" parameterType="org.example.model.User">
update user
<set>
<if test="username != null">
username=#{username},
</if>
<if test="password != null">
password=#{password},
</if>
<if test="nickname != null">
nickname=#{nickname},
</if>
<if test="sex != null">
sex=#{sex},
</if>
<if test="birthday != null">
birthday=#{birthday},
</if>
<if test="head != null">
head=#{head},
</if>
<if test="createTime != null">
create_time=#{createTime},
</if>
</set>
where id=#{id}
</update>
5.5 <foreach> 标签
int deleteByIds(List<Integer> ids);
<delete id="deleteByIds">
delete from article
where id in
<foreach collection="list" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</delete>