MyBatis大大简化了JDBC的代码,内置连接池帮助创建和维护连接,实现对象和数据库表之间的转换。
一 . MyBatis 是什么 ?
1.1 MyBatis 的定义
MyBatis 是⼀款优秀的持久层框架 ,
持久层框架 : 之前的数据是未进行持久化的 , 存放在内存中的 , 现在我们把它保存下来 , 这就叫做持久化 .
它支持自定义 SQL、存储过程以及高级映射 .
MyBatis 去除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
通过 MyBatis 可以简单的通过 XML 或者注解来映射原始类型、接口 , 然后来为数据库中添加和查询记录
简单来说 , MyBatis 就是实现数据库的操作的 , 无非就是 CRUD , 他替换了 JDBC 的方式
MyBatis 是基于 JDBC 的 , 就像 Spring MVC 是基于 Servlet 一样 , 都是进行了封装 .
我们常说的 SSM , 就是这三种技术的统称 : Spring Boot、Spring MVC、MyBatis
MyBatis 的官网 : https://mybatis.org/mybatis-3/zh/index.html
1.2 为什么要使用 MyBatis ?
对于后端程序来说 , 常见的流程如下 :
我们已经学习过 JDBC , 为什么还要学 MyBatis ?
之前学习 JDBC 分九大部 :
创建数据库连接池 : DataSource
通过 DataSource 获取数据库连接 Connection
编写要执行带占位符 (?) 的 SQL 语句
通过 Connection 及 SQL 创建操作命令对象 Statement
替换占位符:指定要替换的数据库字段类型,占位符索引及要替换的值
使⽤ Statement 执行 SQL 语句
查询操作:返回结果集 ResultSet,更新操作:返回更新的数量
处理结果集
释放资源
这些代码基本和我们的业务没多大关系 , 属于功能性代码 , 而且操作及其麻烦 , 所以我们的 MyBatis 就出现了 , MyBatis 大大简化了我们 JDBC 的代码 , 而且之前学习 JDBC 还需要考虑资源释放问题 , 在 MyBatis 里面就不需要考虑了 , 因为 MyBatis 里面内置了连接池 , 它会帮助我们去创建和维护连接 , 以及创建资源 .
1.3 学习什么东西 ?
配置 MyBatis 开发环境
使用 MyBatis 模式和语法操作数据库
1.4 MyBatis 和数据库有什么关系 ?
我们目前的简单一点的程序基本就分为三个部分 : 一部分是应用程序 , 一部分是数据库 , 中间就是通过应用程序想要调用数据库 , 要通过 MyBatis .
MyBatis 是一个持久层框架 , 也是一个 ORM 框架 , ORM (Object Relational Mapping) 叫做对象关系映射 , 在面向对象编程思想中 , 可以理解为一张表就是一个对象 .
在 MyBatis 里面 , 就可以通过面向对象(OOP) 的思想 , 来实现增删改查
在 JDBC 里面 , 更像是面向过程编程 , 这一步写什么 , 那一步写什么 , 按照顺序一步一步去写 .
那么在面向对象编程语言当中 , 将关系型数据库中的数据与对象建立起映射关系 , 进而自动的完成数据与对象的
互相转换:
将输入数据(即传入对象)+SQL 映射成原生 SQL
将结果集映射为返回对象,即输出对象
ORM 就把数据库映射为对象:
数据库表(table)–> 类(class)
记录(record,行数据)–> 对象(object)
字段(field) --> 对象的属性(attribute)
⼀般的 ORM 框架,会将数据库模型的每张表都映射为⼀个 Java 类。
也就是说使用 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 -- 状态信息:默认为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);
我们先使用一张表即可
把这段 SQL 复制到 MySQL 里面
-- 创建数据库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 -- 状态信息:默认为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 查询插入的对象并且打印出来即可
2.2 新建项目
接下来测试项目是否能正常运行
接下来完成第三步 , 项目就可以正常启动了
2.3 设置数据库和 MyBatis 的配置
配置数据库的连接信息(连接哪台数据库)
在我们的 application.properties / application.yml 里面进行配置
spring: datasource: url: jdbc:mysql://127.0.0.1:3306/mycnblog?characterEncoding=utf8&useSSL=false username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver
如果使用 MySQL 是 5.x 之前的版本 , driver-class-name 就使用 “com.mysql.jdbc.Driver”,如果是大于 5.x 的版本 , 那么就使用 “com.mysql.cj.jdbc.Driver”
配置 MyBatis XML 文件存放位置和命名规则
# 配置 mybatis xml 的⽂件路径,在 resources/mapper 创建所有表的 xml ⽂件mybatis: mapper-locations: classpath:mapper/**Mapper.xml# 路由存放路径: 当前路径底下新建一个 mapper,然后里面所有的名字后缀为 Mapper.xml 的都是 MyBatis 的 xml 文件
然后在 resources 文件夹下面创建一个 mapper 路径就行
后面所有的数据库连接就都放在 mapper 文件夹下
xxxMapper.xml 里面存储的都是 SQL 操作语句
那么我们为什么要配置 MyBatis 的 xml ?
这就需要谈到我们的 MyBatis 组成了
MyBatis 组成
MyBatis 是由两部分组成的
接口 : 声明当前类的所有方法(给别人调用)
比如说我们现在有一张用户表 , 那我们就会创建一个 MyBatis 的接口 , 他里面会声明操作表的各种方法 , 比如说增删改查
但是接口只能实现声明 , 并不能具体实现 , 那么我们的具体实现就是各种 SQL 语句 , 那放在哪里呢 ? 就放在许多的 xxx.xml 文件里面
xxx.xml : 和接口是一一对应的 , 一个类会有一个接口和一个对应实现操作的 xml 文件(存放 SQL)
SSM 项目常用配置
spring: datasource: url: jdbc:mysql://127.0.0.1:3306/mycnblog?characterEncoding=utf8&useSSL=false username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver# 配置 mybatis xml 的⽂件路径,在 resources/mapper 创建所有表的 xml ⽂件mybatis: mapper-locations: classpath:mybatis/**Mapper.xml# 路由存放路径: 当前路径底下新建一个 mapper,然后里面所有的名字最后为 Mapper.xml 的都是 MyBatis 的 xml 文件 configuration: # 配置打印 MyBatis 执行的 SQL log-impl: org.apache.ibatis.logging.stdout.StdOutImpl# 配置打印 MyBatis 执行的 SQLlogging: level: com: example: demo: debug
之前我们的程序并未启动 , 现在我们就可以启动了
🔍 那为什么我们的 MyBatis 要这么设计 ?
✅ MyBatis 要实现增删改查 , 他就一定要去写 SQL , 写 SQL 的话就有两种选择 :
把他写到 Java 的类里面 : 但是这种方法并不太行 , 有些 SQL 语句很长很复杂 , 那我们怎么存储 SQL 语句 ? 用双引号当成字符串引起来 ? 这显然是不太行的 , 因为 SQL 里面可能会存在参数、拼接或者关键字 , 用字符串包裹起来后 , 参数等信息获取就会十分困难
把它放到 xml 里面 : 程序的请求先经过 Controller , Controller 验证参数没问题后 , 会调用 Service 服务层 , Service 会根据当前服务来决定调用几个接口 , 调用这些接口的时候要进行数据持久化 , 但是我们数据持久化具体的实现是在 xml 里面的 , 那我的程序 (Service) 就需要通过接口去调用 xml
三 . 实现用户信息的查询
3.1 新建一个对象类
新建一个 model 包
在 model 包底下新建一个类
然后添加一个 @Data 注解 , 这样就不需要再写 Getter Setter 以及 toString 方法了
package com.example.demo.model;import lombok.Data;@Datapublic class UserInfo {}
接下来我们继续编写 , 一定要注意的是 : 我们的属性名要和数据库里面的字段名保持一致
package com.example.demo.model;import lombok.Data;import java.util.Date;/** * 一个普通的实体类 * 用来给 MyBatis 做数据表(UserInfo)的映射的 * 保证类属性名称和数据表的字段名完全一致 * 顺序无所谓,类名可以不一样 */@Datapublic class UserInfo { private int id; private String username; private String password; private String photo; private Date createtime; private Date updatetime; private int state; }
那我们的 MyBatis 是由接口跟 XML 组成的 , 所以我们先来创建接口
3.2 创建一个接口
我们新建一个 mapper 包代表数据持久层
接下来在 mapper 包下面新建接口
接下来 , 在接口里面去写函数声明
package com.example.demo.mapper;import com.example.demo.model.UserInfo;import java.util.List;public interface UserMapper { // 查询所有用户信息 , 用 List 接收 public List<UserInfo> getAll();}
那 MyBatis 的接口能是普通的接口吗 ? 那肯定不是 , 我们还需要加一个注解 : @Mapper
package com.example.demo.mapper;import com.example.demo.model.UserInfo;import org.apache.ibatis.annotations.Mapper;import java.util.List;// MyBatis 接口,此注解一定不能省略@Mapperpublic interface UserMapper { // 查询所有用户信息 , 用 List 接收 public List<UserInfo> getAll();}
3.3 创建与上面接口对应的 xml 文件(实现类中的所有方法)
为了跟上面的 java 文件夹里面的 mapper 区分开 , 我们把 application.yml 里面的 mapper 文件夹改成 mybatis
那我们就在 resources 底下新建一个 mybatis 文件夹
接下来 , 我们去写一个 xxx.mapper , 去实现 UserMapper 接口里面的方法
接下来 , 我们把这段代码复制到 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=""> </mapper>
这段代码的意思就是将接口和 xxx.xml 关联上了 , 但是 UserMapper.xml 还没实现 UserMapper 接口里面的方法 , 所以接下来我们就去实现 getAll 的方法
<?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.UserInfo"> select * from userInfo </select></mapper>
其中 , id 就是要实现的方法名 , resultType 就是要实现的方法的返回值当中最小单位的路径
在 标签内编写 SQL 语句(可不带分号)
接下来 , 我们就去测试目前代码是否有问题 , 我们就需要学习单元测试 .
番外2 : Spring Boot 单元测试
3.4 常见错误
实体类当中的属性名与数据库当中的字段名不一致
我们数据库当中的字段名是 username
实体当中 , 我们的属性名改成 name
用单元测试运行一下
xxx.xml 里面的 id 错误
再进行单元测试呢 ?
就会变得红彤彤的了
xxx.xml 里面的 resultType 删除掉
运行一下单元测试
又是红彤彤的
四 . 根据用户 ID 查询用户信息
首先 , 我们还是在接口里面声明方法
package com.example.demo.mapper;import com.example.demo.model.UserInfo;import org.apache.ibatis.annotations.Mapper;import org.apache.ibatis.annotations.Param;import java.util.List;// MyBatis 接口,此注解一定不能省略@Mapperpublic interface UserMapper { // 根据用户 ID 查询用户信息 // 需要添加注解 @Param("") // 括号里面的参数名字随便起 // 这个注解的意思是:拿到uid的参数,把他赋值到id上 public UserInfo getUserById(@Param("uid")Integer id);}
然后我们去写 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"> <select id="getUserById" resultType="com.example.demo.model.UserInfo"> select * from userInfo where id = 1 </select></mapper>
但是要注意这里
在 MyBatis 里面 , 传参使用的是 : #{}
其实我们也可以这样思考 , 需要进行输入参数的时候 , 就用 #{} 替换
这样的话 , 我们的查询语句就变成了
<?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="getUserById" resultType="com.example.demo.model.UserInfo"> select * from userInfo where id = #{id} </select></mapper>
我们单元测试来测试一下
然后编写单元测试代码
package com.example.demo.mapper;import com.example.demo.model.UserInfo;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import java.util.List;import static org.junit.jupiter.api.Assertions.*;@SpringBootTest // 当前测试的上下文环境为 Spring Bootclass UserMapperTest { // 要测试谁 , 就注入谁 @Autowired private UserMapper userMapper; @Test void getUserById() { UserInfo userInfo = userMapper.getUserById(1); System.out.println(userInfo); }}
运行之后报错了 , 一般单元测试出错误了 , 基本就是 xxx.xml 的错
那是 UserMapper.xml 里面哪里出现了错误呢 ?
如果你要是记不住 #{} 里面到底填什么 , 那你就索性让 @Param(“”) 里面的参数与后面紧跟着的参数一致即可
再次运行单元测试
那我们目前传过去的 id 是 1 , 那传 2 呢 ?
传 2 的话就没找到 , 为 null
五 . 增、删、改操作
5.1 添加功能
添加用户操作有两种情况 :
默认情况下返回被影响的行数
少数情况下需要返回对应的自增主键的 ID (添加成功的用户的 ID)
那我们就来实现添加用户操作吧 .
我们再继续构建一组 SQL 数据
-- 创建文章表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';
然后我们要操作 文章表 , 就需要有一个文章的实体类 , 起到一个数据载体的作用
然后填入相关字段
package com.example.demo.model;import lombok.Data;import java.util.Date;/** * 文章的实体类 */@Datapublic class ArticleInfo { private int id; private String title; private String content; private Date createtime; private Date updatetime; private int uid; private int rcount;//访问量 private int state;//状态}
因为我们是新一波的操作 , 所以接口我们也新建一个
返回被影响的行数
package com.example.demo.mapper;import com.example.demo.model.ArticleInfo;import org.apache.ibatis.annotations.Mapper;import org.apache.ibatis.annotations.Param;// 记得添加 @Mapper 注解@Mapperpublic interface ArticleInfoMapper { // 返回值设置为int:返回受影响的行数 // 记得添加 @Param("") 注解(其实不添加也可以,但是防止线上环境出问题) // 传的是对应要插入的用户对象 // 为了避免错误,我们前后的参数保持一致,都叫做articleInfo public int add(@Param("articleInfo") ArticleInfo articleInfo);}
接下来 , 我们去新建一个 ArticleInfoMapper.xml
他的前面叫什么无所谓 , 后缀必须是 Mapper.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=""> </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.ArticleInfoMapper"> </mapper>
接下来填写 SQL 语句
<?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.ArticleInfoMapper"> <!-- id 就是方法名 --> <!-- insert 不用写 resultType , 因为他默认就是返回受影响的行数 --> <insert id="add"> <!-- values 后面的参数直接写 #{要插入的属性名} 属性名!划重点,不是数据库里面的字段名 --> insert into articleinfo(title,content,uid) values (#{title},#{content},#{uid}) </insert></mapper>
注意一点 : 我们这里的 #{title},#{content},#{uid} 中 , {} 里面的值不是数据库的字段名 , 而是对应的实体类当中的属性名
我们可以试验一下 , 先把实体类当中的 title 改成 t , 虽然这样的话查询的时候肯定是没有对应的字段的 , 但是插入的话不受影响
我们可以通过单元测试来查看效果
接下来编写单元测试的代码
package com.example.demo.mapper;import com.example.demo.model.ArticleInfo;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import static org.junit.jupiter.api.Assertions.*;// 要记得加这个注解,代表 Spring Boot 的单元测试@SpringBootTestclass ArticleInfoMapperTest { // 属性注入 @Autowired private ArticleInfoMapper articleInfoMapper; @Test void add() { ArticleInfo articleInfo = new ArticleInfo(); articleInfo.setT("你好 MyBatis"); articleInfo.setContent("终于见面了"); articleInfo.setUid(1); int result = articleInfoMapper.add(articleInfo); System.out.println("添加结果:" + result); }}
运行一下
那我们把 t 改回成 title ,还是报错
那这就能证明一件事了 : #{} 里面的参数跟属性名和字段名没有任何关系
我们实际上想要运行成功 , 需要把 ArticleInfoMapper 当中的 @Param(“”) 注解删掉
参数是属性的时候可以加上 @Param(“”) 注解
参数是对象的情况下 , 就不要加了
如果你非要添加这个注解的话 , 我们就需要在 ArticleInfoMapper.xml 里面通过对象.
的方式去设置
那我们再来看看我们的数据库里面添没添加成功
回到刚才的问题 : #{} 里面的参数与数据库中的字段名无关 , 拿的是实体类当中的属性名
但是大家切记 : 这种花活大家万万不可取
MyBatis ORM(对象关系模型) 框架 : 实现的功能是将数据库和程序中的类进行映射 , 进行映射就是使用程序中的对象名和数据库中的字段名做映射的 , 所以默认情况下 , 应该将类中的属性名和表中的字段名全部一一对应上 .
拿到自增的 ID
我想要得到添加的对象的 ID ,该怎么解决呢 ?
我们需要在 xxx.xml 里面加两个东西
开启自动生成 : useGeneratedKeys=“true”
把返回值设置到哪个属性上 : keyProperty=“id”
首先 , 重新写一个方法 , 来实现获得自增主键对应的 ID
package com.example.demo.mapper;import com.example.demo.model.ArticleInfo;import org.apache.ibatis.annotations.Mapper;import org.apache.ibatis.annotations.Param;// 记得添加 @Mapper 注解@Mapperpublic interface ArticleInfoMapper { // 返回值设置为int:返回受影响的行数 // 传的是对应要插入的用户对象 // 记得添加 @Param("") 注解(其实不添加也可以,但是防止线上环境出问题) // 为了避免错误,我们前后的参数保持一致,都叫做articleInfo public int add(ArticleInfo articleInfo); // 返回自增主键对应的 id public int addGetId(ArticleInfo articleInfo);}
这里面的参数保持不变即可
接下来 , 在 ArticleInfoMapper.xml 里面去实现对应的 SQL 语句
<?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.ArticleInfoMapper"> <!-- id 就是方法名 --> <!-- insert 不用写 resultType , 因为他默认就是返回受影响的行数 --> <insert id="add"> <!-- values 后面的参数直接写 #{要插入的属性名} 属性名!划重点,不是数据库里面的字段名 --> insert into articleinfo(title,content,uid) values (#{articleinfo.title},#{articleinfo.content},#{articleinfo.uid}) </insert> <!-- id 为方法名 --> <!-- 后面需要加两个标签 --> <!-- 1:开启自动生成 --> <!-- 2:把返回值设置到实体类里面的哪个属性上 --> <insert id="addGetId" useGeneratedKeys="true" keyProperty="id"> <!-- SQL 语句还是一样的 --> insert into articleinfo(title,content,uid) values (#{articleinfo.title},#{articleinfo.content},#{articleinfo.uid}) </insert></mapper>
如果数据库的字段和实体类当中的属性名不一样 , 还可以继续设置一个属性 : keyColumn
接下来 ,我们实现一下单元测试
package com.example.demo.mapper;import com.example.demo.model.ArticleInfo;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import static org.junit.jupiter.api.Assertions.*;// 要记得加这个注解,代表 Spring Boot 的单元测试@SpringBootTestclass ArticleInfoMapperTest { // 属性注入 @Autowired private ArticleInfoMapper articleInfoMapper; @Test void add() { ArticleInfo articleInfo = new ArticleInfo(); articleInfo.setTitle("你好 MyBatis2"); articleInfo.setContent("终于见面了2"); articleInfo.setUid(2); int result = articleInfoMapper.add(articleInfo); System.out.println("添加结果:" + result); } @Test void addGetId() { ArticleInfo articleInfo = new ArticleInfo(); articleInfo.setTitle("MyBatis 实现添加用户并返回自增主键的id"); articleInfo.setContent("设置 xml 中的 useGeneratedKeys=\"true\" keyProperty=\"id\""); articleInfo.setUid(1); int result = articleInfoMapper.addGetId(articleInfo);//返回受影响的行数 System.out.println("添加结果:" + result + " 自增id=" + articleInfo.getId());//这才是返回自增主键对应的id }}
5.2 删除功能
我们直接在之前的 ArticleInfoMapper 里面实现
删除文章 , 我们只需要传过去要删除的文章 id 即可
package com.example.demo.mapper;import com.example.demo.model.ArticleInfo;import org.apache.ibatis.annotations.Mapper;import org.apache.ibatis.annotations.Param;// 记得添加 @Mapper 注解@Mapperpublic interface ArticleInfoMapper { // 返回值设置为int:返回受影响的行数 // 传的是对应要插入的用户对象 // 记得添加 @Param("") 注解(其实不添加也可以,但是防止线上环境出问题) // 为了避免错误,我们前后的参数保持一致,都叫做articleInfo public int add(@Param("articleInfo") ArticleInfo articleInfo); // 返回自增主键对应的 id public int addGetId(@Param("articleInfo") ArticleInfo articleInfo); // 删除单条数据 // 返回值代表受影响的行数 public int delById(@Param("id") Integer id);}
声明我们已经实现了 ,接下来我们就需要去写实现了 , 继续在我们的 ArticleInfoMapper.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.ArticleInfoMapper"> <!-- id 就是方法名 --> <!-- insert 不用写 resultType , 因为他默认就是返回受影响的行数 --> <insert id="add"> <!-- values 后面的参数直接写 #{要插入的属性名} 属性名!划重点,不是数据库里面的字段名 --> insert into articleinfo(title,content,uid) values (#{articleInfo.title},#{articleInfo.content},#{articleInfo.uid}) </insert> <!-- id 为方法名 --> <!-- 后面需要加两个标签 --> <!-- 1:开启自动生成 --> <!-- 2:把返回值设置到哪个属性上 --> <insert id="addGetId" useGeneratedKeys="true" keyProperty="id" keyColumn=""> <!-- SQL 语句还是一样的 --> insert into articleinfo(title,content,uid) values (#{articleInfo.title},#{articleInfo.content},#{articleInfo.uid}) </insert> <!-- id 为方法名 --> <delete id="delById"> delete from articleinfo where id = #{id} </delete></mapper>
接下来 , 我们来编写单元测试代码
package com.example.demo.mapper;import com.example.demo.model.ArticleInfo;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import static org.junit.jupiter.api.Assertions.*;// 要记得加这个注解,代表 Spring Boot 的单元测试@SpringBootTestclass ArticleInfoMapperTest { // 属性注入 @Autowired private ArticleInfoMapper articleInfoMapper; @Test void add() { ArticleInfo articleInfo = new ArticleInfo(); articleInfo.setTitle("你好 MyBatis2"); articleInfo.setContent("终于见面了2"); articleInfo.setUid(2); int result = articleInfoMapper.add(articleInfo); System.out.println("添加结果:" + result); } @Test void addGetId() { ArticleInfo articleInfo = new ArticleInfo(); articleInfo.setTitle("MyBatis 实现添加用户并返回自增主键的id"); articleInfo.setContent("设置 xml 中的 useGeneratedKeys=\"true\" keyProperty=\"id\""); articleInfo.setUid(1); int result = articleInfoMapper.addGetId(articleInfo);//返回受影响的行数 System.out.println("添加结果:" + result + " 自增id=" + articleInfo.getId());//这才是返回自增主键对应的id } @Test void delById() { int result = articleInfoMapper.delById(2); System.out.println("删除结果:" + result); }}
我们在删除之前先来查询一下结果
接下来 , 我们要删除 2 号文章
我们来看一下数据库 , 是否删除成功了
5.3 修改功能
我们还是在 ArticleInfoMapper 里面写方法声明
package com.example.demo.mapper;import com.example.demo.model.ArticleInfo;import org.apache.ibatis.annotations.Mapper;import org.apache.ibatis.annotations.Param;// 记得添加 @Mapper 注解@Mapperpublic interface ArticleInfoMapper { // 返回值设置为int:返回受影响的行数 // 传的是对应要插入的用户对象 // 记得添加 @Param("") 注解(其实不添加也可以,但是防止线上环境出问题) // 为了避免错误,我们前后的参数保持一致,都叫做articleInfo public int add(@Param("articleInfo") ArticleInfo articleInfo); // 返回自增主键对应的 id public int addGetId(@Param("articleInfo") ArticleInfo articleInfo); // 删除单条数据 // 返回值代表受影响的行数 public int delById(@Param("id") Integer id); // 修改标题 // 返回值代表受影响的行数 // 参数有两个 // 1. 主键id // 2. 最终要把标题改成什么 public int updateTitle(@Param("id") Integer id,@Param("title") String title);}
接下来去 ArticleInfoMapper.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.ArticleInfoMapper"> <!-- id 就是方法名 --> <!-- insert 不用写 resultType , 因为他默认就是返回受影响的行数 --> <insert id="add"> <!-- values 后面的参数直接写 #{要插入的属性名} 属性名!划重点,不是数据库里面的字段名 --> insert into articleinfo(title,content,uid) values (#{articleInfo.title},#{articleInfo.content},#{articleInfo.uid}) </insert> <!-- id 为方法名 --> <!-- 后面需要加两个标签 --> <!-- 1:开启自动生成 --> <!-- 2:把返回值设置到哪个属性上 --> <insert id="addGetId" useGeneratedKeys="true" keyProperty="id" keyColumn=""> <!-- SQL 语句还是一样的 --> insert into articleinfo(title,content,uid) values (#{articleInfo.title},#{articleInfo.content},#{articleInfo.uid}) </insert> <!-- id 为方法名 --> <delete id="delById"> delete from articleinfo where id = #{id} </delete> <update id="updateTitle"> update articleinfo set title = #{title} where id = #{id} </update></mapper>
接下来 , 填写单元测试代码
package com.example.demo.mapper;import com.example.demo.model.ArticleInfo;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import static org.junit.jupiter.api.Assertions.*;// 要记得加这个注解,代表 Spring Boot 的单元测试@SpringBootTestclass ArticleInfoMapperTest { // 属性注入 @Autowired private ArticleInfoMapper articleInfoMapper; @Test void add() { ArticleInfo articleInfo = new ArticleInfo(); articleInfo.setTitle("你好 MyBatis2"); articleInfo.setContent("终于见面了2"); articleInfo.setUid(2); int result = articleInfoMapper.add(articleInfo); System.out.println("添加结果:" + result); } @Test void addGetId() { ArticleInfo articleInfo = new ArticleInfo(); articleInfo.setTitle("MyBatis 实现添加用户并返回自增主键的id"); articleInfo.setContent("设置 xml 中的 useGeneratedKeys=\"true\" keyProperty=\"id\""); articleInfo.setUid(1); int result = articleInfoMapper.addGetId(articleInfo);//返回受影响的行数 System.out.println("添加结果:" + result + " 自增id=" + articleInfo.getId());//这才是返回自增主键对应的id } @Test void delById() { int result = articleInfoMapper.delById(3); System.out.println("删除结果:" + result); } @Test void updateTitle() { int result = articleInfoMapper.updateTitle(1,"Hello World"); System.out.println("修改结果:" + result); }}
在修改之前 , 我们来看一下数据
运行一下
我们回数据库看一眼
目前我们已经完成了数据库的增删改查 ,但是要想把他完善成一个真正的项目还不够呀 , 因为我们还没添加程序的入口 , 也就是 Controller
那么接下来 ,我们就逐步完善 ,让这个项目变成真正的项目
六 . 补充项目完整
目前为止 , 我们的数据持久层的代码已经写完了 , 接下来需要完成控制器层以及服务层
我们首先创建控制器层 - Controller
UserController.java
package com.example.demo.controller;import com.example.demo.model.UserInfo;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestController@RequestMapping("/user")public class UserController { // 用户的查询 @RequestMapping("/getall") public List<UserInfo> getUsers() { // 先让程序不报错 return null; }}
然后再编写 Service 层
package com.example.demo.service;import com.example.demo.mapper.UserMapper;import com.example.demo.model.UserInfo;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.util.List;@Servicepublic class UserService { // 属性注入 @Autowired private UserMapper userMapper; public List<UserInfo> getAll() { // 直接调用 usermapper 里面的getAll return userMapper.getAll(); }}
那我们回过头再来看 Controller , Controller 层调用 Service
package com.example.demo.controller;import com.example.demo.model.UserInfo;import com.example.demo.service.UserService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestController@RequestMapping("/user")public class UserController { @Autowired private UserService userService; // 用户的查询 @RequestMapping("/getall") public List<UserInfo> getUsers() { return userService.getAll(); }}
现在的过程是这样
那我运行一下 , 通过 Controller 能不能得到用户信息
那这样的话 , 我们就实现了正常的交互了文章来源:https://www.toymoban.com/news/detail-655516.html
到此为止 , MyBatis Part 1 就结束了 , MyBatis Part 2 很快就来喽~文章来源地址https://www.toymoban.com/news/detail-655516.html
到了这里,关于MyBatis 查询数据库、持久层框架简介的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!