MyBatis 是⼀款优秀的持久层框架,它⽀持⾃定义 SQL、存储过程以及⾼级映射。MyBatis 去除了⼏乎所有的 JDBC 代码以及设置参数和获取结果集的⼯作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接⼝和 Java POJO(Plain Old Java Objects,普通⽼式 Java 对象)为数据库中的记录。
简单来说 MyBatis 是更简单完成程序和数据库交互的⼯具,也就是更简单的操作和读取数据库⼯具。
对于后端开发来说,程序是由以下两个重要的部分组成的:
对于 JDBC 来说,整个操作⾮常的繁琐,我们不但要拼接每⼀个参数,⽽且还要按照模板代码的⽅式,⼀步步的操作数据库,并且在每次操作完,还要⼿动关闭连接等,⽽所有的这些操作步骤都需要在每个⽅法中重复书写。于是我们就想,那有没有⼀种⽅法,可以更简单、更⽅便的操作数据库呢?
答案是肯定的,这就是我们要学习 MyBatis 的真正原因,它可以帮助我们更⽅便、更快速的操作数据库。
MyBatis 学习分为两部分:
开始搭建 MyBatis 之前,我们先来看⼀下 MyBatis 在整个框架中的定位,框架交互流程图:
在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';
-- 添加⼀个⽤户信息
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);
新项目添加MyBatis
老项目添加MyBatis
1.下载插件
2.右击generate
3.搜索”MyBatis“添加
配置连接字符串
在配置文件中添加如下内容,这里在application.yml文件中添加:
# 数据库连接配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/mycnblog?characterEncoding=utf8&useSSL=false
username: root
password: 111111
driver-class-name: com.mysql.cj.jdbc.Driver
配置 MyBatis 中的 XML 路径
# 配置 mybatis xml 的⽂件路径,在 resources/mapper 创建所有表的 xml ⽂件
mybatis:
mapper-locations: classpath:mapper/**Mapper.xml
添加实体类
添加用户的实体类(属性需要和字段名一致):
@Data
public class User {
private Integer id;
private String username;
private String password;
private String photo;
private Date createtime;
private Date updatetime;
}
添加mapper接口
@Mapper
public interface userMapper {
//查询
public List<User> queryAll();
}
添加sql语句,实现接口
在配置文件的mapper文件夹中,定义UserMapper.xml 查询所有⽤户的具体实现 SQL:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybati
s.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mybatisdemo.mapper.UserMapper">
<select id="queryAll" resultType="com.example.mybatisdemo.model.User">
select * from userinfo
</select>
</mapper>
以下是对以上标签的说明:
测试一下
UserMapperTest.java代码:
@Slf4j
@SpringBootTest
class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
void queryAll() {
List<User> users = userMapper.queryAll();
log.info(users.toString());
}
}
如何传参
添加sql语句:根据id查询:通过 “#” 传参
<select id="queryById" resultType="com.example.mybatisdemo.model.User">
select * from userinfo where id=#{uid}
</select>
添加mapper接口:
//根据id查询
User queryById(@Param("uid") Integer id);
@Param是重命名
补充: 当只有一个参数时,可以不加注解,并且参数名称可以不一样,如下图所示:
方法声明:
//插入
Integer insert(User user);
添加sql语句:
<insert id="insert">
insert into userinfo(username,password,photo) values(#{username},#{password},#{photo})
</insert>
如果设置了param注解,就必须要使用param注解的命名:
方法声明:
Integer insert1(@Param("userinfo") User user);
SQL语句:
<insert id="insert1">
insert into userinfo(username,password,photo) values(#{userinfo.username},#{userinfo.password},#{userinfo.photo})
</insert>
特殊的添加:返回自增的id
默认情况下返回的是受影响的⾏号,如果想要返回⾃增 id,具体实现如下:
方法声明:
Integer insert1(@Param("userinfo") User user);
UserMapper.xml:
<insert id="insert1" useGeneratedKeys="true" keyProperty="id" >
insert into userinfo(username,password,photo) values(#{userinfo.username},#{userinfo.password},#{userinfo.photo})
</insert>
方法声明:
//修改
void update(User user);
UserMapper.xml:
<update id="update">
update userinfo set username=#{username},password=#{password} where id=#{id}
</update>
方法声明:
//删除
void delete(Integer id);
UserMapper.xml:
<delete id="delete">
delete from userinfo where id=#{id}
</delete>
预编译处理是指:MyBatis 在处理#{}时,会将 SQL 中的 #{} 替换为?号,使⽤ PreparedStatement的 set ⽅法来赋值。
直接替换:是MyBatis 在处理 ${} 时,就是把 ${} 替换成变量的值。
举个例子来看看这两者的区别:
- ⼀般航空公司乘机都是头等舱和经济舱分离的,头等舱的⼈先登机,登机完之后,封闭经济舱,然后再让经济舱的乘客登机,这样的好处是可以避免浑⽔摸⻥,经济舱的⼈混到头等舱的情况,这就相当于预处理,可以解决程序中不安全(越权处理)的问题
- ⽽直接替换的情况相当于,头等舱和经济舱不分离的情况,这样经济舱的乘客在通过安检之后可能越权摸到头等舱,这就相当于参数直接替换,它的问题是可能会带来越权查询和操作数据等问题。比如6.3的sql注入问题。
使用 #{} 进行排序查询
方法声明:
//排序
List<User>queryByOrder(String order);
使用 #{} 进行排序查询:
<select id="queryByOrder" resultType="com.example.mybatisdemo.model.User">
select * from userinfo order by #{order}
</select>
查询测试:
void queryByOrder() {
List<User>users=userMapper.queryByOrder("desc");
log.info(users.toString());
}
运行结果如下所示:
这是因为当使⽤ #{sort} 查询时,如果传递的值为 String 则会加单引号,就会导致 sql 错误。
使用 ${} 进行排序查询
排序时,只能使用 $ 。
使用 ${} 进行排序查询则查询成功:
<select id="queryByOrder" resultType="com.example.mybatisdemo.model.User">
select * from userinfo order by id ${order}
</select>
但是它有可能会引起sql注入问题。即用户输入的参数中,有可能带有恶意sql。
解决方法:可以在前端只能让用户进行点击,选择排序方式,参数由后端控制输入,后端在查询之前,对参数进行校验,只能传 desc 和 asc 两个值。
SQL注入(SQL lnjection)是发生在Web程序中数据库层的安全漏洞,是比较常用的网络攻击方式之一,他不是利用操作系统的BUG来实现攻击,而是针对程序员编写时的疏忽,通过SQL语句,实现无账号登录,甚至修改数据库。也就是说,SQL注入就是在用户输入的字符串中添加SQL语句,如果在设计不良的程序中忽略了检查,那么这些注入进去的SQL语句就会被数据库服务器误认为是正常的SQL语句而运行,攻击者就可以执行计划外的命令或者访问未授权的数据。
like 查询是一个模糊查询。使⽤ #{} 会报错,而这个又不能直接使用 ${},会有sql注入问题,可以考虑使⽤ mysql 的内置函数 concat() 来处理,实现代码如下:
concat ()方法用于连接字符串
方法声明:
List<User>queryByLike(String name);
UserMapper.xml:
<select id="queryByLike" resultType="com.example.mybatisdemo.model.User">
select * from userinfo where username like concat('%',#{name},'%')
</select>
在配置文件中添加如下文件即可:
# 配置打印 MyBatis 执行的 SQL
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
增、删、改返回影响的⾏数,那么在 mapper.xml 中是可以不设置返回的类型的,然⽽即使是最简单查询⽤户的名称也要设置返回的类型。
对于 select 查询标签来说⾄少需要两个属性:
绝⼤数查询场景可以使⽤ resultType 进⾏返回,如下代码所示:
<select id="queryAll" resultType="com.example.mybatisdemo.model.User">
select * from userinfo
</select>
它的优点是使⽤⽅便,直接定义到某个实体类即可。
resultMap 使⽤场景:
字段名称和程序中的属性名不同的情况
<select id="queryAll" resultType="com.example.mybatisdemo.model.User">
select * from userinfo
</select>
这个时候就可以使⽤ resultMap 了,resultMap 的使⽤如下:
<resultMap id="baseMap" type="com.example.mybatisdemo.model.User">
<id column="id" property="id"></id>
<result column="username" property="name"></result>
<result column="password" property="pwd"></result>
</resultMap>
<select id="queryAll" resultType="com.example.mybatisdemo.model.User">
select * from userinfo
</select>
前面创建了一个user表,这里再创建一个文章表。
创建数据库和表
-- 创建文章表
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';
-- 文章添加测试数据
insert into articleinfo(title,content,uid)
values('Java','Java正⽂',1);
添加实体类
@Data
public class ArticleInfo {
private Integer id;
private String title;
private String content;
private Date createtime;
private Date updatetime;
private Integer rcount;
private User user;
}
添加mapper接口