目录
我们都知道,一个好的程序必然是由前后端组成的,那么其前端与后端的交互如何才能获取数据呢? 那就需要后端从数据库提取数据返回给前端了,我们之前学习的MySQL数据库有JDBC可以操作,那么在JDBC中我们需要一些类比如:
1. 创建数据库连接池 DataSource2. 通过 DataSource 获取数据库连接 Connection3. 编写要执⾏带 ? 占位符的 SQL 语句4. 通过 Connection 及 SQL 创建操作命令对象 Statement5. 替换占位符:指定要替换的数据库字段类型,占位符索引及要替换的值6. 使⽤ Statement 执⾏ SQL 语句7. 查询操作:返回结果集 ResultSet,更新操作:返回更新的数量8. 处理结果集9. 释放资源
其实这样是非常麻烦的,假如我们要连接的数据库众多,那么每一次交互都要来上这么一套,后端程序员简直要疯...........所以MyBatis应运而生,接下来跟着小玉一起来学习吧~~~
1.如何学习MyBatis?
● 配置 MyBatis 开发环境;● 使⽤ MyBatis 模式和语法操作数据库。
● 数据库表(table)--> 类(class)● 记录(record,⾏数据)--> 对象(object)● 字段(field) --> 对象的属性(attribute)
2.如何使用MyBatis?
2.1创建数据库及其表
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';
-- 创建⽂章表
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://ww
w.baidu.com',1);
2.2引入MyBatis依赖并配置相关信息
1.创建new project的时候,选择引入MyBatis依赖,一定要同时选择Mysql Driver因为你要告诉MyBatis你要连接的数据库是什么类型的:MySQL?Oracle?......
2. 在我们添加好maven之后,需要在application.properties中添加配置信息:
#设置数据库的相关链接信息:
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mycnblog?characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=你的数据库密码
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#设置mybatis配置信息:当前路径下的mybatis包下以Mapper.xml结尾的文件中实现接口
mybatis.mapper-locations=classpath:mybatis/*Mapper.xml
#打印sql语句:
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
logging.level.com.example.demo=debug
(你注意了!其中有个 spring.datasource.password要填你的数据库密码!!)
2.3添加业务代码
1.那么根据图我们先创建一个实体类:
package com.example.demo.model;
import lombok.Data;
import java.time.LocalDate;
/**
* Created with IntelliJ IDEA.
* Description:实体类
* User: yyx
* Date: 2023-10-27
* Time: 21:26
*/
@Data
public class Userinfo {
private int id;
private String username;
private String password;
private String photo;
private LocalDate createtime;
private LocalDate uodatetime;
private int state;
}
(你注意了! 这里的类的类名最好跟你的数据库中表名相同,类的属性要跟该表下的字段名相同!!!最好在model包底下,因为model层掌管数据的)
添加上Lombok的注解,这样就无须手动生成getter/setter方法了~~~
下面就是重点了!!!
2.添加mapper接口:
注意它的这个目录结构,这个UserMapper是个接口在dao层,dao掌管接口!!
(你注意了!!这个接口层只是声明方法,不需要亲自实现,真正实现方法是在.xml中实现的)
3. 在.xml实现方法
注意它这个目录结构!!在resources底下建一个包mybatis然后生成一个以Mapper.xml结尾的文件 ! 前缀的话最好跟你的接口文件相对应,词能达意~~
原因是我们在application.properties中声明了这一点:
在*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">
<!--只需要更改namespace-->
<mapper namespace="com.example.demo.dao.UserMapper">
</mapper>
这里只需要更改namespace即可:(用来指明这个xml是实现哪个接口的)
4.在dao层的mapper中实现service
package com.example.demo.dao;
import com.example.demo.model.Userinfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* Created with IntelliJ IDEA.
* Description:数据持久层的标志
* User: yyx
* Date: 2023-10-27
* Time: 21:24
*/
@Mapper
public interface UserMapper {
//Interface只写声明!!!方法的声明
List<Userinfo> getAll();
}
这个声明是代表返回的类型是一个关于Userinfo的集合类,方法名称是getAll().
声明之后就要在.xml中实现这个方法了:(你注意了! 这个标签写在<mapper></mapper>里面)
<select id="getAll" resultType="com.example.demo.model.Userinfo">
select * from userinfo
</select>
查询的SQL还是需要自己来写的~~我们需要用到一个<select></select>,不需要在SQL语句后加上分号
标签里面的id属性就是你在接口里面的方法名,必须要对应上!!! 其中的resultType属性是表明返回的类型是哪个实体类(要写全限定名:包名+类名)
5.生成Test单元测试,验证是否可以对数据库进行操作
在UserMapper接口中右键单击选择Generate 然后选择Test(你注意了!有的码友可能没有Test你需要在UserMapper代码块中右键才可以,不要在空白地方右键单击!!)
注意目录结构!!
此时我们需要写两处:
1.使用@AutoWired注入UserMapper
2.写入测试代码;
使用usermapper调用对应的方法,使用约定好的返回值接受
然后就能在日志里查看内容了,因为我们在配置文件里设置了打印日志,所以能看到日志信息:
(因为小玉这数据库里前后插入过一些数据,所以查出来好多结果,你们的话,用我给你们的SQL语句应该只有一条:id为1,username和password都叫admin....)
3.实操
3.1 <select>---传一个参数的查询操作
1.在UserMapper接口中声明方法:
int delByID(@Param("id")Integer id);
这里需要使用一个只能修饰参数的注解@Param 其中括号里的属性尽量与字段名保持一致,避免出错,后面跟上参数类型及名称.
2.在.xml中实现方法:
<select id="getById" resultType="com.example.demo.model.Userinfo">
select * from userinfo where id=${uid}
</select>
其中我们需要将参数内嵌到SQL语句中:此时用到了格式化工具:#{} 这里小玉需要着重讲一下这里,因为今天刚好被一个面试官问到了!!!!
* #{} 和 ${} 的区别?
在参数匹配的时候,我们通常会使用#{}或者${}来进行占位,但是他们之间是有区别的:
#{}是预编译层面的替换,在编译的时候会先将待替换的部分编译成一个问号? 然后再日志的Params中会将参数解析,填入问号当中;
${}是直接替换,在编译的时不设置问号?,直接将参数替换在占位符上,但是这样的直接替换会引起严重的SQL注入问题!非常危险!
而#{}就没有SQL注入的问题,因为其底层是使用PreparadStatement预编译,在参数中若是发现了类似SQL的语句(如' 或=1或" 等等会进行转义,然后替换 )
3.生成Test进行测试:
@Test
void getById() {
Userinfo userinfo = userMapper.getById("1");
System.out.println(userinfo.toString());
}
此时注意了@Autowired只需要注入一次UserMapper就可以了,这里我们在调用getById()方法的时候就需要传入参数了,设置的id将被传入Params当中被#{}替换掉:
3.2 <delete>---传入一个参数的删除操作
1.在UserMapper中声明方法: 同样使用@Param注解
int delByID(@Param("id")Integer id);
2.在UserMapper.xml中实现方法:
<delete id="delByID">
delete from userinfo where id=#{id}
</delete>
3.生成Test测试;
void delByID() {
Integer id = 1;
int i = userMapper.delByID(id);
System.out.println("受影响的行数:"+i);
}
(你注意了!! 这里参数返回是一个int类型代表受影响的行数,不是返回一个Userinfo对象或者是一个List<Userinfo>集合类了,这跟我们在MySQL中的逻辑是一样的,增删改操作都是返回受影响的行数的!)
4.看日志:
此时我们可以打开数据库看一下ID为1的是不是被删除了:
没了已经
3.3 <update>---传入一个对象的修改操作
1.在UserMapper中声明方法:
int update(Userinfo userinfo);
2.在UserMapper.xml中实现方法:
<update id="update">
update userinfo set username='${username}' where id='${id}'
</update>
这里我们使用${}占位,我们换换看看:(因为是直接替换所以当要替换的内容是String类型的时候,需要手动加上单引号 ' ' )
3.生成Test:
@Test
void update() {
Userinfo userinfo = new Userinfo();
userinfo.setId(1);
userinfo.setUsername("超级管理员");
int update = userMapper.update(userinfo);//我忘记调用mapper接口了
System.out.println(update);
}
此时我们需要注意,因为传入的是一个Userinfo对象,所以我们需要下实例化该对象,设置一下这个对象你需要修改的值,仅设置需要修改的字段即可~~(@Lombok注解会提供getter/setter方法);
必须牢记需要使用userMapper调用对应的接口声明的方法!!
4.查看日志:
(小玉的id为1的信息已经被删了,假如你们是跟着小玉做的,那就请手动在MySQL中插入原来的那个信息吧~然后再修改,这里小玉修改了另外的信息,id不为1的张三同学~~~)
可以看到这里不是使用?预编译了,而是直接替换注意直接替换时String类型的问题!!!!!!!!!!!!
看一下数据库改了没:
3.4 <insert>---传入一个对象的增加操作
1.在UserMapper中声明方法;
int add(Userinfo userinfo);//返回受影响的函数
2.在UserMapper.xml中实现方法:
<insert id="add">
insert into userinfo(id,username,password) values(#{id},#{username},#{password})
</insert>
3.生成Test方法验证:
@Test
void add() {
Userinfo userinfo = new Userinfo();
userinfo.setUsername("张三");
userinfo.setId(1);
userinfo.setPassword("123");
int add = userMapper.add(userinfo);
System.out.println("受影响的行数"+ add);
}
只要我们是以对象作为参数传递的,在Test的时候,都需要先实例化实体类,然后再设置字段,然后再使用userMapper调用方法,注意设置字段的时候,要跟你要实现的操作参数对应!!
看下数据库:
3.4.1 如何在insert的时候拿到自增id而不是受影响的行数?
在默认情况下,增删改都是以int作为返回值,代表受影响的行数的,那么如何返回该数据的自增id呢?
在UserMapper中还是老样子定义一个add2()方法,返回的是int,在xml里实现的时候,就需要下功夫了:
<insert id="add2" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
insert into userinfo (photo,username,password) values(#{photo},#{username},#{password})
</insert>
useGeneratedKeys 代表:令 MyBatis 使⽤ JDBC 的 getGeneratedKeys ⽅法来取出由数据库内部⽣成的主键(⽐如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的⾃动递增字段),默认值:false。
keyColumn:设置⽣成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第⼀列的时候,是必须设置的。如果⽣成列不⽌⼀个,可以⽤逗号分隔多个属性名称。
@Test
void add2() {//每次调用这个方法,主键都会加一
Userinfo userinfo = new Userinfo();
userinfo.setPassword("444");
userinfo.setPhoto("");
userinfo.setUsername("李四");
int i = userMapper.add2(userinfo);
System.out.println("受影响的行数+"+i+"主键:"+userinfo.getId());
}
此时我们注意到,这个UserMapper调用add2()方法还是会返回一个int 还是代表受影响的行数,但是我们使用userinfo这个实体类的getId()方法即可返回自增主键,我们在Test中并未设置id但是却能返回一个id就代表我们的useGeneratedKeys / keyColumn / keyProperty 生效了!!
*模糊匹配 like concat()
当我们需要在表中模糊查找姓名中带有"三"这个人时,就需要用到模糊匹配,那这时候我们就需要传入一个参数,记得返回值是一个List<Userinfo>:(别忘记在@Param()之后写上参数类型和参数名!小玉老是忘记)
1.UserMapper中声明:
List<Userinfo> getLike(@Param("username")String username);
2..XML实现:
<select id="getLike" resultType="com.example.demo.model.Userinfo">
select * from userinfo where username like concat('%',#{username},'%')
<!--一定要注意字段匹配 username-->
</select>
我们在SQL中的写法应该是会是这样的:
select * from userinfo where username like '%三%';
这个三是我们希望传进来的参数,但是前面的'% %'我们可以使用concat()方法拼接:将多个字符串连接成一个字符串。
3.生成Test检验:
@Test
void getLike() {
String username = "超级";
List<Userinfo> like = userMapper.getLike(username);
System.out.println(like);
}
好像也没什么可讲了,我们使用mybatis主要还是要熟悉SQL语句,熟悉数据库的使用,毕竟框架还是建立在最基础的语法上的,mybatis主要就是操作一个映射层Mapper层,用来声明方法,还有一层是xxxMapper.xml,以Mapper.xml结尾的文件,用来实现方法,,这连个层级之间一一对应,对了,码友们可以下载一个MybatisX这样的插件,这样的话很方便的:
点击小鸟就会在两个问价当中来回切换,就不用你手动去找了~~~
主要就是增删改查这四个标签,还有一些面试题,大家应该可以应对自如的吧?