文章目录
1. 任务描述
如上图所示产品原型,里面包含了品牌数据的 查询 、 按条件查询 、 添加 、 删除 、 批量删除 、 修改 等功能,而这些功能其实就是对数据库表中的数据进行 CRUD 操作。接下来就使用 Mybatis 完成品牌数据的增删改查操作。以下是要完成的功能列表:
-
查询
查询所有数据
查看详情
条件查询 -
添加
-
修改
修改全部字段
修改动态字段 -
删除
删除一个
批量删除
2. 准备工作
(1) 创建数据库表 tb_brand
-- 删除tb_brand表
drop table if exists tb_brand;
-- 创建tb_brand表
create table 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;
(2) 实体类 Brand
在实体类中,基本数据类型建议使用对应的包装类型。
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 Integer getId() {
return id;
}
//getter、setter方法
//toString方法
}
(3) 创建测试用例 MybatisTest
public class MybatisTest {
}
(4) 安装 MyBatisX 插件
在此之前,UserMapper 接口和对应的 UserMapper.xml 文件都是手写的,安装 MyBatisX 插件后,可以根据 UserMapper 接口中的方法自动地向 UserMapper.xml 文件中添加 statement。
MybatisX 是一款基于 IDEA 的快速开发插件,为效率而生。
主要功能:
① XML 和接口方法相互跳转
② 根据接口方法生成 statement
安装完 mybatisx 插件后,可以点击代码前面的小鸟实现 “XML 和接口方法相互跳转”;也可以根据在 mapper 接口中写的接口方法生成 statement。
UserMapper.xml 中的生成结果如下:
<!--statement-->
<select id="selectById" resultType="com.itheima.pojo.User"></select>
再向其中添加 SQL 语句即可。
<!--statement-->
<!--改一下别名-->
<select id="selectById" resultType="user">
select * from tb_user where id=#{id};
</select>
3. 配置文件完成查询功能
今后无论用 mybatis 完成多么复杂的功能,基本就三步:
- 在 Mapper 接口中编写接口方法
参数:无
返回类型:List<Brand> - 利用接口方法生成 SQL 映射文件中的 statement,并补充 SQL 语句
- 执行方法,测试
在三步之前,首先配置好 pom.xml 文件(同前),准备好 logback.xml(同前),创建实体类 Brand(同前),准备好核心配置文件 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>
<!--相当于给pojo下面的所有实体类都取了别名
别名默认是类名,不区分大小写,可以不用带所在包的名称-->
<typeAliases>
<package name="com.itheima.pojo"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/><!--该操作可以被spring接管-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!--mybatis:数据库名-->
<!--jdbc:mysql:///mybatis?useSSL=false"也可以-->
<property name="url" value="jdbc:mysql://localhost/mybatis?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--更改sql映射文件路径-->
<!--注意:resource下新建的是dictionary,
分隔符用“/”,不能用“.”-->
<!--<mapper resource="com/itheima/mapper/UserMapper.xml"/>-->
<!--用包扫描的方式来加载 SQL 映射文件-->
<!--通过接口来找sql映射文件,所以路径中的分隔符用"."-->
<package name="com.itheima.mapper"/>
</mappers>
</configuration>
3.1 查询所有数据
下面开始三个基本步骤:
(1) 编写 Mapper 接口
public interface BrandMapper {
List<Brand> selectAll();
}
(2) SQL 映射文件:由 Mapper 接口方法生成 statement,并补充编写 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">
<mapper namespace="com.itheima.mapper.BrandMapper">
<!--statement-->
<!--生成后改一下别名-->
<select id="selectAll" resultType="brand">
select * from tb_brand;
</select>
</mapper>
(3) 编写测试代码
public class MybatisTest {
@Test
public void testSelectAll() throws IOException {
//加载mybatis核心配置文件,获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取SqlSession对象,用它来执行SQL
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//执行方法
List<Brand> brands = brandMapper.selectAll();
System.out.println(brands);
//释放资源
sqlSession.close();
}
}
点这里可以执行测试代码:
输出结果:
在输出结果中可以看出,有些字段为 null,这是因为数据库表中的字段名称与实体类的属性写法不同,对应不上:
解决方案:
(1) 起别名:对不一样的列名起别名,让别名和实体类的属性名相同。
<select id="selectAll" resultType="brand">
select id,brand_name as brandName, company_name as companyName, ordered, description
from tb_brand;
</select>
缺点:每次查询都要定义一次别名。
(2) SQL 片段
<!--SQL片段-->
<sql id="brand_column">
<!--起别名时,as可以省略-->
id, brand_name as brandName, company_name as companyName, ordered, description, status
</sql>
<select id="selectAll" resultType="brand">
select
<include refid="brand_column"></include>
from tb_brand;
</select>
缺点:如果 select 语句所要查询的字段各不相同,就要定义各种各样的 SQL 片段,不灵活。
(3) resultMap(最常用)
<!--resultMap的id随便写-->
<!--type就是原SQL语句外部标签的resulType属性值-->
<resultMap id="brandResultMap" type="brand">
<!--把数据库表字段与实体类属性写法不同的写在这里-->
<result column="brand_name" property="brandName"/>
<result column="company_name" property="companyName"/>
</resultMap>
<!--resultType属性改成resultMap属性-->
<select id="selectAll" resultMap="brandResultMap">
select * from tb_brand;
</select>
3.2 查看详情
有些数据的属性比较多,在页面表格中无法全部实现,而只会显示部分,而其他属性数据的查询可以通过 查看详情 来进行查询,如上图所示。
查看详情就是根据 id 查询数据。
在 BrandMapper 接口中添加:
Brand selectById(int id);
在 BrandMapper.xml 中添加:
<select id="selectById" parameterType="int" resultMap="brandResultMap">
# “#{id}”一般与接口方法的形参名称一致
select * from tb_brand where id=#{id};
</select>
参数占位符:
① #{}:会被替换为 “?”,防止 SQL 注入(更好)
② ${}:拼接 SQL,一定会存在 SQL 注入问题
适用情况:一般的参数传递用 #{};表名或列名不固定时用 ${}
参数类型:parameterType="int"可省略,Integer 也可。
特殊字符:如小于号 “<” 不能直接出现,解决方法有:
① 转义字符
<select id="selectById" resultMap="brandResultMap">
select * from tb_brand where id < #{id};
</select>
② CDATA 区
<select id="selectById" resultMap="brandResultMap">
select * from tb_brand where id
# 大写CD+enter
<![CDATA[
<
]]>
#{id};
</select>
在测试用例 MybatisTest.java 中添加:
@Test
public void testSelectById() throws IOException {
//接收参数
int id = 1;
//加载mybatis核心配置文件,获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取SqlSession对象,用它来执行SQL
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//执行方法
Brand brand = brandMapper.selectById(id);
System.out.println(brand);
//释放资源
sqlSession.close();
}
3.3 条件查询
上图功能为 “进行多条件查询,将查询结果展示在下方的表格中”。
分析: 条件字段 企业名称 和 品牌名称 需要进行模糊查询,所以条件应该是:
(1) 编写接口方法
当查询条件不只一个时,就需要考虑接口方法形参与 SQL 语句占位符的对应问题,解决方法有三种:
① 散装参数:@Param(“SQL参数占位符名称”)
表示把该参数传给哪个占位符。
List<Brand> selectByCondition(
//status参数传给叫“status”的占位符
@Param("status") int status,
@Param("companyName") String companyName,
@Param("brandName") String brandName);
② 对象参数:把查询条件封装成实体类的对象传递到接口方法中,使 SQL 语句根据对象的 getter 方法获取对应属性。条件是占位符名称要与实体类的属性名称一致。
List<Brand> selectByCondition(Brand brand);
③ Map 集合参数:也可以把查询条件封装成 Map 集合,Map 集合键的名称要与 SQL 语句中的占位符名称一致。
List<Brand> selectByCondition(Map map);
(2) 编写 SQL 语句
<select id="selectByCondition" resultMap="brandResultMap">
select *
from tb_brand
where status = #{status}
# 模糊查询
and company_name like #{companyName}
and brand_name like #{brandName}
</select>
(3) 编写测试方法
下面代码中,要特别注意:模糊查询的相关参数要特殊处理。
① 散装参数的测试
@Test
public void testSelectByCondition() throws IOException {
//接收参数
int status = 1;
String companyName = "小米";
String brandName = "小米";
//模糊查询,所以要处理参数
companyName = "%"+companyName+"%";
brandName = "%"+brandName+"%";
//加载mybatis核心配置文件,获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取SqlSession对象,用它来执行SQL
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//执行方法
List<Brand> brands = brandMapper.selectByCondition(status,companyName,brandName);
System.out.println(brands);
//释放资源
sqlSession.close();
}
② 对象参数的测试
@Test
public void testSelectByCondition() throws IOException {
//接收参数
int status = 1;
String companyName = "小米";
String brandName = "小米";
//模糊查询,所以要处理参数
companyName = "%"+companyName+"%";
brandName = "%"+brandName+"%";
//封装对象
Brand brand = new Brand();
brand.setStatus(1);
brand.setCompanyName(companyName);
brand.setBrandName(brandName);
//加载mybatis核心配置文件,获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取SqlSession对象,用它来执行SQL
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//执行方法(传递对象参数)
List<Brand> brands = brandMapper.selectByCondition(brand);
System.out.println(brands);
//释放资源
sqlSession.close();
}
③ Map 集合的测试
@Test
public void testSelectByCondition() 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);
//加载mybatis核心配置文件,获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取SqlSession对象,用它来执行SQL
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//执行方法(传递对象参数)
List<Brand> brands = brandMapper.selectByCondition(map);
System.out.println(brands);
//释放资源
sqlSession.close();
}
输出结果:
3.4 动态条件查询
3.4.1 动态条件查询之多条件
上节的条件查询存在一个 bug。用户在输入条件时,可能不会填写所有的条件,这时就查询不出来,所以 SQL 语句就不能那样写了。
例如用户只输入 当前状态 时,SQL 语句就是:
select * from tb_brand where status = #{status}
而用户如果只输入企业名称时,SQL语句就是
select * from tb_brand
where company_name like #{companName}
而用户如果输入了 当前状态 和 企业名称 时,SQL语句又不一样
select * from tb_brand
where status = #{status} and company_name like #{companName}
SQL 语句会随着用户的输入或外部条件的变化而变化,称为动态 SQL,Mybatis 对动态 SQL 有很强大的支撑:
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
下面先来学习 if 标签和 where 标签:
if 标签:表示条件判断,test 属性里面写逻辑表达式。
<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>
//封装对象
Map map = new HashMap();
map.put("status", status);
map.put("companyName",companyName);
// map.put("brandName",brandName);
输出结果:
但是它也存在问题,如果此时给的参数值是:
Map map = new HashMap();
// map.put("status" , status);
map.put("companyName", companyName);
map.put("brandName" , brandName);
拼接的 SQL 语句就变成了:
select * from tb_brand
where and company_name like ? and brand_name like ?
上面的语句中 where 后直接跟 and,是错误的 SQL 语句,解决方式有两种(推荐第二种):
(1) 恒等式:如添加 ”1=1“
(2) 用 where 标签替换 where 关键字:在 SQL 语句中替换 where 关键字,动态去掉第一个条件前的 and,如果所有的参数没有值则不加 where 关键字(推荐)。
3.4.2 动态条件查询之单条件
如上图所示,在查询时只能选择 品牌名称 、 当前状态 、 企业名称 这三个条件中的一个,但是用户选哪一个不能确定。这种就属于单个条件的动态 SQL 语句。
这种需求需要使用到 choose(when, otherwise) 标签实现,类似于 Java 中的 switch-case-default 语句。
(1) 编写接口方法
List<Brand> selectByConditionSingle(Brand brand);
(2) 编写SQL语句
方式 1(不常用):
<select id="selectByConditionSingle" resultMap="brandResultMap">
select *
from tb_brand
where
<choose>
<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>
<!--以防所有字段都没有值时,where后面没有语句导致语法错误-->
1=1
</otherwise>
</choose>
</select>
方式 2(常用):
<select id="selectByConditionSingle" resultMap="brandResultMap">
select *
from tb_brand
<where>
<!--如果所有字段都没有值,
where标签就不添加where关键字-->
<choose>
<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>
(3) 编写测试方法
@Test
public void testSelectByConditionSingle() throws IOException {
//接收参数
int status = 1;
String companyName = "小米";
String brandName = "小米";
//模糊查询,所以要处理参数
companyName = "%"+companyName+"%";
brandName = "%"+brandName+"%";
//封装对象(实体类对象或Map对象都可)
Brand brand = new Brand();
// brand.setStatus(status);
brand.setCompanyName(companyName);
// brand.setBrandName(brandName);
//加载mybatis核心配置文件,获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取SqlSession对象,用它来执行SQL
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//执行方法(传递对象参数)
List<Brand> brands = brandMapper.selectByConditionSingle(brand);
System.out.println(brands);
//释放资源
sqlSession.close();
}
4. 配置文件完成添加功能
4.1 添加数据
上图是添加数据的页面,在该页面输入想要的数据后点击 ”提交“ 按钮,就会将这些数据添加到数据库中。接下来就来实现添加数据的操作。
(1) 编写 Mapper 接口方法
参数:除了 id 之外的所有数据(id会自增)
结果:void
void add(Brand brand);
(2) 编写SQL语句:SQL 映射文件
<insert id="add">
insert into tb_brand (brand_name,company_name,ordered,description,status)
values (#{brandName},#{companyName},#{ordered},#{description},#{status});
</insert>
(3) 测试
注意:插入数据要提交事务
@Test
public void testAdd() throws IOException {
//接收参数
int status = 1;
String companyName = "波导";
String brandName = "波导手机";
String description = "手机中的战斗机";
int ordered = 10;
//封装对象
Brand brand = new Brand();
brand.setStatus(status);
brand.setCompanyName(companyName);
brand.setBrandName(brandName);
brand.setDescription(description);
brand.setOrdered(ordered);
//加载mybatis核心配置文件,获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取SqlSession对象,用它来执行SQL
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//执行方法(传递对象参数)
brandMapper.add(brand);
//手动提交事务
sqlSession.commit();
//释放资源
sqlSession.close();
}
在上面的测试代码中,提交事务是必须的,否则事务回滚。虽 SQL 执行成功,但数据库表中未插入数据。
还有一种自动提交事务的方式:
SqlSession sqlSession = sqlSessionFactory.openSession(true);
- openSession():默认开启事务,进行增删改操作后需要使用
sqlSession.commit();
手动提交事务 - openSession(true):可以设置为自动提交事务(关闭事务)
4.2 添加完数据后返回主键
数据添加成功后,要想获取数据的 id,要在 insert 标签上添加两个属性:
- useGeneratedKeys:获取自增长的主键值。true 表示获取。
- keyProperty :将获取到的主键值封装到哪个属性
就能在插入数据后获取到 id:
brandMapper.add(brand);
System.out.println(brand.getId());
5. 配置文件完成修改功能
5.1 修改全部字段
如图所示是修改页面,用户在该页面编辑需要修改的数据,点击 “提交” 按钮,就会修改数据库中对应的数据。
接下来我们就具体来实现:
(1) 编写 Mapper 接口方法
参数:所有数据
结果:void
void update(Brand brand);
(2) 编写SQL语句:SQL映射文件
<update id="update">
update tb_brand
set brand_name =#{brandName},
company_name=#{companyName},
ordered =#{ordered},
description=#{description},
status =#{status}
where id =#{id}
</update>
(3) 测试方法
@Test
public void testUpdate() throws IOException {
//接收参数
int status = 1;
String companyName = "波导";
String brandName = "波导手机";
String description = "波导手机,手机中的战斗机";
int ordered = 200;
int id = 7;
//封装对象
Brand brand = new Brand();
brand.setStatus(status);
brand.setCompanyName(companyName);
brand.setBrandName(brandName);
brand.setDescription(description);
brand.setOrdered(ordered);
brand.setId(id);
//加载mybatis核心配置文件,获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取SqlSession对象,用它来执行SQL
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//获取UserMapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//执行方法(传递对象参数)
brandMapper.update(brand);
//提交事务
sqlSession.commit();
//释放资源
sqlSession.close();
}
5.2 修改动态字段
在上节,修改信息时修改的是所有的字段。如果用户修改信息时,有的输入框没有输入内容,就会导致修改后有的字段为空。
这显然不合理,未输入的内容应该保留之前的数据。这就需要用到动态 SQL。
<update id="update">
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>
上面代码用 set 标签而不是 set 关键字的原因:
① 若最后一个字段未修改,set 语句最后就是逗号,语法错误。
② 若所有字段都未修改,set 语句就空留一个 set,语法错误。
测试:
@Test
public void testUpdate() throws IOException {
//接收参数
int status = 0;
String companyName = "波导";
String brandName = "波导手机";
String description = "波导手机,手机中的战斗机";
int ordered = 200;
int id = 7;
//封装对象
Brand brand = new Brand();
brand.setStatus(status);
// brand.setCompanyName(companyName);
// brand.setBrandName(brandName);
// brand.setDescription(description);
// brand.setOrdered(ordered);
brand.setId(id);
//加载mybatis核心配置文件,获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取SqlSession对象,用它来执行SQL
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//获取UserMapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//执行方法(传递对象参数)
brandMapper.update(brand);
//提交事务
sqlSession.commit();
//释放资源
sqlSession.close();
}
6. 配置文件完成删除功能
6.1 删除单条数据
如上图所示,每行数据后面都有一个 删除 按钮,当用户点击该按钮时,就会将该行数据删掉。
这种删除是通过主键 id 删除的,因为 id 是表中数据的唯一标识。
接下来就来实现该功能。
(1) 编写 Mapper 接口方法
void deleteById(int id);
(2) 编写 SQL
<delete id="deleteById">
delete from tb_brand where id=#{id};
</delete>
(3) 测试
public void testDeleteById() throws IOException {
//接收参数
int id = 7;
//加载mybatis核心配置文件,获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取SqlSession对象,用它来执行SQL
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//获取UserMapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//执行方法(传递对象参数)
brandMapper.deleteById(id);
//提交事务
sqlSession.commit();
//释放资源
sqlSession.close();
}
6.2 批量删除数据
如上图所示,用户可以选择多条数据,然后点击上面的 删除 按钮,就会删除数据库中对应的多行数据。
选中复选框并批量删除时,会将所有被选中记录的 id 封装成一个数组,再遍历数组,根据数组中的每个 id 来删除对应的记录。
(1) 编写 Mapper 接口方法
参数:id数组
结果:void
void deleteByIds(int[] ids);
(2) 编写 SQL
方法 1:
<delete id="deleteByIds">
#缺点:几个”?“不确定
delete from tb_brand
where id in (?.?.?)
</delete>
方法 2:
foreach 标签:用来迭代任何可迭代的对象(如数组,集合)。
- collection 属性:mybatis 会将数组参数,封装为一个 Map 集合。
默认:array = 数组
使用@Param注解改变map集合的默认key的名称 - item 属性:本次迭代获取到的元素。
- separator 属性:集合项迭代之间的分隔符。foreach 标签不会错误地添加多余的分隔符。也就是最后一次迭代不会加分隔符。
- open 属性:该属性值是在拼接SQL语句之前拼接的语句,只会拼接一次
- close 属性:该属性值是在拼接SQL语句拼接后拼接的语句,只会拼接一次
<delete id="deleteByIds">
delete from tb_brand where id
in
<foreach collection="array" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
如果 Mapper 接口方法加上@Param("ids")
注解,foreach 的 collection 属性值就能写成 “ids”,从而保持与 id 数组名一致。
void deleteByIds(@Param("ids") int[] ids);
<delete id="deleteByIds">
delete from tb_brand where id
in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
假如数组中的 id 数组是 {1,2,3},拼接后的 SQL 语句就是delete from tb_brand where id in (1,2,3)
(3) 测试
@Test
public void testDeleteByIds() throws IOException {
//接收参数
int[] ids = {2,3};
//加载mybatis核心配置文件,获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取SqlSession对象,用它来执行SQL
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//获取UserMapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//执行方法(传递对象参数)
brandMapper.deleteByIds(ids);
//提交事务
sqlSession.commit();
//释放资源
sqlSession.close();
}
7. mybatis 参数传递
Mybatis 接口方法中可以接收各种各样的参数,如下:
- 多个参数
- 单个参数,可以是如下类型:
- POJO 类型(实体类,如 User、Brand)
- Map 集合类型
- Collection 集合类型
- List 集合类型
- Array 类型
- 其他类型
7.1 多个参数
如下面的代码,就是接收两个参数,而接收多个参数的一种方式是使用 @Param 注解。
那么为什么要加该注解呢?这个问题要弄明白就必须来研究Mybatis 底层对于这些参数是如何处理的。
接口方法中定义多个参数时,Mybatis 会将这些参数封装成 Map 集合对象,值就是参数值,而键在没有使用 @Param 注解时有以下命名规则:
以 arg 开头 :第一个参数就叫 arg0,第二个参数就叫 arg1,以此类推。如:
map.put("arg0", 参数值1);
map.put("arg1", 参数值2);
以 param 开头 : 第一个参数就叫 param1,第二个参数就叫 param2,依次类推。如:
map.put("param1", 参数值1);
map.put("param2", 参数值2);
所以,代码可以这么写:
上面的写法,代码的可读性会变差,此时可以使用 @Param 注解。
在 username 参数前加上 @Param 注解:
User select(@Param("username") String username, String password);
Mybatis 在封装 Map 集合时,键名就会变成如下:
map.put("username", 参数值1);//原来是arg0
map.put("param1", 参数值1);
map.put("arg1", 参数值2);
map.put("param2", 参数值2);
所以代码可以这么写:
结论:以后接口参数是多个时,在每个参数上都使用 @Param 注解。这样代码的可读性更高。
7.2 单个参数
(1) POJO 类型(实体类,如 User、Brand)
直接使用。要求 属性名 和 参数占位符 名称一致。
(2) Map 集合类型
直接使用。要求 map集合的 键名 和 参数占位符名称一致。
(3) Collection 集合类型
Mybatis 会将集合封装到 map 集合中,如下:
map.put("arg0", collection集合);
map.put("collection", collection集合);
可以使用 @Param 注解替换 map 集合中默认的 arg 键名。
(4) List 集合类型
Mybatis 会将集合封装到 map 集合中,如下:
map.put("arg0",list集合);
map.put("collection",list集合);
map.put("list",list集合);
可以使用 @Param 注解替换 map 集合中默认的 arg 键名。
(5) Array 类型
Mybatis 会将集合封装到 map 集合中,如下:
map.put("arg0",数组);
map.put("array",数组);
可以使用 @Param 注解替换 map集合中默认的 arg 键名。
(6) 其他类型
比如 int 类型, 参数占位符 名称叫什么都可以,尽量做到见名知意。
8. 注解完成增删改查
使用注解开发会比配置文件开发更加方便。如下就是使用注解进行开发:
@Select(value = "select * from tb_user where id = #{id}")
public User select(int id);
注解是用来替换 SQL 映射文件的。所以,使用注解就不需要在 SQL 映射文件中书写对应的 statement。
Mybatis 针对 CURD 操作都提供了对应的注解,已经做到见名知意。如下:
- 查询 :@Select
- 添加 :@Insert
- 修改 :@Update
- 删除 :@Delete
【提示】注解完成简单功能,配置文件完成复杂功能
接下来通过一个案例来展示 Mybatis 的注解开发:
@Select("select * from tb_user where id=#{id}")
User selectById(int id);
测试:
public class MybatisDemo {
public static void main(String[] args) throws IOException {
//加载mybatis核心配置文件,获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取SqlSession对象,用它来执行SQL
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserMapper接口的代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.selectById(1);
System.out.println(user);
//释放资源
sqlSession.close();
}
}