本期简绍MyBatis相关内容~
目录
MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装。MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录。
因为对于后端开发来说,程序主要是由后端程序和数据库两部分来组成的。而这两个部分要进行通讯,就需要有连接数据库的工具,前面我们已经学过了JDBC,但正是因为JDBC相对于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 'default.png', 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);②将其放在mysql中:
①添加MyBatis相关依赖
(1)新项目添加MyBatis依赖
1.基于spring项目进行创建:
(a)
(b)
(c)
(d)
(2)老项目添加MyBatis:
我们在pom.xml中点击鼠标右键,然后如下图所示操作
②配置数据库连接字符串和MyBatis(保存的xml目录)
(1)配置数据库的连接信息
1.主配置文件(application.yml)
#生产环境的配置文件 spring: profiles: active: dev2.开发环境配置文件(application-dev.yml)
#开发环境的配置文件 #无论是生产环境和开发环境都要配置以下四项 # 配置数据库的连接 spring: datasource: url: jdbc:mysql://127.0.0.1:3306/mycnblog?characterEncoding=utf8 username: root # 用户名(默认是root) password: 123456 # 自己的密码 driver-class-name: com.mysql.cj.jdbc.Driver # mysql的驱动名称3.当前运行环境配置文件(application-prod.yml)
这里先不做配置
(2)配置MyBatis的xml保存路径
xml属于资源文件,放于resource目录下。因此我们在resource目录下新建一个mybatis目录文件。
代码:
# 配置当前运行的环境(配置文件) spring: profiles: active: dev # 使用开发环境的配置文件 # 配置 MyBatis 的 XML 的保存路径 # 在resource底下新建一个文件夹,用来放 XML mybatis: mapper-locations: classpath:mybatis/**Mapper.xml③使用Mybatis的操作模式操作数据库:
创建controller,service,model,mapper的package在com.example.demo的目录下,方便在其中创建对应的类
(1)在model里创建实体类UserInfo(这里最好和数据中的同名,这样便于看)代码如下:
//这是一个实体类,对象的名字最好和数据库表名保持一致,看起来清楚 package com.example.demo.model; import lombok.Data; import org.springframework.stereotype.Controller; @Data @Controller public class UserInfo { private int id; private String username; private String password; private String photo; private String createtime; private String updatetime; private String state; }(2)在mapper中创建UserMapper(这样写比较规范,因为会引入@Mapper,当然也可以不这么写)
代码如下:(即定义普通的接口)
package com.example.demo.mapper; import com.example.demo.model.UserInfo; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; //实现xml和interface之间的交互 @Mapper//从普通的接口变成了mybatis的interface;可以通过xml实现,而普通的xml是要通过java才能实现的 public interface UserMapper { //根据用户id来查询用户 //@param是参数在xml中叫id,相当于重命名,但是使用中,只能用修改后的名字 public UserInfo getUserById(@Param("id") Integer id); }(3)创建xml实现上面的接口
注意:我们在前面配置的时候,就写了xml的存放路径需要在mybatis的目录下,并且命名为**Mapper.xml(而在这里为了方便我们更好的知道这里的xml对应的是哪个接口,所以我们将其前面命名一致为UserMapper)
1.mybatis的固定xml格式:(每新建一个项目都会用到的,但是namepace需要根据自己的情况来填写)
"1.0" encoding="UTF-8"?> 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>注意:(在查询操作的时候,这两个参数都是需要的,查询语句,有结果集的时候,都要映射到Java的对象,映射方式有很多种,但是必须指定一种,这里我们指定的就是resultType)
我们写的时候,就是interface中写方法申明,在xml中写具体实现就OK了。
④对以上查询操作进行测试:
(我们利用service中的类调用mapper,而controller里的类去调用service)
(1)service相关代码:
package com.example.demo.service; import com.example.demo.mapper.UserMapper; import com.example.demo.model.UserInfo; import org.springframework.stereotype.Service; import javax.annotation.Resource; @Service public class UserService { @Resource private UserMapper userMapper;//因为是mybatis接口,所以可以用 public UserInfo getUserById(Integer id){ return userMapper.getUserById(id); } }(2)Controller相关代码:
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.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class UserController { @Autowired private UserService userService; @RequestMapping("/getUserById") public UserInfo getUserById(Integer id){ if (id==null)return null; return userService.getUserById(id); } }输出结果:
对应数据库中内容进行查看:
一致的,查询成功
⑤MyBatis的执行过程:
这也就是为什么我们在④中需要引入controller下的类,以及service下的类,已经他们返回的代码的意义所在。
注意:为了更容易的排查sql中的问题,我们在开发环境中application-dev.yml设置日志进行打印(这个也是固定的写法)
# 开启 MyBatis SQL 打印 logging: level: com: example: demo: debug mybatis: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl写后我们运行程序成功,就可以在控制台上看到:
①在Mapper接口中声明一个方法:(注意!!!Mapper接口中,必须得有@Mapper注解,否则是不能够与xml产生映射的)
② 在xml中执行查询操作:使用预处理符
#{}
或者替换符${}
来将程序传入的参数替换到sql语句中:③查询结果:
①什么是单元测试?
1、可以非常简单、直观、快速的测试某个功能是否正确。
2、使用单元测试可以帮我们在打包的时候,发现一些问题,因为在打包之前,所以的单元测试必须通过,否则不能打包成功。
3、使用单元测试,在测试功能的时候,可以不污染连接的数据库,也就是可以不对数据库进行任何改变的情况下,测试功能的正确性。
②为什么使用单元测试?因为其可以不污染数据库,因此我们可以在添加进去前进行测试
③如何创建单元测试?
(1)框架支持(在我们创建spring boot项目时会自动进行添加,所以这个地方不做过多说明)
(2)我们在需要测试方法的所在类中,右键点击Generate,Test,然后对测试的内容进行勾选。(下面我们以测试UserMapper类中的getUserById为例)
a.
b.
c.
(3)进入测试页面后,我们先选择添加配置单元测试类的注解:@SpringBootTest注解
(4)在测试方法上添加@Test注解
(5)注入Mapper对象,调用里面操作数据库的方法,进行单元测试。
a.直接输出结果方式进行测试:
(注意:在一个测试中是可以有多个测试方法,且每个测试方法都可以独立地进行启动)
输出结果如下:
b.通过断言来进行测试:(断言常用方法如下)
示例:(我们以assert不为null来进行判断,如果不为null,就会返回√,否则则表示测试用例不通过)
这就表示测试例成功。
失败显示如下:(很显然id=1的是不为null 的)
(6)使用@Transactional注解可以防止污染数据库
使用这个注解后,是不会修改数据库里的内容的,因此当我们进行增删查改的时候,可以先这样进行测试看到底有没有问题,再进行后续操作。这个在下面的操作中也会进行演示。
①在Mapper接口中声明一个方法:(注意!!!Mapper接口中,必须得有@Mapper注解,否则是不能够与xml产生映射的)
//根据id来修改用户名称,且返回值为影响的行数 public Integer updateName(@Param("id")Integer id,@Param("username")String name);② 在xml中执行查询操作:使用预处理符
#{}
或者替换符${}
来将程序传入的参数替换到sql语句中:
<update id="updataName"> update userinfo set username=#{username} where id=#{id} update>③测试代码:
@Test @Transactional void updateName() { int result = userMapper.updateName(1, "李四"); Assertions.assertSame(1,result); }测试结果:
我们可以发现测试是通过了的,但是由于我们加了@Transactional这个注解的话,数据库是不会进行实际的修改的,我们可以查一下:
④我们去掉测试代码中的 @Transactional,来看一下是否会更改数据库的值
仍然是通过了测试,这个时候,我们来查询一下数据库:
⑤我们也可以通过类似于上述查询的方式,在url中对数据库的内容进行修改:
在UserService,UserController中分别添加如下内容:
(我们返回的仍然是受影响的行数)
输出结果:
此时,我们查看数据库,看username是否已经被改成了lisi。
①声明方法:
//根据id删除某一个用户的信息,返回受影响行数 public Integer deleteUser(@Param("id")Integer id);②在xml中编写sql:
<delete id="deleteUser"> delete from userInfo where id=#{id}; delete>③测试代码:
@Test @Transactional void deleteUser(){ int result=userMapper.deleteUser(1); Assertions.assertSame(1,result); }④输出结果:
测试成功,因为本身这个数据表就只有一行数据,这里就不真正删除啦
增加的操作会相对复杂一点。我们声明方法中的形参传入一个UserInfo对象
①返回一个参数
(1)声明方法:
//插入一行数据,返回受影响的行数 public Integer add(UserInfo userInfo);(2)在xml中编写sql:
<insert id="add"> insert into userinfo(username, password, photo) values (#{username}, #{password},#{photo}); insert>(3)测试代码:
@Test void add(){ UserInfo userInfo=new UserInfo(); userInfo.setId(2); userInfo.setUsername("张三"); userInfo.setPhoto("abc.png"); userInfo.setPassword("123456"); int result=userMapper.add(userInfo); Assertions.assertSame(1,result); }(4)输出结果:
测试成功,此时只返回了一个参数
我们来查看该表现在的内容:
②返回多个参数:
比如返回受影响的行数和自增id的值,这个时候我们就需要对xml的
insert
标签进行配置。(1)声明:
public Integer addId(UserInfo userInfo);
(2)xml中:(注意!!!insert标签中还需要设置useGeneratedKeys="true"表示为自增主键,keyProperty="id"表示哪一个是自增主键)
<insert id="addId" useGeneratedKeys="true" keyProperty="id" keyColumn="id"> insert into userinfo(username, password, photo) values (#{username}, #{password},#{photo}); insert>(3)测试代码:
@Test void addId() { UserInfo userInfo = new UserInfo(); userInfo.setUsername("王五"); userInfo.setPassword("123456"); userInfo.setPhoto("default.png"); System.out.println("插入之前的id=" + userInfo.getId()); int result = userMapper.addId(userInfo); System.out.println("插入之后的id=" + userInfo.getId()); Assertions.assertSame(1,result); }(4)输出结果:
测试成功
查看现在的数据表:
最后尝试通过名字来改id,上面我们看到张三的id为3,我们来试一试能不能改为2:
①声明方法:
public Integer updateidByname(Integer id,String username);
②在xml中修改:
<update id="updateidByname"> update userInfo set id=#{id} where username=#{username}; update>③测试代码:
@Test void updateidByname(){ int result = userMapper.updateidByname(2, "张三"); Assertions.assertSame(1,result); }④输出结果:
测试成功
本期MyBatis到此结束,这一期的内容可以支撑我们做SSM项目了,下期,我们将延伸一下。