MyBatis
MyBatis简介
-
定义
- 它是一款优秀的持久层框架,用于简化JDBC开发
- 它原来是Apache的一个开源项目iBatis,后来改名为MyBatis
- 中文官网:https://mybatis.org/mybatis-3/zh_CN/index.html
-
JaveEE三层架构
- 表现层(做页面展示)
- 业务层(做逻辑处理)
- 持久层(负责将数据保存到数据库的那一层代码。即做数据持久化的)
-
框架
- 框架是一个半成品软件,是一套可重用的、通用的、软件基础代码模型
- 在框架的基础之上构建软件编写更加高效、规范、通用、可扩展
-
JDBC缺点
- 硬编码
- 将一些字符串信息写到代码中,且这些字符串信息后续可能会有所改变,比如:注册驱动和获取连接的部分
- 操作繁琐
- 手动设置参数:比如利用
PreparedStatement
类中的public void setXxx(参数1,参数2)
方法来对SQL语句中的?
进行赋值时,若?较多,则需要调用多次该方法,这样就比较繁琐 - 手动封装
ResultSet
结果集:封装结果集时需要将结果集中需要的数据拿出来然后放到对象中,最后将对象放到集合中,比较繁琐
- 手动设置参数:比如利用
MyBatis可将造成硬编码部分的代码写到配置文件中;将操作繁琐部分的代码简化,如下图所示
- 硬编码
-
MyBatis优点
- 是一款优秀的持久层框架
- 支持自定义 SQL、存储过程以及高级映射
- 免除了几乎所有的JDBC代码以及设置参数和获取结果集的工作
- 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
-
MyBatis两种版本
- 普通版------MyBatis
- 增强版------MyBatis-Plus
MyBatis环境搭建
MyBatis快速入门(可跟着MyBatis官网的入门来操作)
示例:查询user表中所有的数据
-
步骤
-
第一步:创建mybatis数据库并在该数据库中创建tb_user表,添加数据.
#创建数据库 CREATE DATABASE mybatis; USE mybatis; DROP TABLE IF EXISTS tb_user; #创建表 CREATE TABLE tb_user ( id INT PRIMARY KEY AUTO_INCREMENT, username varchar(20), password varchar(20), gender char(1)L, addr varchar(30), ); #向表中添加数据 INSERT INTO `tb_user` VALUES (1, 'zhangsan', '123', '男', '北京'); INSERT INTO `tb_user` VALUES (2, '李四', '234', '女', '天津'); INSERT INTO `tb_user` VALUES (3, '王五', '11', '男', '西安');
-
第二步:创建模块(该模块是Maven的项目),导入坐标
-
创建MyBatis的Maven模块
-
导入坐标:从官网的入门中可看到将图示以来写到pom.xml文件中即可,其中x.x.x为MyBatis的版本号。MyBatis导入后也要导入MySQL的坐标依赖以及其它多个需要用到的相关依赖(依赖导入方法可详见Maven部分),具体代码详见下述
<dependencies> <!--MyBatis依赖--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.16</version> </dependency> <!--mysql驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency> <!--Junit单元测试--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> <!--添加slf4j日志api--> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>2.0.12</version> </dependency> <!--添加logback-classic依赖--> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.5.6</version> <scope>test</scope> </dependency> <!--添加logback-core依赖--> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>1.4.14</version> </dependency> </dependencies>
-
-
第三步:编写SQL映射文件(统一管理sql语句,解决JDBC的硬编码问题)
-
写sql映射文件(对应官网 探究已映射的 SQL 语句 ):在该Maven项目的源代码配置文件目录(即main包下的resources目录下)创建一个
userMapper.xml
文件,然后将官网的简单示例代码复制到该文件中进行对应修改,该文件代码简单示例如下sql映射文件命名规范 :
tableNameMapper.xml
即要操作的表名后加上Mapper,比如我现在要用该映射文件操作stu表,则sql映射文件名为stuMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- namespace:名称空间 --> <mapper namespace="test"> <!-- id:为sql语句的唯一标识 resultType:为对应sql语句执行完毕后返回结果的类型 假设我要将其执行的结果封装到一个类中则此时该属性后的值就应该为全类名 由于我现在是对user表进行操作的,所以我就将执行结果保存到一个user类中 所以就需要先在Maven项目的源代码java文件目录(即main包下的java目录下)创建一个user类,该类中的属性和方法随后再写即可,此处先创建出来是为了让resultType有属性值 然后resultType="at.guigu.pojo.User" --> <select id="selectAll" resultType="at.guigu.pojo.User"> select * from tb_user; </select> </mapper>
注意:
若要将sql语句的查询结果封装成一个对象则该数据封装的对象一般放在名为
pojo
的包中 若嫌弃resultType的属性值太长造成冗余,可为其设置别名
-
-
第四步:编写MyBatis核心配置文件(替换连接信息,解决JDBC的硬编码问题)
-
从官网的入门中可看到将依赖导入到pom.xml文件后需要构建
SqlSessionFactory
(可在XML中构建,也可不使用XML构建) -
在XML中构建
SqlSessionFactory
的步骤如下:-
在该Maven项目的源代码配置文件目录(即main包下的resources目录下)创建一个
mybatis-config.xml
文件,然后将官网的简单示例粘贴进去即可,官网代码示例如下所示<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "https://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <!--数据库 连接信息--> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <!--加载SQL映射文件:传入sql映射文件的路径--> <mapper resource="UserMapper.xml"/> </mappers> </configuration>
由于
mybatis-config.xml
文件和UserMapper.xml文件均在一个目录下,属于同一级,所以在传入sql映射文件路径时直接写入该文件名即可
-
-
-
第五步:编码
-
定义POJO类(对于数据封装的对象一般放在名为
pojo
的包中)在刚刚创建的实体类User类中写入自己想要的sql语句执行后的结果的属性及方法
package at.guigu.pojo; public class User { private int id; private String username; private String password; private String gender; private String addr; public User() { } public User(int id, String username, String password, String gender, String addr) { this.id = id; this.username = username; this.password = password; this.gender = gender; this.addr = addr; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } public String getAddr() { return addr; } public void setAddr(String addr) { this.addr = addr; } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", gender='" + gender + '\'' + ", addr='" + addr + '\'' + '}'; } }
-
源代码java文件目录创建类:加载核心配置文件,获取
SqlSessionFactory
对象;然后获取SqlSession对象,执行SQL语句;随后释放资源-
类中来加载MyBatis核心配置文件并获取
SqlSessionFactory
对象,获取该对象代码如下(直接从官网复制即可,不需记忆)://配置mybatis-config.xml文件路径,注意:若该文件直接在resources目录下,则直接写文件名即可 String resource = "org/mybatis/example/mybatis-config.xml"; //利用Resources类中的静态方法将配置文件加载到内存 InputStream inputStream = Resources.getResourceAsStream(resource); 获取SqlSessionFactory对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
注意 :在配置mybatis-config.xml文件路径时,若该文件直接在resources目录下,则直接写文件名即可(即
String resource = "mybatis-config.xml";
),否则运行时会报错:Cannot invoke "org.apache.ibatis.session.SqlSessionFactory.openSession()" because "this.sqlSessionFactory" is null
,此时页面响应状态码为500
,如图所示
-
获取SqlSession对象,执行SQL语句,使用的接口、类及方法如下
SqlSessionFactory接口的方法 解释 SqlSession openSession()
获取SqlSession对象 SqlSession
接口的方法解释 <E> List<E> selectList(String statement)
执行sql语句。参数为 namespace.id
代码如下:
package at.guigu; import at.guigu.pojo.User; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; import java.util.List; public class MyBatisDemoOne { public static void main(String[] args) throws Exception { //1 加载核心配置文件,获取`SqlSessionFactory`对象 //1.1 配置mybatis-config.xml文件路径,由于该文件直接在resources目录下,所以直接写文件名即可 String resource = "mybatis-config.xml"; //1.2 利用Resources类中的静态方法将配置文件加载到内存 InputStream inputStream = Resources.getResourceAsStream(resource); //1.3 获取SqlSessionFactory对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //2 获取SqlSession对象,执行SQL语句 //2.1 获取SqlSession对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //2.2 执行sql语句 List<User> users = sqlSession.selectList("test.selectAll"); System.out.println(users); } }
注意 :在该代码中,
<E> List<E> selectList(String statement)
方法的参数仍会造成硬编码问题,因为不同的sql语句有不同的namespace和id,为解决该问题,可详见Mapper代理开发部分 -
-
-
问题解决
SQL映射文件的标红警告提示
-
产生原因
- idea和数据库未建立连接,导致idea不能识别表信息从而标红
-
解决方法:在idea中配置mysql数据库连接信息
-
按图示操作即可
-
-
该方法不仅是SQL映射文件的标红警告提示的方法,还是可以在idea中直接操作数据库的方法
-
idea与数据库连接后可在idea中新建写sql语句的console面板来操作数据库,如图所示
idea中写sql语句时很强大,自动提示和补全比较牛
-
Mapper代理开发
-
作用
- 解决原生方式中的硬编码
- 简化后期执行SQL
-
Mapper代理开发步骤及要求
-
第一步:定义与SQL映射文件同名的Mapper接口(Maven项目中接口统一在一个包中,此处假设mapper包中写接口类),并且将Mapper接口和SQL映射文件放置在同一个目录下
-
定义与SQL映射文件同名的Mapper接口:由于博主的sql映射文件名为UserMapper.xml,所以接口名为UserMapper
-
将Mapper接口和SQL映射文件放置在同一个目录下:在该Maven项目的源代码配置文件目录(即main包下的resources目录下)创建多层目录,多层目录对应Mapper接口所在的多层包,比如我的UserMapper接口在
at.guigu.mapper
包下,则此时就在resources目录下创建多层目录格式为at/guigu/mapper
,然后将sql映射文件UserMapper.xml拖入到该mapper目录下 -
右键
UserMapper.xml
SQL映射文件→Copy Path/Reference
→Path From Source Root
,复制UserMapper.xml
SQL映射文件现在的路径,然后更改mybatis-config.xml
MyBatis核心配置文件中sql映射文件的路径即可。
-
-
第二步:设置SQL映射文件UserMapper.xml的
namespace
属性为Mapper接口的全限定名,修改后的代码如下所示<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- namespace:名称空间 --> <mapper namespace="at.guigu.mapper.UserMapper"> <!-- id:为sql语句的唯一标识 resultType:为对应sql语句执行完毕后返回结果的类型 假设我要将其执行的结果封装到一个类中则此时该属性后的值即为全类名 由于我现在是对user表进行操作的,所以我就将执行结果保存到一个user类中 所以就需要先在Maven项目的源代码java文件目录(即main包下的java目录下)创建一个user类 然后resultType="at.guigu.pojo.User" --> <select id="selectAll" resultType="at.guigu.pojo.User"> select * from tb_user; </select> </mapper>
-
第三步:在Mapper接口UserMapper中定义方法,方法名就是SQL映射文件中sql语句的id,并保持参数类型和返回值类型一致
- 由SQL映射文件可知,方法名为selectAll,返回值类型为User。然后判断对应的sql语句的返回结果是一个结果还是多个结果,若是一个则在接口中定义一个对象即可;若是多个则需定义集合,将来执行sql语句后的结果就会直接放在集合中
-
第四步:编码
- 通过SqlSession的getMapper方法获取Mapper接口的代理对象
- 调用对应方法完成sql语句的执行
-
第三四步代码如下
package at.guigu.pojo; import at.guigu.mapper.UserMapper; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; import java.util.List; //MyBatis代理开发 public class MyBatisDemoTwo { public static void main(String[] args) throws IOException { //1 加载核心配置文件,获取`SqlSessionFactory`对象 //1.1 配置mybatis-config.xml文件路径,由于该文件直接在resources目录下,所以直接写文件名即可 String resource = "mybatis-config.xml"; //1.2 利用Resources类中的静态方法将配置文件加载到内存 InputStream inputStream = Resources.getResourceAsStream(resource); //1.3 获取SqlSessionFactory对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //2 获取SqlSession对象,执行SQL语句 //2.1 获取SqlSession对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //2.2 获取Mapper接口UserMapper的代理对象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); //2.3 执行sql语句 List<User> users = userMapper.selectAll(); System.out.println(users); } }
-
-
注意:若Mapper接口名称和SQL映射文件名称相同并且在同一目录下则可使用包扫描的方式简化SQL映射文件的加载
-
由Mapper代理开发的要求可知,SQL映射文件的名称与对应Mapper接口的名称一定相同且Mapper接口与对应的SQL映射文件均在同一个目录下
- 此处在同一个目录下是近似看作在同一个目录下,以博主的项目代码为例:UserMapper接口在java目录下的at.guigu.mapper包下,而对应的SQL映射文件在resources目录下的at.guigu.mapper目录下,由于目录结构相同,所以可近似看作在同一个目录下。此时MyBatis核心配置文件中sql映射文件的路径,就可写为如下形式:
<mappers> <!--加载SQL映射文件--> <!-- <mapper resource="at/guigu/mapper2/UserMapper.xml"> --> <package name="at.guigu.mapper"/> </mappers>
-
优点:以后项目大的话就会有多个SQL映射文件,原来的方式(即
<mapper resource="at/guigu/mapper2/UserMapper.xml">
)只是一次加载一个映射文件,而现在的新方式(即<package name="at.guigu.mapper"/>
)是加载该目录下的所有映射文件,不需要重复使用<mapper resource="SQL映射文件路径">
来加载SQL映射文件
-
MyBatis核心配置文件(mybatis-config.xml)
-
该核心配置文件中可进行的配置内容详见官网中的XML配置
-
部分标签解释如下(其它标签可详见官网)
标签 解释 typeAliases
设置包下单个类的类型别名或设置包下所有类的类型别名 environments
配置数据库连接环境信息,可配置多个environment,并通过default属性来切换不同的environment mappers
定义 SQL 映射文件
设置别名
在MyBatis核心配置文件中设置别名(即mybatis-config.xml)
假设我在at.guigu.pojo包下由有多个实体类,实体类的全类名如下:
at.guigu.pojo.Author
at.guigu.pojo.Blog
at.guigu.pojo.Comment
at.guigu.pojo.Post
at.guigu.pojo.Section
at.guigu.pojo.Tag
-
给单个类的全类名设置类型别名方式一
<typeAliases> <typeAlias alias="Author" type="at.guigu.pojo.Author"/> <typeAlias alias="Blog" type="at.guigu.pojo.Blog"/> <typeAlias alias="Comment" type="at.guigu.pojo.Comment"/> <typeAlias alias="Post" type="at.guigu.pojo.Post"/> <typeAlias alias="Section" type="at.guigu.pojo.Section"/> <typeAlias alias="Tag" type="at.guigu.pojo.Tag"/> </typeAliases>
-
设置完成后在SQL映射文件中定义resultType属性的属性值时即可不用写类的全类名,写其别名即可
<select id="selectById" resultType="Author"></select> <select id="selectById" resultType="Blog"></select> <select id="selectById" resultType="Comment"></select> <select id="selectById" resultType="Post"></select> <select id="selectById" resultType="Section"></select> <select id="selectById" resultType="Tag"></select>
-
博主自己的示例图如下
-
-
给单个类的全类名设置类型别名方式二:利用
@Alias("别名")
注解为类的全类名设置类型别名- 博主自己的示例图如下
-
给包下的所有类的全类名设置类型别名
<typeAliases> <package name="at.guigu.pojo"/> </typeAliases>
-
此时相当于给at.guigu.pojo包下的所有类设置类型别名,该包下所有类的类型别名为对应类的首字母小写的类名,比如
Author的类型别名为author Blog的类型别名为blog Comment的类型别名为comment Post的类型别名为post Section的类型别名为section Tag的类型别名为tag
-
博主自己的示例图如下
-
-
注意
typeAliases
标签在MyBatis核心配置文件中的顺序必须按照官网的顶层结构来写,写在environments
标签之前- 若在MyBatis的核心配置文件中给某个类设置了别名后又利用注解给该类设置了别名,则会以注解设置的别名为准
MyBatis参数传递注意形式
- MyBatis接口的方法可接收各种参数,它的底层会对不同的参数进行不同的封装处理(MyBatis底层提供了一个类
ParamNameResolver
来封装参数),参数总体有两类- 单个参数
- POJO类型:将sql语句的查询结果封装成一个对象则该数据封装的对象一般放在名为
pojo
的包中。 - Map集合、Collection集合、List集合、Array数组、其他类型(即基本数据类型或String类型等)
- POJO类型:将sql语句的查询结果封装成一个对象则该数据封装的对象一般放在名为
- 多个参数
- 多个参数时要利用注解
@Param("参数")
来将对应的方法参数放到SQL映射文件中SQL语句对应的参数占位符
- 多个参数时要利用注解
- 单个参数
- 需要加
@Param("参数")
注解的有- 单个参数
- Collection集合、List集合、Array(数组):它们几个都会被封装到Map集合中
- 多个参数
- 单个参数
MyBatis注解开发
-
特点:
-
传统的MyBatis是将SQL语句写在SQL映射文件中,而MyBatis注解开发是将SQL语句写在注解中
-
使用注解开发要比使用配置文件开发更方便
-
注解直接在对应接口的方法上,如图所示
-
-
用到的注解
注解 解释 @Select
选择数据 @Insert
插入数据 @Update
修改或更新表中数据 @Delete
删除表中数据 -
注意
- 注解是适用于简单的SQL语句,若要做一些复杂的SQL语句则最好使用XML映射
- 即注解完成简单功能,配置文件完成复杂功能
MyBatis案例
- 示例:完成品牌数据的增删改查操作
- 要完成的功能列表清单
- 查询
- 查询所有数据
- 查看详情
- 条件查询
- 添加
- 修改
- 修改全部字段
- 修改动态字段
- 删除
- 删除一个
- 批量删除
- 查询
环境准备
-
在mybatis数据库中创建数据库表tb_brand
DROP TABLE IF EXISTS tb_brand; -- 创建品牌表brand CREATE TABLE IF NOT EXISTS tb_brand ( -- id 主键 id int PRIMARY KEY auto_increment, -- 品牌名称 brand_name VARCHAR(20), -- 企业名称 company_name VARCHAR(20), -- 排序字段 ordered INT, -- 描述信息 description VARCHAR(100), -- 状态:0:禁用 1:启用 status INT ); -- 添加数据 INSERT INTO tb_brand(brand_name, company_name, ordered, description, status) VALUES ('三只松鼠', '三只松鼠股份有限公司', 5, '好吃不上火', 0), ('华为', '华为技术有限公司', 100, '华为致力于构建万物互联的世界', 1), ('小米', '小米科技有限公司', 50, 'Are you OK', 1); SELECT * FROM tb_brand;
-
创建实体类Brand------若要将sql语句的查询结果封装成一个对象则该数据封装的对象一般放在名为
pojo
的包中package at.guigu.pojo; public class Brand { // id 主键 private Integer id; // 品牌名称 private String brandName; // 企业名称 private String companyName; // 排序字段 用于将某个品牌显示在最前面让消费者看到 private Integer ordered; // 描述信息 private String description; // 状态:0:禁用 1:启用 private Integer status; public Brand(Integer id, String brandName, String companyName, Integer ordered, String description, Integer status) { this.id = id; this.brandName = brandName; this.companyName = companyName; this.ordered = ordered; this.description = description; this.status = status; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getBrandName() { return brandName; } public void setBrandName(String brandName) { this.brandName = brandName; } public String getCompanyName() { return companyName; } public void setCompanyName(String companyName) { this.companyName = companyName; } public Integer getOrdered() { return ordered; } public void setOrdered(Integer ordered) { this.ordered = ordered; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public Integer getStatus() { return status; } public void setStatus(Integer status) { this.status = status; } @Override public String toString() { return "Brand{" + "id=" + id + ", brandName='" + brandName + '\'' + ", companyName='" + companyName + '\'' + ", ordered=" + ordered + ", description='" + description + '\'' + ", status=" + status + '}'; } }
-
测试用例------写在测试代码java文件目录(即Maven项目的test目录下的java目录下)
-
安装MyBatisX插件
-
它是一款基于IDEA的快速开发插件,为效率而生
-
功能
-
XML和接口方法互相跳转
-
安装完成后如图所示,接口中的方法及SQL映射文件中会对应出现小鸟,单击小鸟可实现XML和接口中方法的自动跳转
-
-
根据接口方法生成statement(即SQL语句)
- 若接口中新写一个方法,该方法在SQL映射文件中并未定义SQL语句,则此时可借助该插件实现自动生成statement(即SQL语句),如下图所示
-
-
实现功能列表清单
查询
查询所有数据
-
代码略详见快速入门
-
问题: 由于封装数据的类中属性名和数据库中表的字段名不一致(如图一)可能会造成最终结果显示不出来(如图二)
即数据库表的字段名称和实体类的属性名称不一样则不能自动封装数据
-
解决方式一: 在SQL映射文件中对SQL语句中的字段名称起别名(即将数据库表中的字段名称的别名设置为与实体类的属性名称对应一样),如下代码所示
- 该方法有个缺点:即每次查询都需要设置别名,所以详见解决方式二
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--namespace:名称空间--> <mapper namespace="at.guigu.mapper.BrandMapper"> <!-- <select id="selectAll" resultType="at.guigu.pojo.User">--> <select id="selectAll" resultType="brand"> <!-- select * from tb_brand; 改为如下形式--> select id, brand_name brandName, company_name companyName, ordered, description, status from tb_brand; </select> <!-- <select id="selectById" resultType="at.guigu.pojo.User">--> <select id="selectById" resultType="brand"> select * from tb_brand where id = #{id}; </select> </mapper>
-
解决方式二: 在SQL映射文件中定义SQL片段并引用SQL片段
- 该方法缺点:不灵活,若SQL片段中是所有的字段名,此时假如只需要查询若干个字段名怎么办
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--namespace:名称空间--> <mapper namespace="at.guigu.mapper.BrandMapper"> <!--定义SQL片段--> <sql id = "brand_column">id, brand_name brandName, company_name companyName, ordered, description, status</sql> <select id="selectAll" resultType="brand"> <!--引入SQL片段--> select <include refid="brand_column" /> from tb_brand; </select> <!-- <select id="selectById" resultType="at.guigu.pojo.User">--> <select id="selectById" resultType="brand"> select * from tb_brand where id = #{id}; </select> </mapper>
-
解决方式三: 利用
<resultMap id="" type="">映射配置</resultMap>
标签id
:唯一标识此resultMap
的 ID,一般命名为目标Java类名+ResultMap,即id = “brandResultMap”type
:指定结果映射的目标 Java 类的完全限定名(全类名)(注意:支持使用别名)。MyBatis 会将查询结果映射到这个类的实例。此处type = "at.guigu.pojo.Brand"
即type = "brand"
-
解释:用于定义一个 结果映射 ,指定如何将 SQL 查询结果集中的列映射到 Java 对象的属性。
-
可用的标签体
标签体 解释 <id property= "Java类属性名" column="SQL字段名">
标识主键列 <result property="Java类属性名" column="SQL字段名"/>
标识非主键列 <association property="Java类属性名" javaType="java类全类名"></association>
处理嵌套对象的映射,可以嵌套 <resultMap>
标签或者其他<result>
,<id>
标签<collection property="Java类属性名" ofType="com.example.Order"></collection>
处理嵌套集合的映射,可以嵌套 <resultMap>
标签或者其他<result>
,<id>
标签。(注意:ofType的属性值为:集合中元素的 Java 类型。) -
具体过程详见如下xml文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--namespace:名称空间--> <mapper namespace="at.guigu.mapper.BrandMapper"> <!--结果映射--> <resultMap id="brandResultMap" type="brand"> <!--由于id为主键,且数据库中的字段名和对应结果映射的目标类中的属性名一样,所以此处不需要主键映射,只需进行非主键映射即可--> <result column="brand_name" property="brandName"/> <result column="company_name" property="companyName"/> </resultMap> <!--将resultType改为resultMap且属性值为resultMap标签中id的属性值--> <select id="selectAll" resultMap="brandResultMap"> select * from tb_brand; </select> </mapper>
-
-
查看详情
-
解释
查看详情是通过返回对应的序号给后台,然后后台通过id找到对应数据传输到前端供浏览者获取信息,由于一个id对应一个品牌的信息,所以返回的是一个对象
Mapper接口中定义的方法为
Brand selectById(int id);
SQL映射文件中的SQL语句为
<select id="selectById" parameterType="int" resultMap="brandResultMap"> select * from tb_brand where id = #{id}; <!--#{id}用于接收传入的参数,相当于 select * from tb_brand where id = ?; --> </select>
-
注意:参数占位符来代替要传入SQL语句的参数
-
#{}
:可放置SQL注入 -
${}
:存在SQL注入问题(最好不要用这个参数占位符,只有在表名或列名不固定的情况下才使用该参数占位符)select * from tb_brand where id = ${id}; <!--假设传入参数为1则相当于select * from tb_brand where id = 1;-->
-
-
注意2:
<select></select>
标签中的属性parameterType
可设置传入的参数类型 ,它可以省略不写 -
注意3:特殊字符处理(可详见day18XML知识点)
-
利用转义字符:即XML文件中的特殊字符
<
------<
------小于>
------>
------大于&
------&
------和号'
------'
------单引号"
------"
------引号
<!-- 查看详情--> <select id="selectById" parameterType="int" resultMap="brandResultMap"> select * from tb_brand where id > #{id}; </select>
-
将特殊字符写在
CDATA
区<!-- 查看详情--> <select id="selectById" parameterType="int" resultMap="brandResultMap"> select * from tb_brand where id <![CDATA[ > ]]> #{id}; </select>
-
特殊字符比较少时用转义字符,反之则用CDATA区
-
-
-
完整代码如下
-
SQL映射文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--namespace:名称空间--> <mapper namespace="at.guigu.mapper.BrandMapper"> <!--结果映射--> <resultMap id="brandResultMap" type="brand"> <!--由于id为主键,且数据库中的字段名和对应结果映射的目标类中的属性名一样,所以此处不需要主键映射,只需进行非主键映射即可--> <result column="brand_name" property="brandName" /> <result column="company_name" property="companyName" /> </resultMap> <!--查询所有数据--> <select id="selectAll" resultMap="brandResultMap"> select * from tb_brand; </select> <!-- 查看详情--> <select id="selectById" parameterType="int" resultMap="brandResultMap"> select * from tb_brand where id = #{id}; </select> </mapper>
-
接口
package at.guigu.mapper; import at.guigu.pojo.Brand; import org.apache.ibatis.annotations.Param; import java.util.List; import java.util.Map; public interface BrandMapper { //查询所有数据 List<Brand> selectAll(); //查看详情 Brand selectById(int id); }
-
测试代码
package at.guigu.test; import at.guigu.mapper.BrandMapper; import at.guigu.pojo.Brand; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.List; import java.util.Map; public class MyBatisTest { private static BrandMapper getBrandMapper() throws IOException { //1 加载核心配置文件,获取`SqlSessionFactory`对象 //1.1 配置mybatis-config.xml文件路径,由于该文件直接在resources目录下,所以直接写文件名即可 String resource = "mybatis-config.xml"; //1.2 利用Resources类中的静态方法将配置文件加载到内存 InputStream inputStream = Resources.getResourceAsStream(resource); //1.3 获取SqlSessionFactory对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //2 获取SqlSession对象,执行SQL语句 //2.1 获取SqlSession对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //2.2 获取Mapper接口UserMapper的代理对象 BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); return brandMapper; } //查询所有数据 @Test public void testSelectAll() throws IOException { //获取Mapper接口对象 BrandMapper brandMapper = getBrandMapper(); //2.3 执行sql语句 List<Brand> brands = brandMapper.selectAll(); System.out.println(brands); } //查看详情 @Test public void testSelectById() throws IOException { int id = 1;//接收前端传回的序号 //获取Mapper接口对象 BrandMapper brandMapper = getBrandMapper(); //2.3 执行sql语句 Brand brands = brandMapper.selectById(id); System.out.println(brands); } }
-
普通多条件查询
-
编写接口方法的方式有三种
- 方式一散装参数接收: 定义含多个参数的接口方法时,要通过
@Param("参数")
注解来将对应的方法参数放到SQL映射文件中SQL语句对应的参数占位符上,其中注解参数名称要和SQL映射文件中SQL语句中的参数占位符名称对应一致 - 方式二对象参数接收: 若三个参数都属于同一个对象的话则可以将它们封装成一个对象,然后将对象作为一个参数,即
List<Brand> selectByCondition(Brand brand)
,此时SQL映射文件中SQL语句对应的参数占位符会自动从对象中找对应的值(前提是对象中的属性名称要和SQL语句中参数占位符名称一致) - 方式三Map集合参数接收: 可将以上三个参数封装到Map集合中作为键,并给其对应的键值,然后将Map集合作为一个参数,即
List<Brand> selectByCondition(Map map)
,此时SQL映射文件中SQL语句对应的参数占位符会自动从对象中找对应的值(前提是Map集合中的键名要和SQL语句中参数占位符名称一致) - 以上三种方式在SQL映射文件中SQL语句的写法是一样的,如下,不一样的是接口中使用了方法重载以及运行的代码
- 方式一散装参数接收: 定义含多个参数的接口方法时,要通过
-
完整代码如下
-
SQL映射文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--namespace:名称空间--> <mapper namespace="at.guigu.mapper.BrandMapper"> <!--结果映射--> <resultMap id="brandResultMap" type="brand"> <!--由于id为主键,且数据库中的字段名和对应结果映射的目标类中的属性名一样,所以此处不需要主键映射,只需进行非主键映射即可--> <result column="brand_name" property="brandName" /> <result column="company_name" property="companyName" /> </resultMap> <!--多条件查询--> <select id="selectByCondition" resultMap="brandResultMap"> select * from tb_brand where status = #{status} and company_name like #{companyName} and brand_name like #{brandName} </select> </mapper>
-
Mapper接口
package at.guigu.mapper; import at.guigu.pojo.Brand; import org.apache.ibatis.annotations.Param; import java.util.List; import java.util.Map; public interface BrandMapper { //查询所有数据 List<Brand> selectAll(); //查看详情 Brand selectById(int id); //多条件查询方式一:散装参数接收 List<Brand> selectByCondition(@Param("status") int status, @Param("companyName") String companyName, @Param("brandName") String brandName); //多条件查询方式二:对象参数接收 List<Brand> selectByCondition(Brand brand); //多条件查询方式三:Map集合参数接收 List<Brand> selectByCondition(Map map); }
-
测试代码
package at.guigu.test; import at.guigu.mapper.BrandMapper; import at.guigu.pojo.Brand; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.List; import java.util.Map; public class MyBatisTest { private static BrandMapper getBrandMapper() throws IOException { //1 加载核心配置文件,获取`SqlSessionFactory`对象 //1.1 配置mybatis-config.xml文件路径,由于该文件直接在resources目录下,所以直接写文件名即可 String resource = "mybatis-config.xml"; //1.2 利用Resources类中的静态方法将配置文件加载到内存 InputStream inputStream = Resources.getResourceAsStream(resource); //1.3 获取SqlSessionFactory对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //2 获取SqlSession对象,执行SQL语句 //2.1 获取SqlSession对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //2.2 获取Mapper接口UserMapper的代理对象 BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); return brandMapper; } //多条件查询方式一:散装参数接收 @Test public void testSelectByCondition() throws IOException { //接收前端传回的多条件参数 int status = 1; String companyName = "华为"; String brandName = "华为"; //处理参数 companyName = "%" + companyName + "%"; brandName = "%" + brandName + "%"; //获取Mapper接口对象 BrandMapper brandMapper = getBrandMapper(); //2.3 执行sql语句 List<Brand> brands = brandMapper.selectByCondition(status, companyName, brandName); System.out.println(brands); } //多条件查询方式二:对象参数接收 @Test public void testSelectByConditionTwo() throws IOException { //接收前端传回的多条件参数 int status = 1; String companyName = "华为"; String brandName = "华为"; //处理参数 companyName = "%" + companyName + "%"; brandName = "%" + brandName + "%"; //将多条件参数封装到对象中 Brand brand = new Brand(); brand.setStatus(status); brand.setCompanyName(companyName); brand.setBrandName(brandName); //获取Mapper接口对象 BrandMapper brandMapper = getBrandMapper(); //2.3 执行sql语句 List<Brand> brands = brandMapper.selectByCondition(brand); System.out.println(brands); } //多条件查询方式三:Map集合参数接收 @Test public void testSelectByConditionThree() throws IOException { //接收前端传回的多条件参数 int status = 1; String companyName = "华为"; String brandName = "华为"; //处理参数 companyName = "%" + companyName + "%"; brandName = "%" + brandName + "%"; //将多条件参数封装到对象中 Map map = new HashMap(); map.put("status", status); map.put("companyName", companyName); map.put("brandName", brandName); //获取Mapper接口对象 BrandMapper brandMapper = getBrandMapper(); //2.3 执行sql语句 List<Brand> brands = brandMapper.selectByCondition(map); System.out.println(brands); } }
-
动态多条件查询
普通多条件查询时,多个条件若缺少其中一个条件(即用户输入条件时不一定会将所有条件都填写)则会导致查询失败,会解决该问题引入了动态多条件查询
-
特点
- SQL映射文件中的SQL语句会随着用户的输入或外部条件的变化而变化,称为动态SQL(MyBatis对动态SQL有强大的支撑)
-
MyBatis提供给动态SQL的标签如下
标签 解释 <if test="逻辑表达式"></if>
条件标签,使用 test
属性进行条件判断choose(when, otherwise)
条件选择标签,类似于Java中的 switch
语句trim(where, set)
foreach
if
标签:常用于判断参数是否有值,使用test
属性进行条件判断
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--namespace:名称空间--> <mapper namespace="at.guigu.mapper.BrandMapper"> <!--结果映射--> <resultMap id="brandResultMap" type="brand"> <!--由于id为主键,且数据库中的字段名和对应结果映射的目标类中的属性名一样,所以此处不需要主键映射,只需进行非主键映射即可--> <result column="brand_name" property="brandName" /> <result column="company_name" property="companyName" /> </resultMap> <!--多条件查询--> <select id="selectByCondition" resultMap="brandResultMap"> select * from tb_brand where <if test="status != null"> status = #{status} </if> <if test="companyName != null and companyName != ''"> and company_name like #{companyName} </if> <if test="brandName != null and brandName != ''"> and brand_name like #{brandName} </if> </select> </mapper>
if
标签缺点: 多条件动态查询时除了第一个参数不能为空外,其它参数都可为空,因为SQL映射文件中不同SQL语句的条件之间通过and连接,此时假设第一个参数为null,则动态SQL语句就变为了select * from tb_brand where and company_name like ?
,该SQL语句是个错误形式,因为where
关键字后直接跟了and
-
解决方式一: 创建恒等式,如下所示
此时无论三个条件是否满足都不会造成SQL语句形式出错,因为此时
where
关键字后的第一个条件1 = 1
是必然成立的<!--多条件查询--> <select id="selectByCondition" resultMap="brandResultMap"> select * from tb_brand where 1 = 1 <if test="status != null"> and status = #{status} </if> <if test="companyName != null and companyName != ''"> and company_name like #{companyName} </if> <if test="brandName != null and brandName != ''"> and brand_name like #{brandName} </if> </select>
-
解决方式二: 利用MyBatis提供的
<where>
标签来替换SQL的where
关键字,如下所示-
where
标签特点 1.会根据实际情况自动去除
and
关键字构成正确的SQL语句 2.若无任何条件该标签会自动去除SQL语句中的where关键字,即此时动态SQL语句就变为
select * from tb_brand;
<!--多条件查询--> <select id="selectByCondition" resultMap="brandResultMap"> select * from tb_brand <where> <if test="status != null"> and status = #{status} </if> <if test="companyName != null and companyName != ''"> and company_name like #{companyName} </if> <if test="brandName != null and brandName != ''"> and brand_name like #{brandName} </if> </where> </select>
-
动态单条件查询------choose(when, otherwise)
-
解释
从多个条件中选择一个条件进行查询
-
注意
choose
标签类似于switch
when
标签类似于case
otherwise
标签相当于switch
语句中的default
-
对应的SQL映射文件如下
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--namespace:名称空间--> <mapper namespace="at.guigu.mapper.BrandMapper"> <!--结果映射--> <resultMap id="brandResultMap" type="brand"> <!--由于id为主键,且数据库中的字段名和对应结果映射的目标类中的属性名一样,所以此处不需要主键映射,只需进行非主键映射即可--> <result column="brand_name" property="brandName" /> <result column="company_name" property="companyName" /> </resultMap> <!--动态单条件查询--> <select id="selectBySingleCondition" resultMap="brandResultMap"> select * from tb_brand where <choose> <!--类似于switch--> <when test="status != null"> status = #{status} </when> <when test="companyName != null and companyName != ''"> company_name like #{companyName} </when> <when test="brandName != null and brandName != ''"> brand_name like #{brandName} </when> <otherwise><!--相等于default,即若条件都没选则执行otherwise标签体的内容--> 1=1 </otherwise> </choose> </select> </mapper>
-
注意:在以上SQL映射文件中,若三个条件都没选则会执行
default
语句的代码,此时SQL语句为select * from where 1=1;
,可能会返回错误的结果,所以可将<choose>
标签放在<where>
标签中来解决该问题,如下所示**( 推荐使用 ,where标签的特点详见动态多条件查询 )**<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--namespace:名称空间--> <mapper namespace="at.guigu.mapper.BrandMapper"> <!--结果映射--> <resultMap id="brandResultMap" type="brand"> <!--由于id为主键,且数据库中的字段名和对应结果映射的目标类中的属性名一样,所以此处不需要主键映射,只需进行非主键映射即可--> <result column="brand_name" property="brandName" /> <result column="company_name" property="companyName" /> </resultMap> <!--动态单条件查询--> <select id="selectBySingleCondition" resultMap="brandResultMap"> select * from tb_brand <where> <choose> <!--类似于switch--> <when test="status != null"> status = #{status} </when> <when test="companyName != null and companyName != ''"> company_name like #{companyName} </when> <when test="brandName != null and brandName != ''"> brand_name like #{brandName} </when> </choose> </where> </select> </mapper>
-
添加
基础添加
-
解释:它会将网页要添加的数据封装成一个对象,然后添加到数据库表中
-
注意:添加时,id不会让用户自己写,这个是自动生成的,所以参数是除了id之外的所有数据
-
SQL映射文件如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--namespace:名称空间--> <mapper namespace="at.guigu.mapper.BrandMapper"> <!--结果映射--> <resultMap id="brandResultMap" type="brand"> <!--由于id为主键,且数据库中的字段名和对应结果映射的目标类中的属性名一样,所以此处不需要主键映射,只需进行非主键映射即可--> <result column="brand_name" property="brandName" /> <result column="company_name" property="companyName" /> </resultMap> <!--基础添加--> <insert id="add"> insert into tb_brand (brand_name, company_name, ordered, description, status) values (#{brandName}, #{companyName}, #{ordered}, #{description}, #{status}); </insert> </mapper>
-
接口如下
package at.guigu.mapper; import at.guigu.pojo.Brand; import org.apache.ibatis.annotations.Param; import java.util.List; import java.util.Map; public interface BrandMapper { //基础添加 void add(Brand brand); }
-
测试代码如下
package at.guigu.test; import at.guigu.mapper.BrandMapper; import at.guigu.pojo.Brand; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import java.io.IOException; import java.io.InputStream; public class MyBatisTest { //基础添加 @Test public void testAdd() throws IOException { //接收前端传回的多个参数 String companyName = "波导"; String brandName = "波导手机"; String description = "手机中的战斗机"; int ordered = 100; int status = 1; //将参数封装到对象中 Brand brand = new Brand(); brand.setCompanyName(companyName); brand.setBrandName(brandName); brand.setDescription(description); brand.setOrdered(ordered); brand.setStatus(status); //1 加载核心配置文件,获取`SqlSessionFactory`对象 //1.1 配置mybatis-config.xml文件路径,由于该文件直接在resources目录下,所以直接写文件名即可 String resource = "mybatis-config.xml"; //1.2 利用Resources类中的静态方法将配置文件加载到内存 InputStream inputStream = Resources.getResourceAsStream(resource); //1.3 获取SqlSessionFactory对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //2 获取SqlSession对象,执行SQL语句 //2.1 获取SqlSession对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //2.2 获取Mapper接口UserMapper的代理对象 BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); //2.3 执行sql语句 brandMapper.add(brand); //手动提交事务 sqlSession.commit(); //3 释放资源 sqlSession.close(); } }
手动提交事务的原因: 若无手动提交事务的代码则运行时能够运行成功但是数据库对应的表中并没有数据,原因是自动提交事务被关闭,所以我们需要手动提交事务,此时数据库的表中就会出现添加的数据,如图所示
- 注意:若不想在最后手动提交事务,则可在获取SqlSession对象时将事务自动提交开启,即
SqlSession sqlSession = sqlSessionFactory.openSession(true);
代表将参数autoCommit设置为true(即开启事务自动提交)
- 注意:若不想在最后手动提交事务,则可在获取SqlSession对象时将事务自动提交开启,即
-
问题:添加数据时会自动在数据库表中添加两条重复数据,如图所示,解决方法如下
主键返回
-
解释
在数据添加成功后需要获取插入数据库数据的主键的值(比如我在基础添加中添加了一个商品,现在添加成功后需要获取对应的主键值即id)
-
注意
-
在基础添加中的示例(添加了一个波导手机),由于id属于主键且课可以自增,所以在添加商品时不需要用户来添加id,而是系统自动添加id,所以用户需要输入的数据是除了主键id之外的所有数据,且这些数据是被封装到对象中然后通过若干方法最终到达数据库的,这就会有一个问题:添加商品成功后,无法通过Brand对象返回对应的id,这是因为并没有将对应的id封装到该对象中去,所以也就无法通过对象来获取用户添加的商品的对应主键id
-
解决方法:将主键绑定到对象上,SQL映射文件如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--namespace:名称空间--> <mapper namespace="at.guigu.mapper.BrandMapper"> <!--结果映射--> <resultMap id="brandResultMap" type="brand"> <!--由于id为主键,且数据库中的字段名和对应结果映射的目标类中的属性名一样,所以此处不需要主键映射,只需进行非主键映射即可--> <result column="brand_name" property="brandName" /> <result column="company_name" property="companyName" /> </resultMap> <!--主键返回--> <insert id="add" useGeneratedKeys="true" keyProperty="id"> insert into tb_brand (brand_name, company_name, ordered, description, status) values (#{brandName}, #{companyName}, #{ordered}, #{description}, #{status}); </insert> </mapper>
属性 解释 useGeneratedKeys
是否将主键绑定到对象,默认为false keyProperty="name"
数据库表的主键对应对象中属性的名称(即将主键与对象中的name属性关联) -
测试代码为
package at.guigu.test; import at.guigu.mapper.BrandMapper; import at.guigu.pojo.Brand; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import java.io.IOException; import java.io.InputStream; public class MyBatisTest { //基础添加 @Test public void testAdd() throws IOException { //接收前端传回的多个参数 String companyName = "波导"; String brandName = "波导手机"; String description = "手机中的战斗机"; int ordered = 100; int status = 1; //将参数封装到对象中 Brand brand = new Brand(); brand.setCompanyName(companyName); brand.setBrandName(brandName); brand.setDescription(description); brand.setOrdered(ordered); brand.setStatus(status); //1 加载核心配置文件,获取`SqlSessionFactory`对象 //1.1 配置mybatis-config.xml文件路径,由于该文件直接在resources目录下,所以直接写文件名即可 String resource = "mybatis-config.xml"; //1.2 利用Resources类中的静态方法将配置文件加载到内存 InputStream inputStream = Resources.getResourceAsStream(resource); //1.3 获取SqlSessionFactory对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //2 获取SqlSession对象,执行SQL语句 //2.1 获取SqlSession对象 SqlSession sqlSession = sqlSessionFactory.openSession(); //2.2 获取Mapper接口UserMapper的代理对象 BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); //2.3 执行sql语句 brandMapper.add(brand); //手动提交事务 sqlSession.commit(); //3 释放资源 sqlSession.close(); } }
-
修改
- 解释:修改(即编辑)已存在的商品数据信息
修改全部字段
-
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"> <!--namespace:名称空间--> <mapper namespace="at.guigu.mapper.BrandMapper"> <!--结果映射--> <resultMap id="brandResultMap" type="brand"> <!--由于id为主键,且数据库中的字段名和对应结果映射的目标类中的属性名一样,所以此处不需要主键映射,只需进行非主键映射即可--> <result column="brand_name" property="brandName" /> <result column="company_name" property="companyName" /> </resultMap> <!--修改商品数据:修改全部字段--> <update id="update"> update tb_brand set brand_name=#{brandName}, company_name=#{companyName}, ordered=#{ordered}, description=#{description}, status=#{status} where id=#{id}; </update> </mapper>
-
接口
package at.guigu.mapper; import at.guigu.pojo.Brand; import org.apache.ibatis.annotations.Param; public interface BrandMapper { //修改数据:修改全部字段 并返回受影响的行数 int update(Brand brand); }
-
测试代码
import at.guigu.mapper.BrandMapper; import at.guigu.pojo.Brand; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import java.io.IOException; import java.io.InputStream; public class MyBatisTest { //修改全部字段 @Test public void testUpdate() throws IOException { //接收前端传回的多个参数 int id = 4; String companyName = "波导岛搭"; String brandName = "波导岛手机的"; String description = "手机中的战斗大机积极"; int ordered = 90; int status = 1; //将参数封装到对象中 Brand brand = new Brand(); brand.setId(id); brand.setCompanyName(companyName); brand.setBrandName(brandName); brand.setDescription(description); brand.setOrdered(ordered); brand.setStatus(status); //1 加载核心配置文件,获取`SqlSessionFactory`对象 //1.1 配置mybatis-config.xml文件路径,由于该文件直接在resources目录下,所以直接写文件名即可 String resource = "mybatis-config.xml"; //1.2 利用Resources类中的静态方法将配置文件加载到内存 InputStream inputStream = Resources.getResourceAsStream(resource); //1.3 获取SqlSessionFactory对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //2 获取SqlSession对象,执行SQL语句 //2.1 获取SqlSession对象并开启事务自动提交 SqlSession sqlSession = sqlSessionFactory.openSession(true); //2.2 获取Mapper接口UserMapper的代理对象 BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); //2.3 执行sql语句并返回受影响的行数 System.out.println(brandMapper.update(brand)); //3 释放资源 sqlSession.close(); } }
修改动态字段
-
解释:用户将来修改的字段是不固定的,不可能提前知道要修改的字段名,此时就需要用到该技术
-
利用
<set>
标签,特点- 该标签会自动判断是否有修改的字段,若无任何修改的字段则生成的动态SQL语句就不会有set关键字,若某个字段未被修改则生成的动态SQL语句会自动忽略该字段修改语句以及该字段修改语句后的逗号,以映射文件中SQL语句为例:
- 假设现在无任何修改字段,则动态SQL语句变为:
update tb_brand where id = ?;
- 假设现在只有字段
description
被修改则动态SQL语句变为:update tb_brand where description = ? where id = ?;
- 假设现在无任何修改字段,则动态SQL语句变为:
- 该标签会自动判断是否有修改的字段,若无任何修改的字段则生成的动态SQL语句就不会有set关键字,若某个字段未被修改则生成的动态SQL语句会自动忽略该字段修改语句以及该字段修改语句后的逗号,以映射文件中SQL语句为例:
-
SQL映射文件中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"> <!--namespace:名称空间--> <mapper namespace="at.guigu.mapper.BrandMapper"> <!--结果映射--> <resultMap id="brandResultMap" type="brand"> <!--由于id为主键,且数据库中的字段名和对应结果映射的目标类中的属性名一样,所以此处不需要主键映射,只需进行非主键映射即可--> <result column="brand_name" property="brandName" /> <result column="company_name" property="companyName" /> </resultMap> <!--修改商品数据:修改动态字段--> <update id="updatePart"> update tb_brand <set> <if test="brandName != null and brandName != ''" > brand_name = #{brandName}, </if> <if test="companyName != null and companyName !='' "> company_name = #{companyName}, </if> <if test="ordered != null"> ordered = #{ordered}, </if> <if test="description != null and description !='' "> description = #{description}, </if> <if test="status != null"> status = #{status} </if> </set> where id = #{id}; </update> </mapper>
-
接口
package at.guigu.mapper; import at.guigu.pojo.Brand; import org.apache.ibatis.annotations.Param; public interface BrandMapper { //修改数据:修改部分字段,即修改动态字段 并返回受影响的行数 int updatePart(Brand brand); }
-
测试代码
import at.guigu.mapper.BrandMapper; import at.guigu.pojo.Brand; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import java.io.IOException; import java.io.InputStream; public class MyBatisTest { //修改全部字段 @Test public void testUpdate() throws IOException { //接收前端传回的多个参数 int id = 4; String companyName = "波导岛搭"; String brandName = "波导岛手机的"; String description = "手机中的战斗大机积极"; int ordered = 90; int status = 1; //将参数封装到对象中 Brand brand = new Brand(); brand.setId(id); brand.setCompanyName(companyName); brand.setBrandName(brandName); brand.setDescription(description); brand.setOrdered(ordered); brand.setStatus(status); //1 加载核心配置文件,获取`SqlSessionFactory`对象 //1.1 配置mybatis-config.xml文件路径,由于该文件直接在resources目录下,所以直接写文件名即可 String resource = "mybatis-config.xml"; //1.2 利用Resources类中的静态方法将配置文件加载到内存 InputStream inputStream = Resources.getResourceAsStream(resource); //1.3 获取SqlSessionFactory对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //2 获取SqlSession对象,执行SQL语句 //2.1 获取SqlSession对象并开启事务自动提交 SqlSession sqlSession = sqlSessionFactory.openSession(true); //2.2 获取Mapper接口UserMapper的代理对象 BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); //2.3 执行sql语句并返回受影响的行数 System.out.println(brandMapper.updatePart(brand)); //3 释放资源 sqlSession.close(); } }
删除
删除一个
-
解释:删除一条数据(商品)
-
SQL映射文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--namespace:名称空间--> <mapper namespace="at.guigu.mapper.BrandMapper"> <!--结果映射--> <resultMap id="brandResultMap" type="brand"> <!--由于id为主键,且数据库中的字段名和对应结果映射的目标类中的属性名一样,所以此处不需要主键映射,只需进行非主键映射即可--> <result column="brand_name" property="brandName" /> <result column="company_name" property="companyName" /> </resultMap> <!--删除一条数据(删除一个商品)--> <delete id="delete"> delete from tb_brand where id = #{id}; </delete> </mapper>
-
Mapper接口
package at.guigu.mapper; import at.guigu.pojo.Brand; import org.apache.ibatis.annotations.Param; public interface BrandMapper { //删除一条数据(删除一个商品)并判断删除是否成功 boolean delete(int id); }
-
测试代码
import at.guigu.mapper.BrandMapper; import at.guigu.pojo.Brand; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import java.io.IOException; import java.io.InputStream; public class MyBatisTest { //修改全部字段 @Test public void testSingleDelete() throws IOException { //接收前端传回的参数 int id = 4; //1 加载核心配置文件,获取`SqlSessionFactory`对象 //1.1 配置mybatis-config.xml文件路径,由于该文件直接在resources目录下,所以直接写文件名即可 String resource = "mybatis-config.xml"; //1.2 利用Resources类中的静态方法将配置文件加载到内存 InputStream inputStream = Resources.getResourceAsStream(resource); //1.3 获取SqlSessionFactory对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //2 获取SqlSession对象,执行SQL语句 //2.1 获取SqlSession对象并开启事务自动提交 SqlSession sqlSession = sqlSessionFactory.openSession(true); //2.2 获取Mapper接口UserMapper的代理对象 BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); //2.3 执行sql语句并返回受影响的行数 System.out.println(brandMapper.delete(id)); //3 释放资源 sqlSession.close(); } }
批量删除
-
解释:一次删除多条数据(商品)
-
注意
删除多条数据时会根据id进行删除,并且会将id封装到数组中,通过遍历数组依次删除对应的数据(商品)
-
用到的标签及属性
标签 解释 <foreach></foreach>
用于循环处理一组数据的标签。常用于在模板文件中循环遍历一个集合,例如列表或数组,并对集合中的每一项执行某些操作 该标签中的属性 解释 collection
或items
指定要迭代的集合或数组。 item
或var
指定循环中每次迭代时的当前项变量名称 separator
指定在每次迭代之间插入的字符串或标记 open
指定在循环开始时添加的字符串或标记 close
指定在循环结束时添加的字符串或标记 -
SQL映射文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--namespace:名称空间--> <mapper namespace="at.guigu.mapper.BrandMapper"> <!--结果映射--> <resultMap id="brandResultMap" type="brand"> <!--由于id为主键,且数据库中的字段名和对应结果映射的目标类中的属性名一样,所以此处不需要主键映射,只需进行非主键映射即可--> <result column="brand_name" property="brandName" /> <result column="company_name" property="companyName" /> </resultMap> <!--批量删除--> <delete id="deleteByIds"> delete from tb_brand where id in <foreach collection="ids" item="id" separator="," open="(" close=")"> #{id} </foreach> </delete> </mapper>
-
注意
由于数组中id个数不确定,所以需要用到
<foreach>
标签来遍历数组,并将遍历到的参数之间用,
隔开,最终放在()
内,假设现在数组ids中有三个id,则动态SQL语句为delete from tb_brand where id in(?,?,?);
-
-
接口
package at.guigu.mapper; import at.guigu.pojo.Brand; import org.apache.ibatis.annotations.Param; public interface BrandMapper { //批量删除 boolean deleteByIds(@Param("ids") int[] ids); }
-
测试代码
import at.guigu.mapper.BrandMapper; import at.guigu.pojo.Brand; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Test; import java.io.IOException; import java.io.InputStream; public class MyBatisTest { //批量删除 @Test public void testDelete() throws IOException { //接收前端传回的参数 int[] ids = {2,3}; //1 加载核心配置文件,获取`SqlSessionFactory`对象 //1.1 配置mybatis-config.xml文件路径,由于该文件直接在resources目录下,所以直接写文件名即可 String resource = "mybatis-config.xml"; //1.2 利用Resources类中的静态方法将配置文件加载到内存 InputStream inputStream = Resources.getResourceAsStream(resource); //1.3 获取SqlSessionFactory对象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //2 获取SqlSession对象,执行SQL语句 //2.1 获取SqlSession对象并开启事务自动提交 SqlSession sqlSession = sqlSessionFactory.openSession(true); //2.2 获取Mapper接口UserMapper的代理对象 BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class); //2.3 执行sql语句并返回受影响的行数 System.out.println(brandMapper.deleteByIds(ids)); //3 释放资源 sqlSession.close(); } }
-
注意:SQL映射文件与对应接口有两种方式,以上示例中是一种方式,以下将对另一种方式进行说明
-
由于MyBatis会自动将数组参数封装为一个Map集合,默认情况下该Map集合中键名为array,键值为传入的数组,所以SQL映射文件和接口也可写为如下形式
-
SQL映射文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--namespace:名称空间--> <mapper namespace="at.guigu.mapper.BrandMapper"> <!--结果映射--> <resultMap id="brandResultMap" type="brand"> <!--由于id为主键,且数据库中的字段名和对应结果映射的目标类中的属性名一样,所以此处不需要主键映射,只需进行非主键映射即可--> <result column="brand_name" property="brandName" /> <result column="company_name" property="companyName" /> </resultMap> <!--批量删除--> <delete id="deleteByIds"> delete from tb_brand where id in <foreach collection="array" item="id" separator="," open="(" close=")"> #{id} </foreach> </delete> </mapper>
-
接口
package at.guigu.mapper; import at.guigu.pojo.Brand; import org.apache.ibatis.annotations.Param; public interface BrandMapper { //批量删除 boolean deleteByIds(int[] ids); }
-