前言
在应用分层的学习时, 我们了解到web应用程序一般分为三层,即Controller, Service, Dao.
之前的案例中, 请求流程如下: 浏览器发起请求, 先请求Controller, Controller接受到请求后,调用Service进行业务逻辑处理, Service再调用Dao, 但是Dao层的数据是Mock的, 真实的数据是从数据库中读取.
我们学习MySQL数据库时, 已经学习了用JDBC操作数据库, 但是JDBC过于复杂. 因此我们可以使用MyBatis以更方便, 更快速的操作数据库.
什么是MyBatis
MyBatis 是一款优秀的持久层框架, 用于简化JDBC的开发.
持久层: 指的就是持久化操作的层, 通常指数据访问层(dao), 是用来操作数据库的.
MyBatis入门
MyBatis操作数据库的步骤:
1.准备工作(创建Springboot工程, 数据库表准备, 实体类)
2.引入MyBatis相关依赖.
3.编写SQL语句(注解/XML).
4.测试
准备工作
创建工程
创建springboot工程, 并导入mybatis的起步依赖, mysql的驱动包
Mybatis是一个持久层框架, 具体的数据存储和数据操作还是在MySQL中操作的, 所以需要添加MySQL驱动.
项目工程创建完成后, 自动在pom.xml文件中, 导入Mybatis依赖和MySQL驱动依赖.
版本会随着SpringBoot版本发生变化, 无需关注.
数据准备
创建用户表, 并创建对应的实体类User.
sql
-- 创建数据库
DROP DATABASE IF EXISTS mybatis_test;
CREATE DATABASE mybatis_test DEFAULT CHARACTER SET utf8mb4;
-- 使⽤数据数据
USE mybatis_test;
-- 创建表[⽤⼾表]
DROP TABLE IF EXISTS userinfo;
CREATE TABLE `userinfo` (
`id` INT ( 11 ) NOT NULL AUTO_INCREMENT,
`username` VARCHAR ( 127 ) NOT NULL,
`password` VARCHAR ( 127 ) NOT NULL,
`age` TINYINT ( 4 ) NOT NULL,
`gender` TINYINT ( 4 ) DEFAULT '0' COMMENT '1-男 2-⼥ 0-默认',
`phone` VARCHAR ( 15 ) DEFAULT NULL,
`delete_flag` TINYINT ( 4 ) DEFAULT 0 COMMENT '0-正常, 1-删除',
`create_time` DATETIME DEFAULT now(),
`update_time` DATETIME DEFAULT now(),
PRIMARY KEY ( `id` )
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4;
-- 添加⽤⼾信息
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'admin', 'admin', 18, 1, '18612340001' );
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'zhangsan', 'zhangsan', 18, 1, '18612340002' );
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'lisi', 'lisi', 18, 1, '18612340003' );
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'wangwu', 'wangwu', 18, 1, '18612340004' );
创建对应的实体类UserInfo
实体类的属性名与表中的字段名一一对应.
java
import lombok.Data;
import java.util.Date;
@Data
public class UserInfo {
private Integer id;
private String username;
private String password;
private Integer age;
private Integer gender;
private String phone;
private Integer deleteFlag;
private Date createTime;
private Date updateTime;
}
配置数据库连接字符串
Mybatis中要连接数据库, 需要数据库相关参数的配置.
MySQL驱动类
登录名
密码
数据库连接字符串
如果是application.yml文件, 配置内容如下:
java
# 数据库连接配置
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false
username: root
password: 550407
driver-class-name: com.mysql.cj.jdbc.Driver
写持久层代码
按如下格式创建:
java
import com.bit.mybatis.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface UserInfoMapper {
//查询所有用户
@Select("Select username, password, age, gender, phone from userinfo")
public List<UserInfo> queryAllUser();
}
MyBatis的持久层接口规范一般都叫XxxMapper
@Mapper注解: 表示的是MyBatis中的Mapper接口
程序运行时, 框架会自动生成接口的实现类对象(代理对象), 并交给Spring的IOC容器管理.
@Select注解: 代表的就是select查询, 也就是注解对应方法的具体实现内容.
单元测试
在创建出来的SpringBoot工程中, 在src的test目录下, 已经自动创建好了测试类, 可直接使用这个测试类来进行测试了.
java
@SpringBootTest
class MybatisApplicationTests {
@Autowired
private UserInfoMapper userInfoMapper;
@Test
void contextLoads() {
List<UserInfo> userInfoList = userInfoMapper.queryAllUser();
System.out.println(userInfoList);
}
}
测试类上添加了注解@SpringBootTest, 该测试类在运行时, 就会自动加载Spring的运行环境. 我们通过@Autowired这个注解, 注入我们要测试的类, 就可以开始进行测试了.
运行结果:
使用idea自动生成测试类
除此之外, 也可以使用idea自动生成测试类.
在需要测试的Mapper接口中, 右键 -> Generate -> Test
MyBatis的基础操作
上面我们学习了MyBatis的查询操作, 接下来我们来学习MyBatis的增删改操作.
在学习这些操作之前, 我们先来学习MyBatis日志的打印.
打印日志
在MyBatis我们可以借助日志, 查看到sql语句的执行, 传递的参数以及执行结果.
在配置文件中进行配置即可.
java
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
重新运行程序, 可以看到SQL的执行内容, 以及传递参数和执行结果.
参数传递
需求: 查找 id = 4的用户, 对应的SQL就是: select * from userinfo where id = 4;
java
@Select("select username, password, age, gender, phone from userinfo where id = 4")
UserInfo queryById();
但是这样的话, 只能查找id=4的数据, 所以SQL语句中的id值不能写成固定的数值, 需要转变为动态的数值.
解决方案: 在queryById方法中添加一个参数(id), 将方法中的参数, 传给SQL语句.
使用#{}的方式获取方法中的参数.
java
@Select("select username, password, age, gender, phone from userinfo where id = #{id}")
UserInfo queryById(Integer id);
如果mapper的接口方法形参只有一个普通类型的参数, #{...}里面的属性名可以随便写, 如:#{id}, #{value}. 建议和参数名保持一致.
添加测试用例:
java
@Test
void queryById() {
UserInfo userInfo = userInfoMapper.queryById(4);
System.out.println(userInfo);
}
运行结果:
也可以通过@Param, 设置参数的别名, 如果使用@Param设置别名, #{...}里面的属性名必须和@Param设置的一样.
java
@Select("select username, password, age, gender, phone from userinfo where id = #{userid}")
UserInfo queryById(@Param("userid") Integer id);
增(Insert)
SQL语句:
sql
insert into userinfo (username, `password`, age, gender, phone) values
("zhaoliu","zhaoliu",19,1,"18700001234")
把SQL中的常量转换为动态的参数:
Mapper接口:
java
@Insert("insert into userinfo (username, password, age, gender, phone) values (#{username}, #{password}, #{age}, #{gender}, #{phone})")
Integer insert(UserInfo userInfo);
直接使用UserInfo 对象来获取参数.
测试代码:
java
@Test
void insert() {
UserInfo userInfo = new UserInfo();
userInfo.setUsername("zhangsan");
userInfo.setPassword("123456");
userInfo.setAge(18);
userInfo.setGender(1);
userInfo.setPhone("129830218213");
userInfoMapper.insert(userInfo);
}
运行后, 观察数据库执行结果.
返回主键
Insert语句默认返回的是受影响的行数.
但有些情况下, 数据插入之后, 还需要有后续的关联操作, 需要获取到新插入数据的id.
比如订单系统
当我们下完订单以后, 需要通知物流系统, 库存系统, 结算系统等, 这时候就需要拿到订单ID.
要想要拿到自增id, 需要在Mapper接口的方法上添加一个Options的注解.
java
@Options(useGeneratedKeys = true, keyProperty = "id")
useGeneratedKey: 这会令MyBatis使用JDBC的getGeneratedKeys方法来取出数据库内部生成的主键(比如: 像MySQL和SQL Server这样的关系型数据库管理系统的自动递增字段), 默认值: false.
keyProperty: 指定能够唯一识别对象的属性, MyBatis会使用getGeneratedKeys的返回值或insert语句的selectKey子元素设置它的值, 默认值: 未设置.
测试数据:
java
@Test
void insert() {
UserInfo userInfo = new UserInfo();
userInfo.setUsername("zhaoliu");
userInfo.setPassword("3128937");
userInfo.setAge(22);
userInfo.setGender(0);
userInfo.setPhone("323830218213");
Integer count = userInfoMapper.insert(userInfo);
System.out.println("添加数据条数:" + count + ", 数据ID" + userInfo.getId());
}
注意: 设置useGeneratedKeys=true之后, 方法的返回值依然是受影响的行数, 自增id会设置在上述KeyProperty指定的属性中.
删(Delete)
SQL语句:
sql
delete from userinfo where id = 6
把SQL中的常量替换成动态的参数
Mapper接口.
java
@Delete("delete from userinfo where id = #{id}")
void delete(Integer id);
改(Update)
SQL语句:
sql
update userinfo set username="zhaoliu" where id = 5
Mapper接口
java
@Update("update userinfo set username = #{username} where id = #{id}")
void update(UserInfo userInfo);