Java EE 突击 12 - MyBatis 查询数据库(1)

这个专栏给大家介绍一下 Java 家族的核心产品 - SSM 框架
JavaEE 进阶专栏

Java 语言能走到现在 , 仍然屹立不衰的原因 , 有一部分就是因为 SSM 框架的存在

接下来 , 博主会带大家了解一下 Spring、Spring Boot、Spring MVC、MyBatis 相关知识点

并且带领大家进行环境的配置 , 让大家真正用好框架、学懂框架

来上一篇文章复习一下吧
点击即可跳转到前置文章
在这里插入图片描述

一 . 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 ?

对于后端程序来说 , 常见的流程如下 :
image.png
我们已经学习过 JDBC , 为什么还要学 MyBatis ?
之前学习 JDBC 分九大部 :

  1. 创建数据库连接池 : DataSource
  2. 通过 DataSource 获取数据库连接 Connection
  3. 编写要执行带占位符 (?) 的 SQL 语句
  4. 通过 Connection 及 SQL 创建操作命令对象 Statement
  5. 替换占位符:指定要替换的数据库字段类型,占位符索引及要替换的值
  6. 使⽤ Statement 执行 SQL 语句
  7. 查询操作:返回结果集 ResultSet,更新操作:返回更新的数量
  8. 处理结果集
  9. 释放资源

这些代码基本和我们的业务没多大关系 , 属于功能性代码 , 而且操作及其麻烦 , 所以我们的 MyBatis 就出现了 , MyBatis 大大简化了我们 JDBC 的代码 , 而且之前学习 JDBC 还需要考虑资源释放问题 , 在 MyBatis 里面就不需要考虑了 , 因为 MyBatis 里面内置了连接池 , 它会帮助我们去创建和维护连接 , 以及创建资源 .

1.3 学习什么东西 ?

  1. 配置 MyBatis 开发环境
  2. 使用 MyBatis 模式和语法操作数据库

1.4 MyBatis 和数据库有什么关系 ?

我们目前的简单一点的程序基本就分为三个部分 : 一部分是应用程序 , 一部分是数据库 , 中间就是通过应用程序想要调用数据库 , 要通过 MyBatis .
MyBatis 是一个持久层框架 , 也是一个 ORM 框架 , ORM (Object Relational Mapping) 叫做对象关系映射 , 在面向对象编程思想中 , 可以理解为一张表就是一个对象 .
在 MyBatis 里面 , 就可以通过面向对象(OOP) 的思想 , 来实现增删改查
在 JDBC 里面 , 更像是面向过程编程 , 这一步写什么 , 那一步写什么 , 按照顺序一步一步去写 .
那么在面向对象编程语言当中 , 将关系型数据库中的数据与对象建立起映射关系 , 进而自动的完成数据与对象的
互相转换:

  1. 将输入数据(即传入对象)+SQL 映射成原生 SQL
  2. 将结果集映射为返回对象,即输出对象
    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 查询插入的对象并且打印出来即可
image.png

2.2 新建项目

image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
接下来测试项目是否能正常运行
image.png
接下来完成第三步 , 项目就可以正常启动了

2.3 设置数据库和 MyBatis 的配置

配置数据库的连接信息(连接哪台数据库)

在我们的 application.properties / application.yml 里面进行配置
image.png
image.png

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 路径就行
image.png
image.png
后面所有的数据库连接就都放在 mapper 文件夹下
image.png
xxxMapper.xml 里面存储的都是 SQL 操作语句
那么我们为什么要配置 MyBatis 的 xml ?
这就需要谈到我们的 MyBatis 组成了

MyBatis 组成

MyBatis 是由两部分组成的

  1. 接口 : 声明当前类的所有方法(给别人调用)

比如说我们现在有一张用户表 , 那我们就会创建一个 MyBatis 的接口 , 他里面会声明操作表的各种方法 , 比如说增删改查
但是接口只能实现声明 , 并不能具体实现 , 那么我们的具体实现就是各种 SQL 语句 , 那放在哪里呢 ? 就放在许多的 xxx.xml 文件里面

  1. xxx.xml : 和接口是一一对应的 , 一个类会有一个接口和一个对应实现操作的 xml 文件(存放 SQL)

image.png

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 执行的 SQL
logging:
  level:
    com:
      example:
        demo: debug

