SpringBoot学习笔记(四)——Spring Boot集成MyBatis起步
目录
- 一、ORM
- 二、MyBatis
- 三、Spring Boot 集成 MyBatis 快速入门示例
- 3.1、在 IDEA 中创建 Spring Boot 项目
- 3.2、创建数据库与表
- 3.3、添加实体类
- 3.4、定义数据访问接口
- 3.5、定义 sql 映射文件
- 3.6、定义配置文件
- 3.7、配置映射接口所在的包
- 3.8、编写单元测试
- 四、基于 XML 映射实现完整数据访问
- 五、基于注解映射实完整数据访问
- 六、说明与注意事项
- 6.1、parameterType 和 resultType 的区别
- 6.2、#{} 和 ${} 的区别
- 6.3、selectOne()和 selectList() 的区别
- 6.4、映射器选择 XML 还是注解
- 6.5、开启热部署
- 七、视频
- 八、示例
- 九、作业
程序员应该将核心关注点放在业务上,而不应该将时间过多的浪费在 CRUD 中,多数的 ORM 框架都把增加、修改与删除做得非常不错了,然后数据库中查询无疑是使用频次最高、复杂度大、与性能密切相关的操作,我们希望得到一种使用方便,查询灵活的 ORM 框架,MyBatis 可以满足这些要求,MyBatis 是一个支持普通 SQL 查询, 存储过程和高级映射的优秀持久层框架,它也是 SSM 框架集成中的重要组成部分。
一、ORM
1.1、ORM 简介
ORM 可以解决数据库与程序间的异构性,比如在 Java 中我们使用 String 表示字符串,而 Oracle 中可使用 varchar2,MySQL 中可使用 varchar,SQLServer 可使用 nvarchar。
对象关系映射 (英语:Object Relational Mapping,简称 ORM,或 O/RM,或 O/R mapping),用于实现面向对象编程语言里不同类型系统的数据之间的转换。简单的说,ORM 是通过使用描述对象和数据库之间映射的元数据,将程序中的对象与关系数据库相互映射。
没有 ORM 时我们是这样完成对象与关系数据库之间的映射的:
//将执行的 sql String sql = "SELECT name, id, age, password FROM users"; //创建命令对象 preparedStatement = connection.prepareStatement(sql); //执行并获得结果集 resultSet = preparedStatement.executeQuery(); //遍历结果集,将数据库中的数据转换成 Java 中的对象 while(resultSet.next()){ String name = resultSet.getString("name"); int id = resultSet.getInt("id"); int age = resultSet.getInt("age"); String password = resultSet.getString("password"); User entity= new User(name,id,age,password); Users.add(entity); }
这种方案存在以下不足:
持久化层缺乏弹性。一旦出现业务需求的变更,就必须修改持久化层的接口
持久化层同时与域模型与关系数据库模型绑定,不管域模型还是关系数据库模型发生变化,都要修改持久化曾的相关程序代码,增加了软件的维护难度。
将和数据库交互(CRUD)的代码硬编码到 JDBC 程序中
实现见状的持久化层需要高超的开发技巧,而且编程量很大
对象模型和关系模型的转换非常麻烦
ORM(O/R Mapping:对象关系映射):
一种将内存中的对象保存到关系型数据库中的技术
负责实体域对象的持久化,封装数据库访问细节
ORM 提供了实现持久化层的另一种模式,采用映射元数据(XML)来描述对象 - 关系的映射细节,使得 ORM 中间件能在任何一个 Java 应用的业务逻辑层和数据库之间充当桥梁。
ORM 提供了实现持久化层的另一种模式,它采用映射元数据来描述对象关系的映射,使得 ORM 中间件能在任何一个应用的业务逻辑层和数据库层之间充当桥梁。
Java 典型的 ORM 中有:
hibernate:全自动的框架,强大、复杂、笨重、学习成本较高
Mybatis:半自动的框架 (懂数据库的人 才能操作) 必须要自己写 sql
JPA:JPA 全称 Java Persistence API、JPA 通过 JDK 5.0 注解或 XML 描述对象-关系表的映射关系,是 Java 自带的框架
ORM 的方法论基于三个核心原则:
· 简单:以最基本的形式建模数据。
· 传达性:数据库结构被任何人都能理解的语言文档化。
· 精确性:基于数据模型创建正确标准化了的结构。
1.2、ORM 的概念
让我们从 O/R 开始。字母 O 起源于 "对象"(Object), 而 R 则来自于 "关系"(Relational)。几乎所有的程序里面,都存在对象和关系数据库。在业务逻辑层和用户界面层中,我们是面向对象的。当对象信息发生变化的时候,我们需要把对象的信息保存在关系数据库中。
当你开发一个应用程序的时候 (不使用 O/R Mapping), 你可能会写不少数据访问层的代码,用来从数据库保存,删除,读取对象信息,等等。你在 DAL 中写了很多的方法来读取对象数据,改变状态对象等等任务。而这些代码写起来总是重复的。
ORM 解决的主要问题是对象关系的映射。域模型和关系模型分别是建立在概念模型的基础上的。域模型是面向对象的,而关系模型是面向关系的。一般情况下,一个持久化类和一个表对应,类的每个实例对应表中的一条记录,类的每个属性对应表的每个字段。
将关系数据库中表中的记录映射成为对象,以对象的形式展现,程序员可以把对数据库的操作转化为对对象的操作。
因此 ORM 的目的是为了方便开发人员以面向对象的思想来实现对数据库的操作。
1.3、ORM 的优缺点
优点:
1. 提高了开发效率。由于 ORM 可以自动对 Entity 对象与数据库中的 Table 进行字段与属性的映射,所以我们实际可能已经不需要一个专用的、庞大的数据访问层。
2.ORM 提供了对数据库的映射,不用 sql 直接编码,能够像操作对象一样从数据库获取数据。
缺点:
牺牲程序的执行效率和会固定思维模式,降低了开发的灵活性。
从系统结构上来看, 采用 ORM 的系统一般都是多层系统,系统的层次多了,效率就会降低。ORM 是一种完全的面向对象的做法,而面向对象的做法也会对性能产生一定的影响。
在我们开发系统时,一般都有性能问题。性能问题主要产生在算法不正确和与数据库不正确的使用上。ORM 所生成的代码一般不太可能写出很高效的算法,在数据库应用上更有可能会被误用,主要体现在对持久对象的提取和和数据的加工处理上,如果用上了 ORM, 程序员很有可能将全部的数据提取到内存对象中,然后再进行过滤和加工处理,这样就容易产生性能问题。
在对对象做持久化时,ORM 一般会持久化所有的属性,有时,这是不希望的。
但 ORM 是一种工具,工具确实能解决一些重复,简单的劳动。这是不可否认的。但我们不能指望工具能一劳永逸的解决所有问题,有些问题还是需要特殊处理的,但需要特殊处理的部分对绝大多数的系统,应该是很少的。
二、MyBatis
MyBatis 本是 apache 的一个开源项目 iBatis, 2010 年这个项目由 apache software foundation 迁移到了 google code,并且改名为 MyBatis 。2013 年 11 月迁移到 Github。
iBATIS 一词来源于“internet”和“abatis”的组合,是一个基于 Java 的持久层框架。iBATIS 提供的持久层框架包括 SQL Maps 和 Data Access Objects(DAOs)
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects, 普通的 Java 对象) 映射成数据库中的记录。
2.1、MyBatis 的特点
简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个 jar 文件 + 配置几个 sql 映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
灵活:mybatis 不会对应用程序或者数据库的现有设计强加任何影响。 sql 写在 xml 里,便于统一管理和优化。通过 sql 基本上可以实现我们不使用数据访问框架可以实现的所有功能,或许更多。
解除 sql 与程序代码的耦合:通过提供 DAO 层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql 和代码的分离,提高了可维护性。
提供映射标签,支持对象与数据库的 ORM 字段关系映射
提供对象关系映射标签,支持对象关系组建维护
提供 XML 标签,支持编写动态 sql。
2.2、MyBatis 工作流程
(1)、加载配置并初始化
触发条件:加载配置文件
配置来源于两个地方,一处是配置文件,一处是 Java 代码的注解,将 SQL 的配置信息加载成为一个个 MappedStatement 对象(包括了传入参数映射配置、执行的 SQL 语句、结果映射配置),存储在内存中。
(2)、接收调用请求
触发条件:调用 Mybatis 提供的 API
传入参数:为 SQL 的 ID 和传入参数对象
处理过程:将请求传递给下层的请求处理层进行处理。
(3)、处理操作请求 触发条件:API 接口层传递请求过来
传入参数:为 SQL 的 ID 和传入参数对象
处理过程:
(A) 根据 SQL 的 ID 查找对应的 MappedStatement 对象。
(B) 根据传入参数对象解析 MappedStatement 对象,得到最终要执行的 SQL 和执行传入参数。
(C) 获取数据库连接,根据得到的最终 SQL 语句和执行传入参数到数据库执行,并得到执行结果。
(D) 根据 MappedStatement 对象中的结果映射配置对得到的执行结果进行转换处理,并得到最终的处理结果。
(E) 释放连接资源。
(4)、返回处理结果将最终的处理结果返回。
无论是用过的 hibernate,mybatis, 你都可以法相他们有一个共同点:
在 java 对象和数据库之间有做 mapping 的配置文件,也通常是 xml 文件
从配置文件 (通常是 XML 配置文件中) 得到 SessionFactory
由 SessionFactory 产生 Session
在 Session 中完成对数据的增删改查和事务提交等
在用完之后关闭 Session
2.3、MyBatis 架构
Mybatis 的功能架构分为三层:
API 接口层:提供给外部使用的接口 API,开发人员通过这些本地 API 来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。
数据处理层:负责具体的 SQL 查找、SQL 解析、SQL 执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。
基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。
2.4、MyBatis 的主要成员如层次结构
主要成员:
Configuration:MyBatis 所有的配置信息都保存在 Configuration 对象之中,配置文件中的大部分配置都会存储到该类中
SqlSession:作为 MyBatis 工作的主要顶层 API,表示和数据库交互时的会话,完成必要数据库增删改查功能
Executor:MyBatis 执行器,是 MyBatis 调度的核心,负责 SQL 语句的生成和查询缓存的维护
StatementHandler:封装了 JDBC Statement 操作,负责对 JDBC statement 的操作,如设置参数等
ParameterHandler:负责对用户传递的参数转换成 JDBC Statement 所对应的数据类型
ResultSetHandler:负责将 JDBC 返回的 ResultSet 结果集对象转换成 List 类型的集合
TypeHandler:负责 java 数据类型和 jdbc 数据类型 (也可以说是数据表列类型) 之间的映射和转换
MappedStatement:MappedStatement 维护一条 <select|update|delete|insert> 节点的封装
SqlSource:负责根据用户传递的 parameterObject,动态地生成 SQL 语句,将信息封装到 BoundSql 对象中,并返回
BoundSql:表示动态生成的 SQL 语句以及相应的参数信息
层次结构:
更多请参考:《深入理解 mybatis 原理》 MyBatis 的架构设计以及实例分析
2.5、学习资源
mybatis3 中文帮助:http://www.mybatis.org/mybatis-3/zh/index.html
mybatis-spring:http://www.mybatis.org/spring/zh/index.html
MyBatis 中国分站:http://www.mybatis.cn/
源代码:https://github.com/mybatis/mybatis-3/
三、Spring Boot 集成 MyBatis 快速入门示例
3.1、在 IDEA 中创建 Spring Boot 项目
添加依赖
Maven POM
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.7</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.zhangguo</groupId> <artifactId>mybatisdemo2</artifactId> <version>0.0.1-SNAPSHOT</version> <name>mybatisdemo2</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.2</version> </dependency><span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">dependency</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">></span>mysql<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">></span>mysql-connector-java<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">dependency</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">dependency</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">></span>org.springframework.boot<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">></span>spring-boot-starter-test<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">scope</span><span style="color: rgba(0, 0, 255, 1)">></span>test<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">scope</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">dependency</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">dependencies</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">build</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">plugins</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">plugin</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">></span>org.springframework.boot<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">></span>spring-boot-maven-plugin<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">plugin</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">plugins</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">build</span><span style="color: rgba(0, 0, 255, 1)">></span>
</project>
3.2、创建数据库与表
创建数据库和表,针对 MySQL 数据库
SQL 脚本如下:
CREATE TABLE `category` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '类型编号', `name` varchar(64) NOT NULL COMMENT '类型名称', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8;
将 SQL 脚本在 MySQL 数据库中执行,完成创建数据库和表的操作,如下:
表中的数据如下:
3.3、添加实体类
Category 实体类代码如下:package com.zhangguo.mybatisdemo2.entity;import java.io.Serializable;
public class Category implements Serializable {
private int id;
private String name;</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> getId() { </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> id; } </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> setId(<span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> id) { </span><span style="color: rgba(0, 0, 255, 1)">this</span>.id =<span style="color: rgba(0, 0, 0, 1)"> id; } </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> String getName() { </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> name; } </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> setName(String name) { </span><span style="color: rgba(0, 0, 255, 1)">this</span>.name =<span style="color: rgba(0, 0, 0, 1)"> name; } @Override </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> String toString() { </span><span style="color: rgba(0, 0, 255, 1)">return</span> "Category{" + "id=" + id + ", name='" + name + '\'' + '}'<span style="color: rgba(0, 0, 0, 1)">; }
}
3.4、定义数据访问接口
CategoryDao 对应的代码如下:
package com.zhangguo.restdemo.dao;import com.zhangguo.restdemo.entity.Category;
import java.util.List;
public interface CategoryDao {
List<Category> findAll();
}
3.5、定义 sql 映射文件
在 resources 目录下创建一个 mapper 目录,专门用于存放 sql 映射文件,在目录中创建一个 CategoryDao.xml 文件,如下图所示:
CategoryDao.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,namespace 的值习惯上设置成包名 +sql 映射文件名,这样就能够保证 namespace 的值是唯一的 --> <mapper namespace="com.zhangguo.mybatisdemo2.dao.CategoryDao"> <!-- 在 select 标签中编写查询的 SQL 语句, 设置 select 标签的 id 属性为 findAll,id 属性值必须是唯一的,不能够重复,且与接口中的一致 ,resultType 属性指明查询返回的结果集类型,因为设置了别名,这里直接用类名即可 --> <select id="findAll" resultType="Category"> SELECT category.id, category.`name` FROM category </select> </mapper>
解释
参考:https://www.cnblogs.com/hellokitty1/p/5216025.html
3.6、定义配置文件
application.yaml 映射文件中主要包含了数据库连接信息与 mybatis 配置信息:
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver username: root password: 123456 url: jdbc:mysql://localhost:3306/gomall?serverTimezone=GMT&useUnicode=true&characterEncoding=utf-8mybatis:
type-aliases-package: com.zhangguo.mybatisdemo2.entity
mapper-locations: classpath:/mapper/*.xml
type-aliases-package 作用是在 Mybatis 的 mapper.xml 文件中 resultType 的 type 或者 paramterType 会返回自定义 entity, 此时可以用全类名名来指定这些实体。
mapper-locations 用于指定映射文件的扫描位置。
3.7、配置映射接口所在的包
Mybatisdemo2Application.java 启动文件的内容如下:
package com.zhangguo.mybatisdemo2; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @MapperScan("com.zhangguo.mybatisdemo2.dao") public class Mybatisdemo2Application {public static void main(String</span><span style="color: rgba(128, 0, 0, 1); font-weight: bold">[]</span><span style="color: rgba(0, 0, 0, 1)"> args) { SpringApplication.run(Mybatisdemo2Application.class</span>, args)<span style="color: rgba(0, 128, 0, 1)">;
}
}
3.8、编写单元测试
package com.zhangguo.mybatisdemo2;import com.zhangguo.mybatisdemo2.dao.CategoryDao;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
class Mybatisdemo2ApplicationTests {@Autowired CategoryDao categoryDao; @Test </span><span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> categoryDaoTest() { System.out.println(categoryDao.findAll()); }
}
测试结果
四、基于 XML 映射实现完整数据访问
MyBatis 可以使用 XML 或注解作为映射器的描述,XML 强大且可以解偶,注解方便且简单。
因为每一个操作都需要先拿到会话,这里先定义一个工具类以便复用:
XML 映射器 CategoryDao.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,namespace 的值习惯上设置成包名 +sql 映射文件名,这样就能够保证 namespace 的值是唯一的 --> <mapper namespace="com.zhangguo.mybatisdemo2.dao.CategoryDao"> <!-- 在 select 标签中编写查询的 SQL 语句, 设置 select 标签的 id 属性为 findAll,id 属性值必须是唯一的,不能够重复,且与接口中的一致 ,resultType 属性指明查询返回的结果集类型,因为设置了别名,这里直接用类名即可 --> <select id="findAll" resultType="Category"> SELECT category.id, category.`name` FROM category </select><span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">select </span><span style="color: rgba(255, 0, 0, 1)">id</span><span style="color: rgba(0, 0, 255, 1)">="findById"</span><span style="color: rgba(255, 0, 0, 1)"> resultType</span><span style="color: rgba(0, 0, 255, 1)">="Category"</span><span style="color: rgba(0, 0, 255, 1)">></span><span style="color: rgba(0, 0, 0, 1)"> SELECT category.id, category.`name` FROM category where id=#{id} </span><span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">select</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">select </span><span style="color: rgba(255, 0, 0, 1)">id</span><span style="color: rgba(0, 0, 255, 1)">="findByName"</span><span style="color: rgba(255, 0, 0, 1)"> resultType</span><span style="color: rgba(0, 0, 255, 1)">="Category"</span><span style="color: rgba(255, 0, 0, 1)"> parameterType</span><span style="color: rgba(0, 0, 255, 1)">="String"</span><span style="color: rgba(0, 0, 255, 1)">></span><span style="color: rgba(0, 0, 0, 1)"> SELECT category.id, category.`name` FROM category where name like '%${value}%' </span><span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">select</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">insert </span><span style="color: rgba(255, 0, 0, 1)">id</span><span style="color: rgba(0, 0, 255, 1)">="insertCategory"</span><span style="color: rgba(255, 0, 0, 1)"> parameterType</span><span style="color: rgba(0, 0, 255, 1)">="Category"</span><span style="color: rgba(0, 0, 255, 1)">></span><span style="color: rgba(0, 0, 0, 1)"> insert into category(name) value(#{name}); </span><span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">insert</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">update </span><span style="color: rgba(255, 0, 0, 1)">id</span><span style="color: rgba(0, 0, 255, 1)">="updateCategory"</span><span style="color: rgba(255, 0, 0, 1)"> parameterType</span><span style="color: rgba(0, 0, 255, 1)">="Category"</span><span style="color: rgba(0, 0, 255, 1)">></span><span style="color: rgba(0, 0, 0, 1)"> update category set name=#{name} where id=#{id} </span><span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">update</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">delete </span><span style="color: rgba(255, 0, 0, 1)">id</span><span style="color: rgba(0, 0, 255, 1)">="deleteCategory"</span><span style="color: rgba(255, 0, 0, 1)"> parameterType</span><span style="color: rgba(0, 0, 255, 1)">="int"</span><span style="color: rgba(0, 0, 255, 1)">></span><span style="color: rgba(0, 0, 0, 1)"> delete from category where id=#{id} </span><span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">delete</span><span style="color: rgba(0, 0, 255, 1)">></span>
</mapper>
数据访问类 CategoryDao.java:
package com.zhangguo.mybatisdemo2.dao;import com.zhangguo.mybatisdemo2.entity.Category;
import java.util.List;
public interface CategoryDao {
/获取所有*/
List<Category> findAll();
/获取对象通过编号/
Category findById(int id);
/**获取对象列表通过名称/
List<Category> findByName(String name);
/新增*/
int insertCategory(Category category);
/更新/
int updateCategory(Category category);
/**删除通过编号/
int deleteCategory(int id);
}
单元测试:
package com.zhangguo.mybatisdemo2;import com.zhangguo.mybatisdemo2.dao.CategoryDao;
import com.zhangguo.mybatisdemo2.entity.Category;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class Mybatisdemo2ApplicationTests {@Autowired CategoryDao categoryDao; @Test @Order(</span>1<span style="color: rgba(0, 0, 0, 1)">) </span><span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> categoryDaoTest() { System.out.println(categoryDao.findAll()); } @Test @Order(</span>2<span style="color: rgba(0, 0, 0, 1)">) </span><span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> findByIdTest(){ System.out.println(categoryDao.findById(</span>1<span style="color: rgba(0, 0, 0, 1)">)); } @Test @Order(</span>3<span style="color: rgba(0, 0, 0, 1)">) </span><span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> findByNameTest(){ System.out.println(categoryDao.findByName(</span>"鲜"<span style="color: rgba(0, 0, 0, 1)">)); } @Test @Order(</span>4<span style="color: rgba(0, 0, 0, 1)">) </span><span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> insertCategoryTest(){ Category category</span>=<span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Category(); category.setName(</span>"其它"<span style="color: rgba(0, 0, 0, 1)">); System.out.println(categoryDao.insertCategory(category)); } @Test @Order(</span>5<span style="color: rgba(0, 0, 0, 1)">) </span><span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> updateCategoryTest(){ Category category; category </span>= categoryDao.findById(14<span style="color: rgba(0, 0, 0, 1)">); category.setName(</span>"其它类型"<span style="color: rgba(0, 0, 0, 1)">); System.out.println(categoryDao.updateCategory(category)); } @Test @Order(</span>6<span style="color: rgba(0, 0, 0, 1)">) </span><span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> deleteCategory(){ System.out.println(categoryDao.deleteCategory(</span>14<span style="color: rgba(0, 0, 0, 1)">)); }
}
测试结果
参考映射文件 1:
<?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 接口的全限定名一致 -->
<mapper namespace="com.san.mapper.UserMapper"><span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)"> 通过ID查询用户 </span><span style="color: rgba(0, 128, 0, 1)">--></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">select </span><span style="color: rgba(255, 0, 0, 1)">id</span><span style="color: rgba(0, 0, 255, 1)">="findUserById"</span><span style="color: rgba(255, 0, 0, 1)"> parameterType</span><span style="color: rgba(0, 0, 255, 1)">="int"</span><span style="color: rgba(255, 0, 0, 1)"> resultType</span><span style="color: rgba(0, 0, 255, 1)">="user"</span><span style="color: rgba(0, 0, 255, 1)">></span><span style="color: rgba(0, 0, 0, 1)"> select * from user where id=#{id} </span><span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">select</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)"> 定义sql片段 </span><span style="color: rgba(0, 128, 0, 1)">--></span> <span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)"> sql片段内,可以定义sql语句中的任何内容 </span><span style="color: rgba(0, 128, 0, 1)">--></span> <span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)"> sql片段内,最好不要使用where和select关键字声明在内 </span><span style="color: rgba(0, 128, 0, 1)">--></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">sql </span><span style="color: rgba(255, 0, 0, 1)">id</span><span style="color: rgba(0, 0, 255, 1)">="whereClause"</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)"> if标签:对输入的参数进行判断 </span><span style="color: rgba(0, 128, 0, 1)">--></span> <span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)"> test标签:指定判断的表达式 </span><span style="color: rgba(0, 128, 0, 1)">--></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">if </span><span style="color: rgba(255, 0, 0, 1)">test</span><span style="color: rgba(0, 0, 255, 1)">="user!=null"</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)"> 判断用户名不为空 </span><span style="color: rgba(0, 128, 0, 1)">--></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">if </span><span style="color: rgba(255, 0, 0, 1)">test</span><span style="color: rgba(0, 0, 255, 1)">="user.username!=null and user.username!=''"</span><span style="color: rgba(0, 0, 255, 1)">></span><span style="color: rgba(0, 0, 0, 1)"> and username like '%${user.username}%' </span><span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">if</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)"> 判断性别不为空 </span><span style="color: rgba(0, 128, 0, 1)">--></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">if </span><span style="color: rgba(255, 0, 0, 1)">test</span><span style="color: rgba(0, 0, 255, 1)">="user.sex!=null and user.sex!=''"</span><span style="color: rgba(0, 0, 255, 1)">></span><span style="color: rgba(0, 0, 0, 1)"> and sex=#{user.sex} </span><span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">if</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">if</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)"> 判断集合 </span><span style="color: rgba(0, 128, 0, 1)">--></span> <span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)"> collection:表示pojo中集合属性的属性名称 </span><span style="color: rgba(0, 128, 0, 1)">--></span> <span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)"> item:为遍历出的结果声明一个变量名称 </span><span style="color: rgba(0, 128, 0, 1)">--></span> <span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)"> open:遍历开始时,需要拼接的字符串 </span><span style="color: rgba(0, 128, 0, 1)">--></span> <span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)"> close:遍历结束时,需要拼接的字符串 </span><span style="color: rgba(0, 128, 0, 1)">--></span> <span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)"> separator:遍历中间需要拼接的字符串 </span><span style="color: rgba(0, 128, 0, 1)">--></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">if </span><span style="color: rgba(255, 0, 0, 1)">test</span><span style="color: rgba(0, 0, 255, 1)">="idList!=null"</span><span style="color: rgba(0, 0, 255, 1)">></span><span style="color: rgba(0, 0, 0, 1)"> and id in </span><span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">foreach </span><span style="color: rgba(255, 0, 0, 1)">collection</span><span style="color: rgba(0, 0, 255, 1)">="idList"</span><span style="color: rgba(255, 0, 0, 1)"> item</span><span style="color: rgba(0, 0, 255, 1)">="id"</span><span style="color: rgba(255, 0, 0, 1)"> open</span><span style="color: rgba(0, 0, 255, 1)">="("</span><span style="color: rgba(255, 0, 0, 1)"> close</span><span style="color: rgba(0, 0, 255, 1)">=")"</span><span style="color: rgba(255, 0, 0, 1)"> separator</span><span style="color: rgba(0, 0, 255, 1)">=","</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)"> and id in (#{id},#{id},#{id}) </span><span style="color: rgba(0, 128, 0, 1)">--></span><span style="color: rgba(0, 0, 0, 1)"> #{id} </span><span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">foreach</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">if</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">sql</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)"> 综合查询,查询用户列表 </span><span style="color: rgba(0, 128, 0, 1)">--></span> <span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)"> #{}中的参数名称要和包装pojo中的对象层级一致,并且属性名称要一致 </span><span style="color: rgba(0, 128, 0, 1)">--></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">select </span><span style="color: rgba(255, 0, 0, 1)">id</span><span style="color: rgba(0, 0, 255, 1)">="findUserList"</span><span style="color: rgba(255, 0, 0, 1)"> parameterType</span><span style="color: rgba(0, 0, 255, 1)">="com.san.model.UserQueryvo"</span><span style="color: rgba(255, 0, 0, 1)"> resultType</span><span style="color: rgba(0, 0, 255, 1)">="user"</span><span style="color: rgba(0, 0, 255, 1)">></span><span style="color: rgba(0, 0, 0, 1)"> select * from user </span><span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)"> where标签:默认去掉后面第一个and,如果没有参数,则把自己干掉 </span><span style="color: rgba(0, 128, 0, 1)">--></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">where</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)"> 引入sql片段 </span><span style="color: rgba(0, 128, 0, 1)">--></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">include </span><span style="color: rgba(255, 0, 0, 1)">refid</span><span style="color: rgba(0, 0, 255, 1)">="whereClause"</span><span style="color: rgba(0, 0, 255, 1)">></</span><span style="color: rgba(128, 0, 0, 1)">include</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">where</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">select</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)"> 综合查询,查询用户的总数 </span><span style="color: rgba(0, 128, 0, 1)">--></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">select </span><span style="color: rgba(255, 0, 0, 1)">id</span><span style="color: rgba(0, 0, 255, 1)">="findUserCount"</span><span style="color: rgba(255, 0, 0, 1)"> parameterType</span><span style="color: rgba(0, 0, 255, 1)">="com.san.model.UserQueryvo"</span><span style="color: rgba(255, 0, 0, 1)"> resultType</span><span style="color: rgba(0, 0, 255, 1)">="int"</span><span style="color: rgba(0, 0, 255, 1)">></span><span style="color: rgba(0, 0, 0, 1)"> select count(*) from user </span><span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">where</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">include </span><span style="color: rgba(255, 0, 0, 1)">refid</span><span style="color: rgba(0, 0, 255, 1)">="whereClause"</span><span style="color: rgba(0, 0, 255, 1)">></</span><span style="color: rgba(128, 0, 0, 1)">include</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">where</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">select</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)"> id标签:专门为查询结果中唯一列映射 </span><span style="color: rgba(0, 128, 0, 1)">--></span> <span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)"> result标签:映射查询结果中的普通列 </span><span style="color: rgba(0, 128, 0, 1)">--></span> <span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)"> type标签:返回类型 </span><span style="color: rgba(0, 128, 0, 1)">--></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">resultMap </span><span style="color: rgba(255, 0, 0, 1)">type</span><span style="color: rgba(0, 0, 255, 1)">="user"</span><span style="color: rgba(255, 0, 0, 1)"> id</span><span style="color: rgba(0, 0, 255, 1)">="UserResMap"</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">id </span><span style="color: rgba(255, 0, 0, 1)">column</span><span style="color: rgba(0, 0, 255, 1)">="id_"</span><span style="color: rgba(255, 0, 0, 1)"> property</span><span style="color: rgba(0, 0, 255, 1)">="id"</span><span style="color: rgba(0, 0, 255, 1)">/></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">result </span><span style="color: rgba(255, 0, 0, 1)">column</span><span style="color: rgba(0, 0, 255, 1)">="username_"</span><span style="color: rgba(255, 0, 0, 1)"> property</span><span style="color: rgba(0, 0, 255, 1)">="username"</span><span style="color: rgba(0, 0, 255, 1)">/></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">result </span><span style="color: rgba(255, 0, 0, 1)">column</span><span style="color: rgba(0, 0, 255, 1)">="sex_"</span><span style="color: rgba(255, 0, 0, 1)"> property</span><span style="color: rgba(0, 0, 255, 1)">="sex"</span><span style="color: rgba(0, 0, 255, 1)">/></span> <span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">resultMap</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">select </span><span style="color: rgba(255, 0, 0, 1)">id</span><span style="color: rgba(0, 0, 255, 1)">="findUserRstMap"</span><span style="color: rgba(255, 0, 0, 1)"> parameterType</span><span style="color: rgba(0, 0, 255, 1)">="int"</span><span style="color: rgba(255, 0, 0, 1)"> resultMap</span><span style="color: rgba(0, 0, 255, 1)">="UserResMap"</span><span style="color: rgba(0, 0, 255, 1)">></span><span style="color: rgba(0, 0, 0, 1)"> select id id_,username username_,sex sex_ from user where id=#{id} </span><span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">select</span><span style="color: rgba(0, 0, 255, 1)">></span>
</mapper>
参考映射文件 2:
<?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 命名空间, 作用就是对 sql 进行分类化的管理, 理解为 sql 隔离 注意: 使用 mapper 代理开发时,namespace 有特殊作用 --> <mapper namespace="test"> <!-- 在映射文件中配置很多 sql 语句 --> <!-- 需求:通过 Id 查询用户表的记录 --> <!-- 通过 SELECT 执行数据库查询 id: 标识映射文件中的 sql,称为 statement 的 id; 将 sql 语句封装在 mapperStatement 的对象中,所以 Id 称为 Statement 的 id; parameterType: 指定输入参数的类型, 这里指定 int 型 #{}: 表示一个占位符; #{id}: 其中 Id 表示接收输入的参数,参数名称就是 Id, 如果输入参数是简单类型,#{} 中的参数名可以任意,可以是 value 或者其它名称; resultType: 指定 sql 输出结果所映射的 java 对象类型,select 指定 resultType 表示将单条记录映射成 java 对象。 --> <select id="findUserById" parameterType="int" resultType="com.mybatis.entity.User" > select * from t_user where id=#{id} </select> <!-- 根据用户名称模糊查询用户信息, 可能返回多条数据 resultType: 指定的就是单条记录所映射的 java 类型; ${}: 表示拼接 sql 字符串,将接收到的参数内容不加任何修饰拼接在 sql 中. 使用 ${} 拼接 sql,可能会引起 sql 注入 ${value}: 接收输入参数的内容,如果传入的是简单类型,${} 中只能使用 value --> <select id="findUserByName" parameterType="java.lang.String" resultType="com.mybatis.entity.User" > select * from t_user where username LIKE '%${value}%' </select> <!-- 添加用户 parameterType: 指定输入的参数类型是 pojo(包括用户信息); #{} 中指定 pojo 的属性名称,接收到 pojo 对象的属性值 ,mybatis 通过 OGNL( 类似 struts2 的 OGNL) 获取对象的属性值 --> <insert id="insertUser" parameterType="com.mybatis.entity.User" > <!-- 将 insert 插入的数据的主键返回到 User 对象中; select last_insert_id(): 得到刚 insert 进去记录的主键值,只适用于自增主键; keyProperty: 将查询到的主键值,设置到 parameterType 指定的对象的那个属性 order:select last_insert_id() 执行顺序,相对于 insert 语句来说它的执行顺序。 resultType: 指定 select last_insert_id() 的结果类型; --> <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer"> select last_insert_id() </selectKey> <!-- 使用 mysql 的 uuid(),实现非自增主键的返回。 执行过程: 通过 uuid() 得到主键,将主键设置到 user 对象的 Id 的属性中,其次,在 insert 执行时,从 user 对象中取出 Id 属性值; <selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String"> select uuid() </selectKey> insert into t_user (id,username,birthday,sex,address) values(#{id},#{username},#{birthday},#{sex},#{address}) --> insert into t_user (username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address}) </insert> <!-- 删除用户 根据 ID 删除用户, 需要输入 Id 值 --> <delete id="deleteUser" parameterType="java.lang.Integer"> delete from t_user where id=#{id} </delete> <!-- 更新用户 需要传入用户的 Id 和用户的更新信息 parameterType: 指定 User 对象,包括 Id 和用户的更新信息, 注意:Id 是必须存在的 #{id}: 从输入的 User 对象中获取 Id 的属性值 --> <update id="updateUser" parameterType="com.mybatis.entity.User"> update t_user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id} </update></mapper>
五、基于注解映射实完整数据访问
定义表并添加数据
CREATE TABLE `student` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(10) NOT NULL, `sex` enum('boy','girl','secret') DEFAULT 'secret', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
映射器,StudentDao 接口:
package com.zhangguo.mybatis02.dao;import com.zhangguo.mybatis02.entities.Student;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;import java.util.List;
public interface StudentMapper {
/**
* 根据学生编号获得学生对象
*/
@Select("select id,name,sex from student where id=#{id}")
Student selectStudentById(int id);</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 根据学生姓名获得学生集合 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)"> @Select(</span>"SELECT id,name,sex from student where name like '%${value}%'"<span style="color: rgba(0, 0, 0, 1)">) List</span><Student><span style="color: rgba(0, 0, 0, 1)"> selectStudentsByName(String name); </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 添加学生 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)"> @Insert(</span>"insert into student(name,sex) values(#{name},#{sex})"<span style="color: rgba(0, 0, 0, 1)">) </span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> insertStudent(Student entity); </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 更新学生 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)"> @Update(</span>"update student set name=#{name},sex=#{sex} where id=#{id}"<span style="color: rgba(0, 0, 0, 1)">) </span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> updateStudent(Student entity); </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 删除学生 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)"> @Delete(</span>"delete from student where id=#{id}"<span style="color: rgba(0, 0, 0, 1)">) </span><span style="color: rgba(0, 0, 255, 1)">int</span> deleteStudent(<span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> id);
}
单元测试:
package com.zhangguo.mybatis02.dao;import com.zhangguo.mybatis02.entities.Student;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;import java.util.List;
/**
StudentDao Tester.
@author <Authors name>
@version 1.0
@since <pre>09/26/2018</pre>
*/
public class StudentDaoAnnoTest {
StudentMapper dao;
@Before
public void before() throws Exception {
dao=new StudentDaoAnno();
}@After
public void after() throws Exception {
}/**
- Method: selectStudentById(int id)
*/
@Test
public void testSelectStudentById() throws Exception {
Student entity=dao.selectStudentById(1);
System.out.println(entity);
Assert.assertNotNull(entity);
}/**
- Method: selectStudentsByName(String name)
*/
@Test
public void testSelectStudentsByName() throws Exception {
List<Student> students=dao.selectStudentsByName("e");
System.out.println(students);
Assert.assertNotNull(students);
}/**
Method: insertStudent
*/
@Test
public void testInsertStudent() throws Exception {
Student entity=new Student();
entity.setName("张小强");
entity.setSex("boy");Assert.assertEquals(1,dao.insertStudent(entity));
}/**
Method: updateStudent
*/
@Test
public void testUpdateStudent() throws Exception {
Student entity=dao.selectStudentById(7);
entity.setName("张美丽");
entity.setSex("girl");Assert.assertEquals(1,dao.updateStudent(entity));
}/**
- Method: deleteStudent
*/
@Test
public void testDeleteStudent() throws Exception {
Assert.assertEquals(1,dao.deleteStudent(7));
}
}
测试结果:
参考映射文件:
package com.winterchen.mapper;import com.winterchen.domain.User;
import org.apache.ibatis.annotations.*;import java.util.List;
import java.util.Map;/**
User 映射类
Created by Administrator on 2017/11/24.
*/
@Mapper
public interface UserMapper {@Select("SELECT * FROM T_USER WHERE PHONE = #{phone}")
User findUserByPhone(@Param("phone")String phone);@Insert("INSERT INTO T_USER(NAME, PASSWORD, PHONE) VALUES(#{name}, #{password}, #{phone})")
int insert(@Param("name") String name, @Param("password") String password, @Param("phone")String phone);@Insert("INSERT INTO T_USER(NAME, PASSWORD, PHONE) VALUES(" +
"#{name, jdbcType=VARCHAR}, #{password, jdbcType=VARCHAR}, #{phone, jdbcType=VARCHAR})")
int insertByMap(Map<String, Object> map);@Insert("INSERT INTO T_USER(NAME, PASSWORD, PHONE) VALUES(#{name}, #{password}, #{phone})")
int insertByUser(User user);@Update("UPDATE T_USER SET NAME = #{name}, PASSWORD = #{password} WHERE PHONE = #{phone}")
void update(User user);@Delete("DELETE FROM T_USER WHERE ID = #{id}")
void delete(Integer id);@Results({
@Result(property = "name", column = "NAME"),
@Result(property = "password", column = "PASSWORD"),
@Result(property = "phone", column = "PHONE")
})
@Select("SELECT NAME, PASSWORD, PHONE FROM T_USER")
List<User> findAll();}
六、说明与注意事项
6.1、parameterType 和 resultType 的区别
parameterType: 在映射文件中通过 parameterType 指定输入参数的类型。
resultType: 在映射文件中通过 resultType 指定输出结果的类型
6.2、#{} 和 ${} 的区别
#{}
#{} 表示一个占位符号,#{} 接收输入参数,类型可以是简单类型,pojo、hashmap;
如果接收简单类型,#{} 中可以写成 value 或其它名称;
#{} 接收 pojo 对象值,通过 OGNL 读取对象中的属性值,通过属性. 属性. 属性... 的方式获取对象属性值。使用 #{} 意味着使用的预编译的语句,即在使用 jdbc 时的 preparedStatement,sql 语句中如果存在参数则会使用? 作占位符,我们知道这种方式可以防止 sql 注入,并且在使用 #{} 时形成的 sql 语句,已经带有引号,例,select? * from table1 where id=#{id}? 在调用这个语句时我们可以通过后台看到打印出的 sql 为:select * from table1 where id='2' 加入传的值为 2. 也就是说在组成 sql 语句的时候把参数默认为字符串。
${}
表示一个拼接符号,会引用 sql 注入,所以不建议使用 ${};
${} 接收输入参数,类型可以是简单类型,pojo、hashmap;
如果接收简单类型,${} 中只能写成 value;
${} 接收 pojo 对象值,通过 OGNL 读取对象中的属性值,通过属性. 属性. 属性... 的方式获取对象属性值。
使用${} 时的 sql 不会当做字符串处理,是什么就是什么,如上边的语句:select * from table1 where id=${id} 在调用这个语句时控制台打印的为:select * from table1 where id=2 ,假设传的参数值为 2
从上边的介绍可以看出这两种方式的区别,我们最好是能用 #{} 则用它,因为它可以防止 sql 注入,且是预编译的,在需要原样输出时才使用${},如,
select * from ${tableName} order by ${id} 这里需要传入表名和按照哪个列进行排序 ,加入传入 table1、id 则语句为:select * from table1 order by id
如果是使用 #{} 则变成了 select * from 'table1' order by 'id' 我们知道这样就不对了。
6.3、selectOne()和 selectList() 的区别
selectOne 表示查询出一条记录进行映射。如果使用 selectOne 可以实现使用 selectList 也可以实现(list 中只有一个对象),如果查询结果为多条则会报错。
selectList 表示查询出一个列表(多条记录)进行映射,可以是 0 到 n 条记录返回。
6.4、映射器选择 XML 还是注解
以下是 MyBatis 官网对 Mapper Annotations 的解释:
Mapper Annotations
Since the very beginning, MyBatis has been an XML driven framework. The configuration is XML based, and the Mapped Statements are defined in XML. With MyBatis 3, there are new options available. MyBatis 3 builds on top of a comprehensive and powerful Java based Configuration API. This Configuration API is the foundation for the XML based MyBatis configuration, as well as the new Annotation based configuration. Annotations offer a simple way to implement simple mapped statements without introducing a lot of overhead.
NOTE : Java Annotations are unfortunately limited in their expressiveness and flexibility. Despite a lot of time spent in investigation, design and trials, the most powerful MyBatis mappings simply cannot be built with Annotations – without getting ridiculous that is. C# Attributes (for example) do not suffer from these limitations, and thus MyBatis.NET will enjoy a much richer alternative to XML. That said, the Java Annotation based configuration is not without its benefits.
翻译:
(最初 MyBatis 是基于 XML 驱动的框架。MyBatis 的配置是基于 XML 的,语句映射也是用 XML 定义的。对于 MyBatis3,有了新的可选方案。MyBatis3 是建立在全面且强大的 Java 配置 API 之上的。 该配置 API 是 MyBatis 基于 XML 配置的基础,也是基于注解配置的基础。注解提供了简单的方式去实现简单的映射语句,不需要花费大量的开销。
注意:很不幸的是,java 注解在表现和灵活性上存在限制。虽然在调研、设计和测试上花费了很多时间,但是最强大的 MyBatis 映射功能却无法用注解实现。这没有什么可笑的。举例来说,C# 的特性就没有这个限制,所以 MyBatis.NET 能拥有一个功能丰富的多的 XML 替代方案。所以,Java 基于注解的配置是依赖于其自身特性的。)
长远来看建议选择 XML 作为映射器
http://www.mybatis.org/mybatis-3/java-api.html
6.5、开启热部署
springboot 提供了热部署,所谓热部署就是当你修改了代码,不用重新启动服务器部署应用,而你只要重新编译一下当前项目,修改的代码就自动生效了,这样极大的节省了部署应用时间,对于开发体验还是有很大提升的,因为我们不用一修改了代码就部署应用,因为部署应用也是会花费很长时间的。在 idea 中点击如下按钮:
1. 首先在 pom 文件中添加依赖和插件
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> <scope>true</scope> </dependency><span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">plugins</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">plugin</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">></span>org.springframework.boot<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">groupId</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">></span>spring-boot-maven-plugin<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">artifactId</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">configuration</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">fork</span><span style="color: rgba(0, 0, 255, 1)">></span>true<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">fork</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">configuration</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">plugin</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">plugins</span><span style="color: rgba(0, 0, 255, 1)">></span></pre>
2. 打开 settings 进行配置
3.window 系统中摁快捷键 Shift+Ctrl+Alt+/
4. 选择 compiler.automake.allow.when.app.running 这一项
5. 重启 IDEA,验证 Sringboot 热部署是否成功。
七、视频
https://www.bilibili.com/video/av32447485/
八、示例
https://git.coding.net/zhangguo5/MyBatis02.git
九、作业
1、请使用 MyBatis 完成一个用户管理的数据访问功能,要求实现根据用户名查询用户对象(id,username,password,name,email,state)功能,表中至少 5 个字段。
2、请分别使用 XML 与注解两种方式实现对象用户表(Users)的单条记录查询、多条记录查询、增加、修改与删除功能,要求单元测试通过。
3、添加用户成功后返回用户的编号,而不是影响行数。(选作)
4、实现多个条件组合查询,类似在电商平台购物可以选择 0-n 个条件,且可以自由组合。(选作)
5、实现分页功能。(选作)