MyBatis是一款优秀的持久层框架,它支持自定义SQL,存储过程以及高级映射.MyBatis去除了几乎所有的JDBC代码以及设置参数和获取结果集的工作.MyBatis可以通过简单的XML或注解来配置和映射原始类型,接口和Java POJO(Plain Old Java Object,普通老式Java对象)为数据库中的记录.
简单来说,MyBatis是更简单完成程序和数据库交互的工具,也就是更简单的操作和读取数据库工具.
支持存储过程,存储sql,但(无法调试无debug,可维护性差)
对于后端开发来说,程序是由以下两个重要的部分组成的:
1.后端程序
2.数据库
而这个两个重要的组成部分要通讯,就要依靠数据连接工具,那数据库连接工具有哪些?比如之前所学习的JDBC,还有现在的MyBatis.那么既然已经有JDBC 了,为什么还要用 MyBatis 呢?因为 JDBC 太繁琐了…用过都懂,写个简单的查询都要写一堆繁琐的代码.
MyBatis学习只分为两部分:
● 配置MyBatis开发环境
● 使用MyBatis模式和语法操作数据库
开始搭建MyBatis之前,先看一下MyaBatis在整个框架中的定位,
框架交互流程图:
MyBatis也是一个ORM框架,ORM(Object Relational Mapping),即对象关系映射. 在面向对象编程语言中,将关系型数据库中的数据与对象建立起映射关系,进而自动的完成数据与对象的互相转换:
1.将输入数据(即传入对象)+SQL映射成原生SQL
2.将结果集映为返回对象,即输出对象
ORM把数据库映射为对象:
数据库表(table) --> 类(class)
记录(record,行数据) --> 对象(object)
字段(field) --> 对象的属性(attribute)
一般的ORM框架,会将数据库模型的每张表都映射为一个Java类.
也就是说使用MyBatis可以像操作对象一样来操作数据库中的表,可以实现对象和数据库表之间的转换,关于MyBatis的使用:
-- 创建数据库
drop database if exists mycnblog;
create database mycnblog DEFAULT CHARACTER SET utf8;
-- 使用数据数据
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
);
-- 创建文章表
drop table if exists articleinfo;
create table articleinfo(
id int primary key auto_increment,
title varchar(100) not null,
content text not null,-- longtext
createtime datetime default now(),-- 默认值now()
updatetime datetime default now(),
uid int not null,
rcount int not null default 1,
`state` int default 1
);
-- 创建视频表
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
);
-- 添加一个用户信息
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);
有两种情况:
①老项目添加MyBatis
②创建新项目的时候直接添加MyBatis
- 连接数据库服务器地址
- 数据库用户名
- 数据库密码
- 数据库的驱动(数据库的类型)
# 开发环境
spring.datasource.url=jdbc:mysql://localhost:3306/oj_databases1?characterEncoding=utf8&useSS=false
spring.datasource.username=root
spring.datasource.password=109614
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
# spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver (MySQL版本8.0以后用这个)
根据这个流程来,(以查询用户为例)
添加实体类:
添加mapper(数据持久层)接口Interface方法定义:
mapper(数据持久层)xxx.xml,方法实现:(此处添加UserMapper.xml)
数据持久层的实现,MyBatis的固定 xml 格式:
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>
添加Service层(服务层):
添加Controller(控制器层):
注:pom.xml自带默认的版本,例如mysql8.x(当前我使用的是5.7,所以得自己指定下版本)
由于userInfo实体类同数据库的userInfo表中的用户名字定义的名称不同,导致我们无法访问到
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>
除了之前查询的时候所使用的的第一种返回类型之外(上面这种),还有另外一种方式,那就是返回字典映射,让实体类的属性和对应的数据库中的表的列名一一对应.
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">
<resultMap id="BaseMap" type="com.example.demo.model.UserInfo">
<id column="username" property="name">id>
<result column="username" property="name">result>
<result column="password" property="password">result>
<result column="photo" property="photo">result>
<result column="createtime" property="createtime">result>
<result column="updatetime" property="updatetime">result>
resultMap>
<select id="getAll" resultMap="BaseMap">
select*from userinfo
select>
mapper>
再次访问
这样就能保证一一对应上了
共同点:这两种设置方式的功能是相同的,都是用来指定结果类型
不同点:
注意:之前新建用户表的时候,除了用户名称以及用户密码没有指定值之外,其他都有默认或者是一个自增主键.所以我们在对用户表进行插入的时候,只需要插入这两条数据即可.
Controller控制器层添加代码:
//调用是在service服务器进行调用的
//Controller会调用service
@RestController
@RequestMapping("/user")
public class UserController {
//在controller要先注入service(下层类),这里采用属性注入
@Autowired
private UserService userService;
/**
* 添加用户(返回受影响的行数)
* @param userInfo
* @return
*/
@RequestMapping("/add")
public int add(UserInfo userInfo){
//参数校验 - > 调用业务逻辑层
if(userInfo == null || userInfo.getName() == null
|| userInfo.getPassword() == null
|| userInfo.getName().equals("")
|| userInfo.getPassword().equals("")){
//非法参数
return 0;
}
return userService.add(userInfo);
}
}
Service服务层添加代码:
/**
* 进行数据组装,调用dao层
*/
@Service
public class UserService {
//属性注入,将下层类注入到这
@Autowired
private UserMapper userMapper;
public int add(UserInfo userInfo) {
//服务(方法编排)
return userMapper.add(userInfo);
}
}
Mapper层添加代码(接口和xml):
InterFace:
@Mapper
public interface UserMapper {
//接口中进行声明,而实现是在xml里面的
int add(UserInfo userInfo);
}
XML:
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">
<resultMap id="BaseMap" type="com.example.demo.model.UserInfo">
<id column="username" property="name">id>
<result column="username" property="name">result>
<result column="password" property="password">result>
<result column="photo" property="photo">result>
<result column="createtime" property="createtime">result>
<result column="updatetime" property="updatetime">result>
resultMap>
<insert id="add">
insert into userinfo(username,password)
value(#{name},#{password})
insert>
mapper>
注意:这里传进来的参数的 key 要和 #{} 里面的要对应,不如无法插入
到这里,代码都写完了,接下来就是进行插入测试:
可以看到,插入成功了.
默认情况下,返回的是受影响的行号,如果想要返回自增id
/**
* 添加方法2,返回用户自增id
* @param userInfo
* @return
*/
@RequestMapping(value = "/add2")
public int add2(UserInfo userInfo){
//参数校验 - > 调用业务逻辑层
if(userInfo == null || userInfo.getName() == null
|| userInfo.getPassword() == null
|| userInfo.getName().equals("")
|| userInfo.getPassword().equals("")){
//非法参数
return 0;
}
//调用数据库执行添加操作,执行完之后会将自增id设置到userinfo的id属性
userService.add2(userInfo);
return userInfo.getId();
}
添加过程和前面可以说基本一样,在对应层改一下复制一下add,改成add2()即可.
xml的实现:
<insert id="add2" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
insert into userinfo(username,password)
value(#{name},#{password})
insert>
mapper.xml会将自增的id插入到当前项目对象类中指定的主键属性上.
/**
* 修改操作
* @param id
* @param password
* @return
*/
@RequestMapping("/update")
public int update(int id,String password){
if(id <= 0 || password == null || password.equals("")){
return 0;
}
return userService.update(id,password);
}
<update id="update">
update userinfo set password = #{password}
where id = #{id}
update>
Controller层,剩余层直接快速生成即可.
@RequestMapping(value = "/del")
public Integer del(Integer id){
if(id <= 0){
return 0;
}
return userService.del(id);
}
<delete id="del">
delete from userinfo where id = #{id};
delete>
预编译处理是指: MyBatis 在处理 #{} 时,会将 SQL 中的 #{} 替换为 ? 号,使用 PreparedStatement 的 set 方法来赋值.
直接替换: 是 MyBatis 在处理 ${} 时,就是把 ${} 替换成变量的值.
我举个例子即可,username 为 varchar类型(java中String)
select * from userinfo where username = #{name};
select * from userinfo where username = ${name};然后发送get请求http://localhost:8080/user/select?username=admin
可以看到我们传递了username 为 admin的参数
上面那两局转换为 sql 分别为:
select * from userinfo where username = ‘admin’;
select * from userinfo where username = admin;
直接替换罢了,改成 '${name}'就一样了
既然如此,那么只要#{}不就得了,为什么还要有${}呢?
别急,下面来说说 #{} 的优点,也就是说说他的使用场景
/**
* ${}的使用
* @param order
* @return
*/
@RequestMapping("/sort")
public List<UserInfo> sort(String order){
return userService.sort(order);
}
<select id="sort" resultMap="BaseMap">
select * from userinfo order by id ${sort}
select>
使用 ${sort} 可以实现排序查询,而使用 #{sort} 就不能实现排序查询了,因为当使用 #{sort}查询时,如果传递的值为 String 则会加单引号,就会导致sql错误
{}里的值可以随意指定