目录
1.MyBatis是什么?
- Mybatis是一款优秀的持久层框架。
- 它支持定制化SQL、存储过程以及高级映射。
- Mybatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。
- Mybatis可以使用简单的XML或注解来配置和映射原生类型、接口和Java的POJO(Plain Old Java Object,普通老式Java对象)为数据库中的记录。
- Mybatis本是Apache的一个开源项目Ibatis,2010年这个项目由Apache software foundation迁移到了Google Code,并改名为Mybatis。
- Mybatis中文文档 :mybatis – MyBatis 3 | 简介
- GitHub : https://github.com/mybatis/myba
2.为什么学习MyBatis?
对于后端开发来说,程序是由以下两个重要的部分组成的: 1. 后端程序 2. 数据库
而这两个重要的组成部分要通讯,就要依靠数据库连接工具,那数据库连接工具有哪些?比如之前我们学习的 JDBC,还有今天我们将要介绍 的 MyBatis,那已经有了 JDBC 了,为什么还要学习 MyBatis? 这是因为 JDBC 的操作太繁琐了
- Mybatis就是帮助程序猿将数据存入数据库中 , 和从数据库中取数据 .
- 传统的jdbc操作 , 有很多重复代码块 .比如 : 数据取出时的封装 , 数据库的建立连接等等… , 通过框架可以减少重复代码,提高开发效率 .
- MyBatis 是一个半自动化的ORM框架 (Object Relationship Mapping) —>对象关系映射
- 所有的事情,不用Mybatis依旧可以做到,只是用了它,所有实现会更加简单!技术没有高低之分,只有使用这个技术的人有高低之别
- MyBatis的优点
- 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件就可以了,易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
- 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。
- 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
- 提供xml标签,支持编写动态sql。
3.MyBatis的第一个程序
3.1MyBatis框架定位
开始搭建 MyBatis 之前,我们先来看一下 MyBatis 在整个框架中的定位,框架交互流程图:
MyBatis 也是一个 ORM 框架,ORM(Object Relational Mapping),即对象关系映射。在面向对象编程语言中,将关系型数据库中的数据 与对象建立起映射关系,进而自动的完成数据与对象的互相转换:
1. 将输入数据(即传入对象)+SQL 映射成原生 SQL
2. 将结果集映射为返回对象,即输出对象 ORM 把数据库映射为对象: 数据库表(table)--> 类(class) 记录(record,行数据)--> 对象(object) 字段(field) --> 对象的属性(attribute)
一般的 ORM 框架,会将数据库模型的每张表都映射为一个 Java 类。 也就是说使用 MyBatis 可以像操作对象一样来操作数据库中的表,可以实现对象和数据库表之间的转换,接下来我们来创建 MyBatis 的第一个实例吧。
3.2创建数据库和表
接下来我们要实现的功能是:使用 MyBatis 的方式来读取文章表中的所有用户,我们使用个人博客的数据库和数据包,具体 SQL 如下。
drop database if exists java23;
create database java23 DEFAULT CHARACTER SET utf8;
-- 使用数据数据
use java23;
-- 创建表[用户表]
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,
`state` int default 1
);
-- 创建文章表
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 ,
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 ,
uid int
);
-- 添加一个用户信息
INSERT INTO `java23`.`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 articleinfo(title,content,uid) values('C++','C++正文',1);
insert into articleinfo(title,content,uid) values('#C','#C正文',1);
-- 添加视频
insert into videoinfo(vid,title,url,uid) values(1,'java title','http://www.baidu.com',1);
3.3添加MyBatis框架支持
添加 MyBatis 框架支持分为两种情况:一种情况是对自己之前的 Spring 项目进行升级,另一种情况是创建一个全新的 MyBatis 和 Spring Boot 的项目,下面我们分别来演示这两种情况的具体实现。
如果是在老项目中新增功能,添加框架支持:
<!-- 添加 MyBatis 框架 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<!-- 添加 MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
在老项目中还存在一种方法:操作方式是使用EditStarters插件
搜索“MyBatis”添加即可:
新项目添加MyBatis
选好依赖即可
3.4配置连接字符串和MyBatis
此步骤需要进行两项设置,数据库连接字符串设置和 MyBatis 的 XML 文件配置。
1 配置连接字符串
在项目resource下的templates下的application配置文件进行配置
# 数据库连接配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/mycnblog?characterEncoding=utf8&useSSL=false
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
如果使用 MySQL 是 5.x 之前的使用的是“com.mysql.jdbc.Driver”,如果是大于 5.x 使用的是“com.mysql.cj.jdbc.Driver”
2 配置 MyBatis 中的 XML 路径
# 配置 mybatis xml 的文件路径,在 resources/下的application中 创建所有表的 xml 文件
mybatis:
mapper-locations: classpath:mapper/**Mapper.xm
3.5添加相关代码
我们按照这个流程来一步步实现功能
1 添加实体类
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.Date;
@Setter
@Getter
@ToString
public class Articleinfo {
private Integer id;
private String title;
private String content;
private Date createtime;
private Date updatetime;
private Integer uid;
private Integer rcount;
private Integer state;
}
2 添加 mapper 接口
数据持久层的接口定义:
import com.example.mybatisstudy.model.Articleinfo;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
import java.util.Map;
@Mapper
public interface ArticleMapper {
List<Articleinfo> all();
Articleinfo queryById(Integer id);
int add(Articleinfo info);
int modify(Articleinfo content);
int delete(Integer id);
}
3 添加 ArticleMapper.xml
数据持久成的实现,mybatis 的固定 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.mybatisstudy.mapper.ArticleMapper">
<select id="getAll" resultType="com.example.demo.model.Articleinfo">
select * from userinfo
</select>
</mapper>
<mapper>标签:需要指定 namespace 属性,表示命名空间,值为 mapper 接口的全限定名,包括全包名.类名。
4 添加 Controlle
控制器层的实现代码如下:
package com.example.mybatisstudy.controller;
import com.example.mybatisstudy.mapper.ArticleMapper;
import com.example.mybatisstudy.model.Articleinfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/article")
public class ArticleController {
private static final Map map=new HashMap<>();
@Autowired
private ArticleMapper articleMapper;
@RequestMapping("/queryAll")
public Object queryAll(){
return articleMapper.all();
}
}
我们编写了基本的查询Articleinfo所有信息的方法,可以借助postman来测试一下
4.实现增,删,改,操作
接下来,我们来实现一下用户的增加、删除和修改的操作
我们以简单的操作作为演示:
ArticleMapper.xml:
<insert id="add">
insert into articleinfo(title,content,uid) values (#{title},#{content},#{uid})
</insert>
<update id="modify">
update articleinfo set content=#{content} where id=#{id}
</update>
<delete id="delete">
delete from articleinfo where id=#{id}
</delete>
mapper interface:
import com.example.mybatisstudy.model.Articleinfo;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
import java.util.Map;
@Mapper
public interface ArticleMapper {
List<Articleinfo> all();
int add(Articleinfo info);
int modify(Articleinfo content);
int delete(Integer id);
}
controller 实现代码:
import com.example.mybatisstudy.mapper.ArticleMapper;
import com.example.mybatisstudy.model.Articleinfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/article")
public class ArticleController {
private static final Map map=new HashMap<>();
@Autowired
private ArticleMapper articleMapper;
@RequestMapping("/queryAll")
public Object queryAll(){
return articleMapper.all();
}
@RequestMapping("/add")
public Object add(Articleinfo info){
int add = articleMapper.add(info);
return add;
}
@RequestMapping("/modify")
public Object modify(Articleinfo info){
int modify = articleMapper.modify(info);
return modify;
}
@RequestMapping("/del")
public Object delete(Integer id){
int delete = articleMapper.delete(id);
return delete;
}
}
5.查询操作
5.1Select
import java.util.List;
import java.util.Map;
@Mapper
public interface ArticleMapper {
List<Articleinfo> all();
Articleinfo queryById(Integer id);
int add(Articleinfo info);
int modify(Articleinfo content);
int delete(Integer id);
}
controller实现方法:
@RequestMapping("/queryById")
public Object queryById(Integer id){
return articleMapper.queryById(id);
}
xml文件下:
<select id="queryById" resultType="com.example.mybatisstudy.model.Articleinfo">
select * from articleinfo where id=#{id}
</select>
5.2模糊查询?
5.3多表查询
一对一的表映射
一对一映射要使用 标签,具体实现如下:
<resultMap id="BaseMap" type="com.example.demo.model.ArticleInfo">
<id property="id" column="id"></id>
<result property="title" column="title"></result>
<result property="content" column="content"></result>
<result property="createtime" column="createtime"></result>
<result property="updatetime" column="updatetime"></result>
<result property="uid" column="uid"></result>
<result property="rcount" column="rcount"></result>
<result property="state" column="state"></result>
<association property="user"
resultMap="com.example.demo.mapper.UserMapper.BaseMap"
columnPrefix="u_">
</association>
</resultMap>
<select id="getAll" resultMap="BaseMap">
select a.*,u.username u_username from articleinfo a
left join userinfo u on a.uid=u.id
</select>
以上使用 标签,表示一对一的结果映射:
property 属性:指定 Article 中对应的属性,即用户。
resultMap 属性:指定关联的结果集映射,将基于该映射配置来组织用户数据。
columnPrefix 属性:绑定一对一对象时,是通过 columnPrefix+association.resultMap.column 来映射结果集字段。association.resultMap.column是指 标签中 resultMap属性,对应的结果集映射中,column字段。
注意事项:column不能省略
一对多:一个用户多篇文章示例:
<resultMap id="BaseMap" type="com.example.demo.model.User">
<id column="id" property="id" />
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="photo" property="photo"></result>
<collection property="alist" resultMap="com.example.demo.mapper.ArticleInfoMapper.BaseMap"
columnPrefix="a_">
</collection>
</resultMap>
<select id="getUserById" resultMap="BaseMap">
select u.*,a.title a_title from userinfo u
left join articleinfo a on u.id=a.uid where u.id=#{id}
</select>
6.动态sql使用
6.1 <if>标签
两种字段:必填字段和非必填字段,那如果在添加的时候有不确定的字段传入,程序应该如何实现呢? 这个时候就需要使用动态标签 来判断了,比如添加的时候性别 sex 为非必填字段,具体实现如下:
<insert id="insert" parameterType="org.example.model.User" useGeneratedKeys="true" keyProperty="id">
insert into user(
username,
password,
nickname,
<if test="sex != null">
sex,
</if>
birthday,
head
) values (
#{username},
#{password},
#{nickname},
<if test="sex != null">
#{sex},
</if>
#{birthday},
#{head}
)
</insert>
注意 test 中的 sex,是传入对象中的属性,不是数据库字段
6.2 <trim>标签
之前的插入用户功能,只是有一个 sex 字段可能是选填项,如果有多个字段,一般考虑使用标签结合标签,对多个字段都采取动态生成的方式。 标签中有如下属性:
prefix:表示整个语句块,以prefix的值作为前缀
suffix:表示整个语句块,以suffix的值作为后缀
prefixOverrides:表示整个语句块要去除掉的前缀
suffixOverrides:表示整个语句块要去除掉的后缀
例如:
<insert id="insert" parameterType="org.example.model.User" useGeneratedKeys="true" keyProperty="id">
insert into user
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="username != null">
username,
</if>
<if test="password != null">
password,
</if>
<if test="nickname != null">
nickname,
</if>
<if test="sex != null">
sex,
</if>
<if test="birthday != null">
birthday,
</if>
<if test="head != null">
head,
</if>
<if test="createTime != null">
create_time,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="username != null">
#{username},
</if>
<if test="password != null">
#{password},
</if>
<if test="nickname != null">
#{nickname},
</if>
<if test="sex != null">
#{sex},
</if>
<if test="birthday != null">
#{birthday},
</if>
<if test="head != null">
#{head},
</if>
<if test="createTime != null">
#{createTime},
</if>
</trim>
</insert>
在以上 sql 动态解析时,会将第一个 部分做如下处理:
基于 prefix 配置,开始部分加上 (
基于 suffix 配置,结束部分加上 )
多个 组织的语句都以 , 结尾,在最后拼接好的字符串还会以 , 结尾,会基于 suffixOverrides 配置去掉最后一个
注意 中的 createTime 是传入对象的属性
6.3 <where>标签
传入的用户对象,根据属性做where条件查询,用户对象中属性不为 null 的,都为查询条件。如 user.username 为 "a",则查询条件为 where username="a": UserMapper 接口中新增条件查询方法:
List<User> selectByCondition(User user);
UserMapper.xml 中新增条件查询 sql:
<select id="selectByCondition" parameterType="org.example.model.User" resultMap="BaseResultMap">
select id, username, password, nickname, sex, birthday, head, create_time
from user
<where>
<if test="username != null">
and username=#{username}
</if>
<if test="password != null">
and password=#{password}
</if>
<if test="nickname != null">
and nickname=#{nickname}
</if>
<if test="sex != null">
and sex=#{sex}
</if>
<if test="birthday != null">
and birthday=#{birthday}
</if>
<if test="head != null">
and head=#{head}
</if>
<if test="createTime != null">
and create_time=#{createTime}
</if>
</where>
</select>
以上标签也可以使用<trim prefix="where " prefixoverrides="and">替换。
6.4 <set>标签
根据传入的用户对象属性来更新用户数据,可以使用标签来指定动态内容。 UserMapper 接口中修改用户方法:根据传入的用户 id 属性,修改其他不为 null 的属性:
int updateById(User user);
UserMapper.xml 中添加更新用户 sql:
<update id="updateById" parameterType="org.example.model.User">
update user
<set>
<if test="username != null">
username=#{username},
</if>
<if test="password != null">
password=#{password},
</if>
<if test="nickname != null">
nickname=#{nickname},
</if>
<if test="sex != null">
sex=#{sex},
</if>
<if test="birthday != null">
birthday=#{birthday},
</if>
<if test="head != null">
head=#{head},
</if>
<if test="createTime != null">
create_time=#{createTime},
</if>
</set>
where id=#{id}
</update>
以上标签也可以使用<trim prefix="set " suffixoverrides="and">替换。
6.5 <foreach>标签
对集合进行遍历时可以使用该标签。标签有如下属性:
collection:绑定方法参数中的集合,如 List,Set,Map或数组对象
item:遍历时的每一个对象
open:语句块开头的字符串
close:语句块结束的字符串
separator:每次遍历之间间隔的字符串
示例:根据多个文章 id 来删除文章数据。 ArticleMapper 中新增接口方法:
int deleteByIds(List<Integer> ids);
ArticleMapper.xml 中新增删除 sql:
<delete id="deleteByIds">
delete from article
where id in
<foreach collection="list" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</delete>