文章目录
什么是MyBatis
MyBatis 是⼀款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。简单来说 MyBatis 是更简单完成程序和数据库交互的工具,也就是更简单的操作和读取数据库工具,是基于JDBC的封装。
MyBatis 也是⼀个 ORM 框架,ORM 把数据库映射为对象:
- 数据库表映射为类
- 表中数据映射为对象
- 表的字段映射为对象的属性
添加 MyBatis 框架
已有项目
如果是在已有项目添加有两种方式
第一种直接在 pom.xml 文件中添加:
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
第二种使用插件 EditStarters ,下载此插件后在 pom.xml 文件点击鼠标右键,选中 Generate,然后点击Add 最后搜索添加需要的依赖
新建项目
新建项目时,只需要直接添加依赖,mybatis是必须的,然后需要添加操作的数据库,例如mysql
MyBatis基本查询
配置连接数据库
在配置文件中配置需要连接的数据库属性,例如 yml配置文件
spring:
datasource:
url: jdbc:mysql://127.0.0.1/mycnblog?characterEncoding=utf8&useSSL=false
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
# 创建所有表的 xml ⽂件
mybatis:
mapper-locations: classpath:/mybatis/*Mapper.xml
如果使用的 mysql 版本是 5.x 的则 driver-class-name 为 com.mysql.jdbc.Driver,如果mysql是5.x以上的则为 com.mysql.cj.jdbc.Driver
mapper-locations 表示的是所有保存了具体操作 sql 的 xml 文件路径。MyBatis所有的sql语句都是保存在 xml 文件中的
添加实体类
这个实体类的对象就是表中数据的映射
@Data
public class User {
private Integer id;
private String username;
private String password;
private String photo;
private LocalDateTime createTime;
private LocalDateTime updateTime;
private Integer state;
}
添加 mapper 接口
MyBatis 的具体实现是在 xml 文件中,而方法的声明则是在接口类中
@Mapper
public interface UserMapper {
// 获取所有用户
List<User> getAll();
}
添加具体实现操作的 .xml 文件
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.UserMapper">
<select id="getAll" resultType="com.example.demo.entity.User">
select * from userinfo
</select>
</mapper>
其中上面部分是固定写法
- namespace 属性表示的是需要实现的接口路径。
- select 标签代表查询。
- id 属性表示需要实现的接口方法的方法名。
- resultType 属性表示查询到的数据映射的实体类
添加服务层
服务层用来返回接口对象实现方法的结果
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public List<User> getAll(){
return userMapper.getAll();
}
}
添加控制器层
控制器层是最终的实现,并指明路由地址
@RestController
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("user")
public List<User> getAll(){
return userService.getAll();
}
}
查询结果
最终的目录层级为:
数据库中原有数据:
启动项目访问得到数据:
查询指定属性的数据
#{} 和 ${}
在了解后续操作之前,需要先认识这两个符号的区别。
在 MyBatis 中写sql语句,通常都会需要传递数据给sql语句的操作,那么和JDBC一样都是使用符号去将需要传递的数据包起来。
而这两个的区别就在与:
- #{} 预编译处理。如果传递的是字符串类型的数据,那么这个符号就会加上 ‘’ 号。
- ${} 直接替换。使用这个不会加上其他的符号,数据是什么就是什么
因此需要查询的时候,推荐使用 #{}
查询指定名字的用户
首先在 Mapper接口中定义方法
User getUserByName(String username);
在 xml 文件中实现方法
<select id="getUserByName" resultType="com.example.demo.entity.User">
select * from userinfo where username=#{username}
</select>
在服务层中调用方法
public User getUserByName(String username){
return userMapper.getUserByName(username);
}
在控制器层中调用方法,并编写路由地址
@RequestMapping("getbyname")
public User getUserByName(String username){
return userService.getUserByName(username);
}
启动项目访问 url
增 删 改
增
使用 < insert >标签,这个标签默认返回值为 Integer类型。注意如果直接返回是只会返回sql语句成功的行数,并不会返回增加用户的id。如有需求需要在控制器层去手动返回用户id。或者使用 keyColumn 属性去指定返回的表中的字段值,并且将 useGeneratedKeys 属性设置为true 后,就可以获取到自增的主键
<insert id="add" useGeneratedKeys="true" keyProperty="id">
insert into userinfo(username,password) values(#{username},#{password})
</insert>
@RequestMapping(value = "/add",method = RequestMethod.POST)
public Integer add(@RequestBody User user){
userService.getAdd(user);
return user.getid();
}
改
使用 < update >标签
<update id="update">
update userinfo set username=#{name} where id=#{id}
</update>
删
使用 < delete > 标签
模糊查询
sql 语句中的模糊查询使用 like 关键字,根据上面的讲述很容易就能写出如下代码:
<select id="UserByName2" resultType="com.example.demo.model.User">
select * from userinfo where username like '%#{username}%';
</select>
但其实这样子写是错误的,因为这条sql 语句最终会转换为:
select * from userinfo where username like '%'username'%';
这很明显就不符合sql 的标准。所以进行模糊查询的时候需要使用 mysql 的内置函数 concat() 去进行字符串的拼接:
<select id="UserByName2" resultType="com.example.demo.model.User">
select * from userinfo where username like concat('%',#{username},'%');
</select>
这样才能拼接出正确且完整的sql 语句:
select * from userinfo where username like '%username%';
resultMap
在 select 标签有一个属性 resultType ,这代表的是查询到的结果映射的类对象。那么 MyBatis的映射规则是根据名称来映射的,也就是说当类里面的属性名和表中的字段名不一致时就得不到数据了。
那么为了解决这种场景就需要使用 resultMap 属性了。首先在 xml 文件中设置好resultMap标签
<resultMap id="Base" type="com.example.demo.entity.User">
<!-- 主键 -->
<id property="id" column="id"/>
<!-- 其他字段 -->
<result property="username" column="username"/>
<result property="pwd" column="password"/>
<result property="createTime" column="createTime"/>
</resultMap>
id:这个标签的标识
type:这个标签所映射的实体类
property:实体类中属性名称
column:数据表中的字段名称
通过这样就可以将实体类中的属性名和数据表中的字段名所关联起来,接着实现select标签
<select id="getListByName" resultMap="Base">
select * from userinfo where username like concat('%', #{username}, '%')
</select>
其中 resultMap属性设置就与刚刚设置的id一致即可
多表查询
实现多表查询可以有几种方式,例如使用 < association >标签或者 < collection >标签。在这里不会对这两展开讲。
首先当我们要进行多表查询时,先确定好主表是哪一个。确定好主表后创建这个表的实体类,例如:
@Data
public class Article {
private Integer id;
private String title;
private String content;
private LocalDateTime createtime;
private LocalDateTime updatetime;
private Integer uid;
private Integer rcount;
private Integer state;
}
创建好实体类之后,再看看还需要查询的副表的属性,创建一个 VO实体类继承于主表的实体类,然后在这个 VO类中添加副表的属性。这样Vo类既有主表的属性又有副表的属性:
@Data
public class ArticleVO extends Article {
private String username;
@Override
public String toString() {
return "ArticleVO{" +
"username='" + username + '\'' +
"} " + super.toString();
}
}
注意这个VO类才是我们用来接收数据的实体类
然后在 xml文件中编写sql语句
<select id="getDetail" resultType="com.example.demo.entity.vo.ArticleVO">
select a.*, u.username from articleinfo a
left join userinfo u on u.id=a.uid where a.id=#{id}
</select>
利用sql语句得到数据后映射到拥有主表和副表字段属性的VO类,这样就能实现简单的多表查询了。
动态 sql
动态 sql 是Mybatis的强⼤特性之⼀,能够完成不同条件下不同的 sql 拼接
< if >标签
有些场景下添加信息添加到数据库中会出现 非必选项这个数据,也就是说在数据表中有这个字段但是再插入数据时并不需要传这个字段的情况下,那么此时编写sql就有难度了,因为并不确定前端传回的数据哪个字段是没有传的,这时候就需要用到标签
<insert id="addUser2">
insert into userinfo(username,
<if test="img!=null">
photo,
</if>
password) values(#{username},
<if test="img!=null">
#{img},
</if>
#{pwd})
</insert>
这个代码的意思是:如果传来的数据中img属性不为空,那么sql语句就拼接上 photo, 这段字符串。
也就是当img属性不为空时就会转换为:
insert into userinfo(username, photo, password) values(#{username}, #{img}, #{pwd});
< trim >标签
if标签虽然很方便但是也会有缺陷,假设现在所有的属性都是非必传的,全部都加上if标签,那最终转换完了之后都会有一个 , 在语句的最后面就会导致sql语句出错。
因此就需要用到 trim 标签
<insert id="addUser3">
insert into userinfo
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="username!=null and username!=''">
username,
</if>
<if test="img!=null and img!=''">
photo,
</if>
<if test="pwd!=null and pwd!=''">
password,
</if>
</trim>
values
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="username!=null and username!=''">
#{username},
</if>
<if test="img!=null and img!=''">
#{img},
</if>
<if test="pwd!=null and pwd!=''">
#{pwd},
</if>
</trim>
</insert>
prefix:表示如果下边有一个if成立则拼接这个字符串
suffix:表示prefix如果有则在标签完成的最后拼接这个字符串
suffixOverrides:表示删除最后存在的这个字符串
也就是说上述的代码假设username和password成立,则变为
insert into userinfo(username, password) values(#{username}, #{pwd});
< where > 和 < set >
有时候做查询或修改操作时也是会有非必传的,那么当需要使用where和set时并不确定非必传的参数是否有值,则这时就不能提前将这两关键字加到sql语句中,就需要使用标签
<select id="getListByIdOrTitle" resultType="com.example.demo.entity.vo.ArticleInfoVO">
select * from articleinfo
<where>
<if test="id!=null and id>0">
id=#{id}
</if>
<if test="title!=null and title!=''">
and title like concat('%',#{title},'%')
</if>
</where>
</select>
例如上述代码,如果id或者title成立,则会自动添加上where,注意where不能删除后缀字符串,只能删除前缀字符串
也就是说当id不成立title成立时,就会自动将and去掉变为:
select * from articleinfo where title like concat('%',#{title},'%');