之前我们的程序并未启动 , 现在我们就可以启动了
image.png
🔍 那为什么我们的 MyBatis 要这么设计 ?
✅ MyBatis 要实现增删改查 , 他就一定要去写 SQL , 写 SQL 的话就有两种选择 :

  1. 把他写到 Java 的类里面 : 但是这种方法并不太行 , 有些 SQL 语句很长很复杂 , 那我们怎么存储 SQL 语句 ? 用双引号当成字符串引起来 ? 这显然是不太行的 , 因为 SQL 里面可能会存在参数、拼接或者关键字 , 用字符串包裹起来后 , 参数等信息获取就会十分困难
  2. 把它放到 xml 里面 : 程序的请求先经过 Controller , Controller 验证参数没问题后 , 会调用 Service 服务层 , Service 会根据当前服务来决定调用几个接口 , 调用这些接口的时候要进行数据持久化 , 但是我们数据持久化具体的实现是在 xml 里面的 , 那我的程序 (Service) 就需要通过接口去调用 xml

image.png

三 . 实现用户信息的查询

3.1 新建一个对象类

新建一个 model 包
image.png
image.png
在 model 包底下新建一个类
image.png
image.png
然后添加一个 @Data 注解 , 这样就不需要再写 Getter Setter 以及 toString 方法了

package com.example.demo.model;

import lombok.Data;

@Data
public class UserInfo {
}

接下来我们继续编写 , 一定要注意的是 : 我们的属性名要和数据库里面的字段名保持一致

package com.example.demo.model;

import lombok.Data;

import java.util.Date;

/**
 * 一个普通的实体类
 * 用来给 MyBatis 做数据表(UserInfo)的映射的
 * 保证类属性名称和数据表的字段名完全一致
 * 顺序无所谓,类名可以不一样
 */
@Data
public class UserInfo {
    private int id;
    private String username;
    private String password;
    private String photo;
    private Date createtime;
    private Date updatetime;
    private int state;
    
}

image.png


那我们的 MyBatis 是由接口跟 XML 组成的 , 所以我们先来创建接口

3.2 创建一个接口

我们新建一个 mapper 包代表数据持久层
image.png
image.png
接下来在 mapper 包下面新建接口
image.png
image.png
接下来 , 在接口里面去写函数声明

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
image.png

package com.example.demo.mapper;

import com.example.demo.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

// MyBatis 接口,此注解一定不能省略
@Mapper
public interface UserMapper {
    // 查询所有用户信息 , 用 List 接收
    public List<UserInfo> getAll();
}

3.3 创建与上面接口对应的 xml 文件(实现类中的所有方法)

image.png
为了跟上面的 java 文件夹里面的 mapper 区分开 , 我们把 application.yml 里面的 mapper 文件夹改成 mybatis
那我们就在 resources 底下新建一个 mybatis 文件夹
image.png
image.png
接下来 , 我们去写一个 xxx.mapper , 去实现 UserMapper 接口里面的方法
image.png
image.png
image.png
接下来 , 我们把这段代码复制到 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>

image.png
这段代码的意思就是将接口和 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 就是要实现的方法的返回值当中最小单位的路径
image.png
在 标签内编写 SQL 语句(可不带分号)
接下来 , 我们就去测试目前代码是否有问题 , 我们就需要学习单元测试 .
番外2 : Spring Boot 单元测试

3.4 常见错误

实体类当中的属性名与数据库当中的字段名不一致

我们数据库当中的字段名是 username
image.png
实体当中 , 我们的属性名改成 name
image.png
用单元测试运行一下
image.png

xxx.xml 里面的 id 错误

image.png
再进行单元测试呢 ?
就会变得红彤彤的了
image.png

xxx.xml 里面的 resultType 删除掉

image.png
运行一下单元测试
又是红彤彤的
image.png

四 . 根据用户 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 接口,此注解一定不能省略
@Mapper
public 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>

但是要注意这里
image.png
在 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>

我们单元测试来测试一下
image.png
image.png
image.png
image.png
然后编写单元测试代码

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 Boot
class UserMapperTest {

    // 要测试谁 , 就注入谁
    @Autowired
    private UserMapper userMapper;
    
    @Test
    void getUserById() {
        UserInfo userInfo = userMapper.getUserById(1);
        System.out.println(userInfo);
    }
}

运行之后报错了 , 一般单元测试出错误了 , 基本就是 xxx.xml 的错
image.png
那是 UserMapper.xml 里面哪里出现了错误呢 ?
image.png
如果你要是记不住 #{} 里面到底填什么 , 那你就索性让 @Param(“”) 里面的参数与后面紧跟着的参数一致即可
image.png
再次运行单元测试
image.png
那我们目前传过去的 id 是 1 , 那传 2 呢 ?
传 2 的话就没找到 , 为 null
image.png

五 . 增、删、改操作

5.1 添加功能

添加用户操作有两种情况 :

  1. 默认情况下返回被影响的行数
  2. 少数情况下需要返回对应的自增主键的 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';

image.png
然后我们要操作 文章表 , 就需要有一个文章的实体类 , 起到一个数据载体的作用
image.png
image.png
然后填入相关字段

package com.example.demo.model;

import lombok.Data;

import java.util.Date;

/**
 * 文章的实体类
 */
@Data
public 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;//状态
}

image.png
因为我们是新一波的操作 , 所以接口我们也新建一个

返回被影响的行数

image.png
image.png

package com.example.demo.mapper;

import com.example.demo.model.ArticleInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

// 记得添加 @Mapper 注解
@Mapper
public interface ArticleInfoMapper {
    // 返回值设置为int:返回受影响的行数
    // 记得添加 @Param("") 注解(其实不添加也可以,但是防止线上环境出问题)
    // 传的是对应要插入的用户对象
    // 为了避免错误,我们前后的参数保持一致,都叫做articleInfo
    public int add(@Param("articleInfo") ArticleInfo articleInfo);
}

接下来 , 我们去新建一个 ArticleInfoMapper.xml
他的前面叫什么无所谓 , 后缀必须是 Mapper.xml
image.png
image.png
还是先把我们的默认模板复制进去

<?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>

image.png
接下来填写 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} 中 , {} 里面的值不是数据库的字段名 , 而是对应的实体类当中的属性名
image.png
我们可以试验一下 , 先把实体类当中的 title 改成 t , 虽然这样的话查询的时候肯定是没有对应的字段的 , 但是插入的话不受影响
image.png
我们可以通过单元测试来查看效果
image.png
image.png
image.png
接下来编写单元测试的代码

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 的单元测试
@SpringBootTest
class 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);
    }
}

运行一下
image.png
那我们把 t 改回成 title ,还是报错
image.png
那这就能证明一件事了 : #{} 里面的参数跟属性名和字段名没有任何关系
我们实际上想要运行成功 , 需要把 ArticleInfoMapper 当中的 @Param(“”) 注解删掉

参数是属性的时候可以加上 @Param(“”) 注解
参数是对象的情况下 , 就不要加了
如果你非要添加这个注解的话 , 我们就需要在 ArticleInfoMapper.xml 里面通过 对象.的方式去设置
image.png

image.png
image.png
那我们再来看看我们的数据库里面添没添加成功
image.png
回到刚才的问题 : #{} 里面的参数与数据库中的字段名无关 , 拿的是实体类当中的属性名
image.png
image.png
但是大家切记 : 这种花活大家万万不可取
MyBatis ORM(对象关系模型) 框架 : 实现的功能是将数据库和程序中的类进行映射 , 进行映射就是使用程序中的对象名和数据库中的字段名做映射的 , 所以默认情况下 , 应该将类中的属性名和表中的字段名全部一一对应上 .

拿到自增的 ID

我想要得到添加的对象的 ID ,该怎么解决呢 ?
我们需要在 xxx.xml 里面加两个东西

  1. 开启自动生成 : useGeneratedKeys=“true”
  2. 把返回值设置到哪个属性上 : 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 注解
@Mapper
public 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

接下来 ,我们实现一下单元测试
image.png
image.png
image.png

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 的单元测试
@SpringBootTest
class 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
    }
}

image.png

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 注解
@Mapper
public 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>

接下来 , 我们来编写单元测试代码
image.png
image.png
image.png

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 的单元测试
@SpringBootTest
class 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);
    }
}

我们在删除之前先来查询一下结果
image.png
接下来 , 我们要删除 2 号文章
image.png
我们来看一下数据库 , 是否删除成功了
image.png

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 注解
@Mapper
public 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>

接下来 , 填写单元测试代码
image.png
image.png
image.png

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 的单元测试
@SpringBootTest
class 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);
    }
}

在修改之前 , 我们来看一下数据
image.png
运行一下
image.png
我们回数据库看一眼
image.png


目前我们已经完成了数据库的增删改查 ,但是要想把他完善成一个真正的项目还不够呀 , 因为我们还没添加程序的入口 , 也就是 Controller

那么接下来 ,我们就逐步完善 ,让这个项目变成真正的项目

六 . 补充项目完整


目前为止 , 我们的数据持久层的代码已经写完了 , 接下来需要完成控制器层以及服务层
我们首先创建控制器层 - Controller
image.png
image.png
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 层
image.png
image.png

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;

@Service
public 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();
    }
}

现在的过程是这样
image.png
那我运行一下 , 通过 Controller 能不能得到用户信息
image.png
image.png
那这样的话 , 我们就实现了正常的交互了


到此为止 , MyBatis Part 1 就结束了 , MyBatis Part 2 很快就来喽~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

加勒比海涛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值