mybatis练习---2种方式
- 能够使用映射配置文件实现CRUD操作
- 能够使用注解实现CRUD操作
配置文件CRUD就是把sql语句写到配置文件中,注解CRUD就是吧sql语句写到注解上。
一、配置文件实现CRUD
如上图所示产品原型,里面包含了品牌数据的
查询
、
按条件查询
、
添加
、
删除
、
批量删除
、
修改
等功能,而这些功能其实 就是对数据库表中的数据进行CRUD
操作。接下来我们就使用
Mybatis
完成品牌数据的增删改查操作。以下是我们要完成功能 列表:
1.1 环境准备
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),
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', '男', '西安');
在
com.itheima.pojo
包下创建
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;
//省略 setter and getter。自己写时要补全这部分代码
}
测试代码需要在
test/java
目录下创建包及测试用例。项目结构如下:
MybatisX
是一款基于
IDEA
的快速开发插件,为效率而生。
主要功能:
1.XML
映射配置文件 和 接口方法 间相互跳转
2.根据接口方法生成
statement
安装方式:
点击
file
,选择
settings
,就能看到如下图所示界面
注意:安装完毕后需要重启
IDEA
插件效果。--看到小鸟出来了,蓝色的小鸟是mapper接口,红色的小鸟是sql映射文件。在statement这个位置,我想要找到selectAll()方法,直接点击那个鸟(蓝色的小鸟)就可以直接跳转过来,如果想要看这个方法对应的sql语句,还是直接点击那个鸟(红色的小鸟)就可以跳转过来
红色头绳的表示映射配置文件,蓝色头绳的表示
mapper
接口。在
mapper
接口点击红色头绳的小鸟图标会自动跳转 到对应的映射配置文件,在映射配置文件中点击蓝色头绳的小鸟图标会自动跳转到对应的mapper
接口。也可以在 mapper接口中定义方法,自动生成映射配置文件中的
statement
,如图所示
比如你在mapper接口写上selectById()方法,看到报错,说这个sql语句selectById没有定义在配置文件中。它可以帮助我们定义按住enter+alt快捷键,然后就可以在配置文件写sql语句了。
1.2 查询所有数据
第一就是我们就写好mapper接口方法,第二就是通过mybatisX插件生成对应的sql statement,它只能生成对应的sql标签,以及id和resultType,其他的sql语句要自己写,第三就是写对应的测试方法。
1.编写接口方法
BrandMapper
public interface BrandMapper {
/**
* 查询所有
*/
List<Brand> selectAll();
}
2.编写SQL语句
BrandMapper.xml
<!--
namespace:名称空间
-->
<mapper namespace="com.itheima.mapper.BrandMapper">
<select id="selectAll" resultMap="brandResultMap">
select *
from tb_brand;
</select>
</mapper>
3.编写测试方法
MyBatisTest
public class MyBatisTest {
@Test
public void testSelectAll() throws IOException {
//1. 获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2. 获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//3. 获取Mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//4. 执行方法
List<Brand> brands = brandMapper.selectAll();
System.out.println(brands);
//5. 释放资源
sqlSession.close();
}
}
4.起别名解决上述问题--用as关键字
BrandMapper.xml
注意:
1.mybatis数据库的列名,跟封装对象的各个属性名必须是完全相同的,不相同会出现取值为空。
2.因为jdbc是通过赋值吧数据库数据赋值到对象属性的,所以属性名可以不同,但是这里对象属性必须和数据库当中的名字一样,要不然为null。
3.定义sql片段(id去定义),然后再去引用它(通过include中的refid属性),有点类似css中的id选择器。
4.resultMap标签有2个属性,id是唯一标识,可以随便起个别名。type是当前的resultMap要和那个类型映射。id是主键映射,result是一般字段映射。
从上面结果可以看到
brandName
和
companyName
这两个属性的数据没有封装成功,查询 实体类 和 表中的字段 发现,在 实体类中属性名是 brandName
和
companyName
,而表中的字段名为
brand_name
和
company_name
,如下图所示 。那么 我们只需要保持这两部分的名称一致这个问题就迎刃而解。
我们可以在写
sql
语句时给这两个字段起别名,将别名定义成和属性名一致即可。
<!--
namespace:名称空间
-->
<mapper namespace="com.itheima.mapper.BrandMapper">
<!--
数据库表的字段名称 和 实体类的属性名称 不一样,则不能自动封装数据
方法一:起别名:对不一样的列名起别名,让别名和实体类的属性名一样
* 缺点:每次查询都要定义一次别名
方法二: sql片段
* 缺点:不灵活
方法三: resultMap:
1. 定义<resultMap>标签
2. 在<select>标签中,使用resultMap属性替换 resultType属性
-->
<!--
方法一:起别名--用as起别名
-->
<!--<select id="selectAll" resultType="brand">
select id, brand_name as brandName, company_name as companyName, ordered, description, status
from tb_brand;
</select>-->
<!--
方法二: sql片段--定义sql片段(id去定义),然后再去引用它(通过include标签中的refid属性),有点类似css中的id选择器
-->
<!-- <sql id="brand_column">
id, brand_name as brandName, company_name as companyName, ordered, description, status
</sql>
-->
<!--
<select id="selectAll" resultType="brand">
select
<include refid="brand_column" />
from tb_brand;
</select>
-->
<!--<select id="selectAll" resultType="brand">
select *
from tb_brand;
</select>-->
</mapper>
5.使用resultMap解决上述问题---这种用的多
BrandMapper.xml
<!--
namespace:名称空间
-->
<mapper namespace="com.itheima.mapper.BrandMapper">
<!--
数据库表的字段名称 和 实体类的属性名称 不一样,则不能自动封装数据
方法一:起别名:对不一样的列名起别名,让别名和实体类的属性名一样
* 缺点:每次查询都要定义一次别名
方法二: sql片段
* 缺点:不灵活
方法三: resultMap:
1. 定义<resultMap>标签
2. 在<select>标签中,使用resultMap属性替换 resultType属性
-->
<!--
方法三: resultMap标签
id:唯一标识
type:映射的类型,支持别名
-->
<resultMap id="brandResultMap" type="brand">
<!--
id:完成主键字段的映射
column:表的列名
property:实体类的属性名
result:完成一般字段的映射
column:表的列名
property:实体类的属性名
-->
<result column="brand_name" property="brandName"/>
<result column="company_name" property="companyName"/>
</resultMap>
<select id="selectAll" resultMap="brandResultMap">
select *
from tb_brand;
</select>
<!--
方法一:起别名--用as起别名
-->
<!--<select id="selectAll" resultType="brand">
select id, brand_name as brandName, company_name as companyName, ordered, description, status
from tb_brand;
</select>-->
<!--
方法二: sql片段--定义sql片段(id去定义),然后再去引用它(通过include标签中的refid属性),有点类似css中的id选择器
-->
<!-- <sql id="brand_column">
id, brand_name as brandName, company_name as companyName, ordered, description, status
</sql>
-->
<!--
<select id="selectAll" resultType="brand">
select
<include refid="brand_column" />
from tb_brand;
</select>
-->
<!--<select id="selectAll" resultType="brand">
select *
from tb_brand;
</select>-->
</mapper>
6.小结
1.3 查看详情
在每个数据后面都有一个查看详情按钮,你点击它他会把商品详细信息列表展示在这里。每一条记录都可以查看详情,查看详情就是查看某条数据的详细信息。你单击查看详情按钮他会把当前数据的id传到后台java代码,java代码根据id完成查询,查完之后会把这条记录的数据对象返回给页面,页面就会展示这条记录详细信息。
1.编写接口方法
在 BrandMapper 接口中定义根据id查询数据的方法
/**
*
查看详情:根据
Id
查询
*/
Brand
selectById
(
int
id
);
2.编写SQL语句
在
BrandMapper.xml
映射配置文件中编写
statement
,使用
resultMap
而不是使用
resultType
<select
id
=
"selectById"
resultMap
=
"brandResultMap"
>
select *
from tb_brand where id = #{id};
</select>
注意:上述
SQL
中的
#{id}
先这样写,一会我们再详细讲解
3.编写测试方法
在 test/java 下的 com.itheima.mapper 包下的 MybatisTest类中 定义测试方法
@Test
public
void
testSelectById
()
throws
IOException
{
//
接收参数,该
id
以后需要传递过来
int
id
=
1
;
//1.
获取
SqlSessionFactory
String
resource
=
"mybatis-config.xml"
;
InputStream inputStream
=
Resources
.
getResourceAsStream
(
resource
);
SqlSessionFactory sqlSessionFactory
=
new
SqlSessionFactoryBuilder
().
build
(
inputStream
);
//2.
获取
SqlSession
对象
SqlSession sqlSession
=
sqlSessionFactory
.
openSession
();
//3.
获取
Mapper
接口的代理对象
BrandMapper brandMapper
=
sqlSession
.
getMapper
(
BrandMapper
.
class
);
//4.
执行方法
Brand brand
=
brandMapper
.
selectById
(
id
);
System
.
out
.
println
(
brand
);
//5.
释放资源
sqlSession
.
close
();
}
4.参数占位符
查询到的结果很好理解就是
id
为
1
的这行数据。而这里我们需要看控制台显示的
SQL
语句,能看到使用?进行占位。说明我们 在映射配置文件中的写的 #{id}
最终会被?进行占位。接下来我们就聊聊映射配置文件中的参数占位符。
mybatis
提供了两种参数占位符:
- #{} :执行SQL时,会将 #{} 占位符替换为?,将来自动设置参数值。从上述例子可以看出使用#{} 底层使用的是 PreparedStatement
- ${} :拼接SQL。底层使用的是 Statement ,会存在SQL注入问题。如下图将 映射配置文件中的 #{} 替换成 ${} 来看效 果
<select
id
=
"selectById"
resultMap
=
"brandResultMap"
>
select *
from tb_brand where id = ${id};
</select>
注意:从上面两个例子可以看出,以后开发我们使用 #{} 参数占位符。
5.parameterType使用
对于有参数的
mapper
接口方法,我们在映射配置文件中应该配置
ParameterType
来指定参数类型。只不过该属性都可以 省略。如下图:
<select
id
=
"selectById"
parameterType
=
"int"
resultMap
=
"brandResultMap"
>
select *
from tb_brand where id = ${id};
</select>
6.SQL语句中特殊字段处理
以后肯定会在
SQL
语句中写一下特殊字符,比如某一个字段大于某个值,如下图
可以看出报错了,因为映射配置文件是
xml
类型的问题,而
> <
等这些字符在
xml
中有特殊含义,所以此时我们需要将这些符 号进行转义,可以使用以下两种方式进行转义
下图的 <
; 就是
<
的转义字符。
1.4 多条件查询
我们经常会遇到如上图所示的多条件查询,将多条件查询的结果展示在下方的数据列表中。而我们做这个功能需要分析最终
的
SQL
语句应该是什么样,思考两个问题
条件字段
企业名称
和
品牌名称
需要进行模糊查询,所以条件应该是:
简单的分析后,我们来看功能实现的步骤:
参数:所有查询条件
结果:
List
- 在映射配置文件中编写SQL语句
- 编写测试方法并执行
1.编写接口方法
在
BrandMapper
接口中定义多条件查询的方法。
而该功能有三个参数,我们就需要考虑定义接口时,参数应该如何定义。
Mybatis
针对多参数有多种实现
- 使用 @Param("参数名称") 标记每一个参数,在映射配置文件中就需要使用 #{参数名称} 进行占位
List
<
Brand
>
selectByCondition
(
@Param
(
"status"
)
int
status
,
@Param
(
"companyName"
)
String
companyName
,
@Param
(
"brandName"
)
String
brandName
);
- 将多个参数封装成一个 实体对象 ,将该实体对象作为接口的方法参数。该方式要求在映射配置文件的SQL中使用 #{内 容} 时,里面的内容必须和实体类属性名保持一致。
List
<
Brand
>
selectByCondition
(
Brand brand
);
- 将多个参数封装到map集合中,将map集合作为接口的方法参数。该方式要求在映射配置文件的SQL中使用 #{内容} 时,里面的内容必须和map集合中键的名称一致。
List<Brand> selectByCondition(Map map);
2.编写SQL语句
在 BrandMapper.xml 映射配置文件中编写 statement ,使用 resultMap 而不是使用 resultType
<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/java 下的 com.itheima.mapper 包下的 MybatisTest类中 定义测试方法
@Test
public
void
testSelectByCondition
()
throws
IOException
{
//
接收参数
int
status
=
1
;
String
companyName
=
"
华为
"
;
String
brandName
=
"
华为
"
;
//
处理参数
companyName
=
"%"
+
companyName
+
"%"
;
brandName
=
"%"
+
brandName
+
"%"
;
//1.
获取
SqlSessionFactory
String
resource
=
"mybatis-config.xml"
;
InputStream inputStream
=
Resources
.
getResourceAsStream
(
resource
);
SqlSessionFactory sqlSessionFactory
=
new
SqlSessionFactoryBuilder
().
build
(
inputStream
);
//2.
获取
SqlSession
对象
SqlSession sqlSession
=
sqlSessionFactory
.
openSession
();
//3.
获取
Mapper
接口的代理对象
BrandMapper brandMapper
=
sqlSession
.
getMapper
(
BrandMapper
.
class
);
//4.
执行方法
//
方式一 :接口方法参数使用
@Param
方式调用的方法
//List<Brand> brands = brandMapper.selectByCondition(status, companyName, brandName);
//
方式二 :接口方法参数是 实体类对象 方式调用的方法
//
封装对象
/* Brand brand = new Brand();
brand.setStatus(status);
brand.setCompanyName(companyName);
brand.setBrandName(brandName);*/
//List<Brand> brands = brandMapper.selectByCondition(brand);
//
方式三 :接口方法参数是
map
集合对象 方式调用的方法
Map map
=
new
HashMap
();
map
.
put
(
"status"
,
status
);
map
.
put
(
"companyName"
,
companyName
);
map
.
put
(
"brandName"
,
brandName
);
List
<
Brand
>
brands
=
brandMapper
.
selectByCondition
(
map
);
System
.
out
.
println
(
brands
);
//5.
释放资源
sqlSession
.
close
();
}
4.动态SQL
上述功能实现存在很大的问题。用户在输入条件时,肯定不会所有的条件都填写,这个时候我们的
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}
针对上述的需要,
Mybatis
对动态
SQL
有很强大的支撑:
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
我们先学习
if
标签和
where
标签:
test
属性:逻辑表达式
<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>
</select>
如上的这种
SQL
语句就会根据传递的参数值进行动态的拼接。如果此时
status
和
companyName
有值那么就会值拼接这 两个条件。
执行结果如下:
但是它也存在问题,如果此时给的参数值是
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
语句。这个就可以使用
where
标签解决
作用:
替换
where
关键字
会动态的去掉第一个条件前的
and
如果所有的参数没有值则不加
where
关键字
<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>
1.5 单个条件(动态SQL)
如上图所示,在查询时只能选择
品牌名称
、
当前状态
、
企业名称
这三个条件中的一个,但是用户到底选择哪儿一个,我们并 不能确定。这种就属于单个条件的动态SQL
语句。
这种需求需要使用到
choose
(when,
otherwise
)标签
实现, 而
choose
标签类似于
Java
中的
switch
语句。
通过一个案例来使用这些标签
1.编写接口方法
在 BrandMapper 接口中定义单条件查询的方法。
/**
*
单条件动态查询
* @param brand
* @return
*/
List
<
Brand
>
selectByConditionSingle
(
Brand brand
);
2.编写SQL语句
在 BrandMapper.xml 映射配置文件中编写 statement ,使用 resultMap 而不是使用 resultType
<select
id
=
"selectByConditionSingle"
resultMap
=
"brandResultMap"
>
select *
from tb_brand
<where>
<choose>
<!--
相当于
switch-->
<when
test
=
"status != null"
>
<!--
相当于
case-->
status = #{status}
</when>
<when
test
=
"companyName != null and companyName != '' "
>
<!--
相当于
case-->
company_name like #{companyName}
</when>
<when
test
=
"brandName != null and brandName != ''"
>
<!--
相当于
case-->
brand_name like #{brandName}
</when>
</choose>
</where>
</select>
3.编写测试方法
在 test/java 下的 com.itheima.mapper 包下的 MybatisTest类中 定义测试方法
@Test
public
void
testSelectByConditionSingle
()
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);
//1.
获取
SqlSessionFactory
String
resource
=
"mybatis-config.xml"
;
InputStream inputStream
=
Resources
.
getResourceAsStream
(
resource
);
SqlSessionFactory sqlSessionFactory
=
new
SqlSessionFactoryBuilder
().
build
(
inputStream
);
//2.
获取
SqlSession
对象
SqlSession sqlSession
=
sqlSessionFactory
.
openSession
();
//3.
获取
Mapper
接口的代理对象
BrandMapper brandMapper
=
sqlSession
.
getMapper
(
BrandMapper
.
class
);
//4.
执行方法
List
<
Brand
>
brands
=
brandMapper
.
selectByConditionSingle
(
brand
);
System
.
out
.
println
(
brands
);
//5.
释放资源
sqlSession
.
close
();
}
执行测试方法结果如下:
1.6 添加数据
如上图是我们平时在添加数据时展示的页面,而我们在该页面输入想要的数据后添加
提交
按钮,就会将这些数据添加到数 据库中。接下来我们就来实现添加数据的操作。
参数:除了
id
之外的所有的数据。
id
对应的是表中主键值,而主键我们是
自动增长
生成的。
明确了该功能实现的步骤后,接下来我们进行具体的操作。
1.编写接口方法
在 BrandMapper 接口中定义添加方法。
/**
*
添加
*/
void
add
(
Brand brand
);
2.编写SQL语句
在 BrandMapper.xml 映射配置文件中编写添加数据的 statement
<insert
id
=
"add"
>
insert into tb_brand (brand_name, company_name, ordered, description, status)
values (#{brandName}, #{companyName}, #{ordered}, #{description}, #{status});
</insert>
3.编写测试方法
在 test/java 下的 com.itheima.mapper 包下的 MybatisTest类中 定义测试方法
@Test
public
void
testAdd
()
throws
IOException
{
//
接收参数
int
status
=
1
;
String
companyName
=
"
波导手机
"
;
String
brandName
=
"
波导
"
;
String
description
=
"
手机中的战斗机
"
;
int
ordered
=
100
;
//
封装对象
Brand brand
=
new
Brand
();
brand
.
setStatus
(
status
);
brand
.
setCompanyName
(
companyName
);
brand
.
setBrandName
(
brandName
);
brand
.
setDescription
(
description
);
brand
.
setOrdered
(
ordered
);
//1.
获取
SqlSessionFactory
String
resource
=
"mybatis-config.xml"
;
InputStream inputStream
=
Resources
.
getResourceAsStream
(
resource
);
SqlSessionFactory sqlSessionFactory
=
new
SqlSessionFactoryBuilder
().
build
(
inputStream
);
//2.
获取
SqlSession
对象
SqlSession sqlSession
=
sqlSessionFactory
.
openSession
();
//SqlSession sqlSession = sqlSessionFactory.openSession(true); //
设置自动提交事务,这种情况不需
要手动提交事务了
//3.
获取
Mapper
接口的代理对象
BrandMapper brandMapper
=
sqlSession
.
getMapper
(
BrandMapper
.
class
);
//4.
执行方法
brandMapper
.
add
(
brand
);
//
提交事务
sqlSession
.
commit
();
//5.
释放资源
sqlSession
.
close
();
}
执行结果如下:
4.添加-主键返回
在数据添加成功后,有时候需要获取插入数据库数据的主键(主键是自增长)。
比如:添加订单和订单项,如下图就是京东上的订单
订单数据存储在订单表中,订单项存储在订单项表中。
明白了什么时候
主键返回
。接下来我们简单模拟一下,在添加完数据后打印
id
属性值,能打印出来说明已经获取到了。
我们将上面添加品牌数据的案例中映射配置文件里
statement
进行修改,如下
<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>
在
insert
标签上添加如下属性:
- useGeneratedKeys:是够获取自动增长的主键值。true表示获取
- keyProperty :指定将获取到的主键值封装到哪儿个属性里
1.7 修改
如图所示是修改页面,用户在该页面书写需要修改的数据,点击
提交
按钮,就会将数据库中对应的数据进行修改。注意一 点,如果哪儿个输入框没有输入内容,我们是将表中数据对应字段值替换为空白还是保留字段之前的值?答案肯定是保留之 前的数据。
接下来我们就具体来实现
1.编写接口方法
在 BrandMapper 接口中定义修改方法。
/**
*
修改
*/
void
update
(
Brand brand
);
上述方法参数 Brand 就是封装了需要修改的数据,而id肯定是有数据的,这也是和添加方法的区别。
2.编写SQL语句
在 BrandMapper.xml 映射配置文件中编写修改数据的 statement 。
<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
标签可以用于动态包含需要更新的列,忽略其它不更新的列。
3.编写测试方法
在 test/java 下的 com.itheima.mapper 包下的 MybatisTest类中 定义测试方法
@Test
public
void
testUpdate
()
throws
IOException
{
//
接收参数
int
status
=
0
;
String
companyName
=
"
波导手机
"
;
String
brandName
=
"
波导
"
;
String
description
=
"
波导手机
,
手机中的战斗机
"
;
int
ordered
=
200
;
int
id
=
6
;
//
封装对象
Brand brand
=
new
Brand
();
brand
.
setStatus
(
status
);
// brand.setCompanyName(companyName);
// brand.setBrandName(brandName);
// brand.setDescription(description);
// brand.setOrdered(ordered);
brand
.
setId
(
id
);
//1.
获取
SqlSessionFactory
String
resource
=
"mybatis-config.xml"
;
InputStream inputStream
=
Resources
.
getResourceAsStream
(
resource
);
SqlSessionFactory sqlSessionFactory
=
new
SqlSessionFactoryBuilder
().
build
(
inputStream
);
//2.
获取
SqlSession
对象
SqlSession sqlSession
=
sqlSessionFactory
.
openSession
();
//SqlSession sqlSession = sqlSessionFactory.openSession(true);
//3.
获取
Mapper
接口的代理对象
BrandMapper brandMapper
=
sqlSession
.
getMapper
(
BrandMapper
.
class
);
//4.
执行方法
int
count
=
brandMapper
.
update
(
brand
);
System
.
out
.
println
(
count
);
//
提交事务
sqlSession
.
commit
();
//5.
释放资源
sqlSession
.
close
();
}
执行测试方法结果如下:
从结果中
SQL
语句可以看出,只修改了
status
字段值,因为我们给的数据中只给
Brand
实体对象的
status
属性设置值 了。这就是 set
标签的作用。
1.8 删除一行数据
如上图所示,每行数据后面都有一个
删除
按钮,当用户点击了该按钮,就会将改行数据删除掉。那我们就需要思考,这种 删除是根据什么进行删除呢?是通过主键id
删除,因为
id
是表中数据的唯一标识。
接下来就来实现该功能。
1.编写接口方法
在 BrandMapper 接口中定义根据id删除方法。
/**
*
根据
id
删除
*/
void
deleteById
(
int
id
);
2.编写SQL语句
在 BrandMapper.xml 映射配置文件中编写删除一行数据的 statement
<delete
id
=
"deleteById"
>
delete from tb_brand where id = #{id};
</delete>
3.编写测试方法
在 test/java 下的 com.itheima.mapper 包下的 MybatisTest类中 定义测试方法
@Test
public
void
testDeleteById
()
throws
IOException
{
//
接收参数
int
id
=
6
;
//1.
获取
SqlSessionFactory
String
resource
=
"mybatis-config.xml"
;
InputStream inputStream
=
Resources
.
getResourceAsStream
(
resource
);
SqlSessionFactory sqlSessionFactory
=
new
SqlSessionFactoryBuilder
().
build
(
inputStream
);
//2.
获取
SqlSession
对象
SqlSession sqlSession
=
sqlSessionFactory
.
openSession
();
//SqlSession sqlSession = sqlSessionFactory.openSession(true);
//3.
获取
Mapper
接口的代理对象
BrandMapper brandMapper
=
sqlSession
.
getMapper
(
BrandMapper
.
class
);
//4.
执行方法
brandMapper
.
deleteById
(
id
);
//
提交事务
sqlSession
.
commit
();
//5.
释放资源
sqlSession
.
close
();
}
运行过程只要没报错,直接到数据库查询数据是否还存在。
1.9 批量删除
如上图所示,用户可以选择多条数据,然后点击上面的
删除
按钮,就会删除数据库中对应的多行数据。
1.编写接口方法
在
BrandMapper
接口中定义删除多行数据的方法。
/**
*
批量删除
*/
void
deleteByIds
(
int
[]
ids
);
参数是一个数组,数组中存储的是多条数据的
id
2.编写SQL语句
在
BrandMapper.xml
映射配置文件中编写删除多条数据的
statement
。
编写
SQL
时需要遍历数组来拼接
SQL
语句。
Mybatis
提供了
foreach
标签供我们使用
foreach
标签
用来迭代任何可迭代的对象(如数组,集合)。
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>
假如数组中的
id
数据是
{1,2,3}
,那么拼接后的
sql
语句就是:
delete from
tb_brand
where
id
in
(
1
,
2
,
3
)
;
3.编写测试方法
在 test/java 下的 com.itheima.mapper 包下的 MybatisTest类中 定义测试方法
@Test
public
void
testDeleteByIds
()
throws
IOException
{
//
接收参数
int
[]
ids
=
{
5
,
7
,
8
};
//1.
获取
SqlSessionFactory
String
resource
=
"mybatis-config.xml"
;
InputStream inputStream
=
Resources
.
getResourceAsStream
(
resource
);
SqlSessionFactory sqlSessionFactory
=
new
SqlSessionFactoryBuilder
().
build
(
inputStream
);
//2.
获取
SqlSession
对象
SqlSession sqlSession
=
sqlSessionFactory
.
openSession
();
//SqlSession sqlSession = sqlSessionFactory.openSession(true);
//3.
获取
Mapper
接口的代理对象
BrandMapper brandMapper
=
sqlSession
.
getMapper
(
BrandMapper
.
class
);
//4.
执行方法
brandMapper
.
deleteByIds
(
ids
);
//
提交事务
sqlSession
.
commit
();
//5.
释放资源
sqlSession
.
close
();
}
1.10 Mybatis参数传递
Mybatis
接口方法中可以接收各种各样的参数,如下:
POJO
类型
Map
集合类型
Collection
集合类型
List
集合类型
Array
类型
其他类型
1.多个参数
如下面的代码,就是接收两个参数,而接收多个参数需要使用
@Param
注解,那么为什么要加该注解呢?这个问题要弄明白 就必须来研究Mybatis
底层对于这些参数是如何处理的。
User
select
(
@Param
(
"username"
)
String
username
,
@Param
(
"password"
)
String
password
);
<select
id
=
"select"
resultType
=
"user"
>
select *
from tb_user
where
username=#{username}
and password=#{password}
</select>
我们在接口方法中定义多个参数,
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);
代码验证:
在
UserMapper
接口中定义如下方法
User
select
(
String
username
,
String
password
);
在
UserMapper.xml
映射配置文件中定义
SQL
<select
id
=
"select"
resultType
=
"user"
>
select *
from tb_user
where
username=#{arg0}
and password=#{arg1}
</select>
或者
<select
id
=
"select"
resultType
=
"user"
>
select *
from tb_user
where
username=#{param1}
and password=#{param2}
</select>
运行代码结果如下
在映射配合文件的
SQL
语句中使用用
arg
开头的和
param
书写,代码的可读性会变的特别差,此时可以使用
@Param 注解。
在接口方法参数上使用
@Param
注解,
Mybatis
会将
arg
开头的键名替换为对应注解的属性值。
代码验证:
- 在 UserMapper 接口中定义如下方法,在 username 参数前加上 @Param 注解
User
select
(
@Param
(
"username"
)
String
username
,
String
password
);
Mybatis
在封装
Map
集合时,键名就会变成如下:
map.put("username"
,参数值
1);
map.put("arg1"
,参数值
2);
map.put("param1"
,参数值
1);
map.put("param2"
,参数值
2);
- 在 UserMapper.xml 映射配置文件中定义SQL
<select
id
=
"select"
resultType
=
"user"
>
select *
from tb_user
where
username=#{username}
and password=#{param2}
</select>
- 运行程序结果没有报错。而如果将 #{} 中的 username 还是写成 arg0
<select
id
=
"select"
resultType
=
"user"
>
select *
from tb_user
where
username=#{arg0}
and password=#{param2}
</select>
结论:以后接口参数是多个时,在每个参数上都使用
@Param
注解。这样代码的可读性更高。
2.单个参数
直接使用。要求
属性名
和
参数占位符名称
一致
直接使用。要求
map
集合的键名
和
参数占位符名称
一致
Mybatis
会将集合封装到
map
集合中,如下:
map.put("arg0"
,
collection
集合
);
map.put("collection"
,
collection
集合
;
可以使用
@Param
注解替换
map
集合中默认的
arg
键名。
Mybatis
会将集合封装到
map
集合中,如下:
map.put("arg0"
,
list
集合
);
map.put("collection"
,
list
集合
);
map.put("list"
,
list
集合
);
可以使用
@Param
注解替换
map
集合中默认的
arg
键名。
Mybatis
会将集合封装到
map
集合中,如下:
map.put("arg0"
,数组
);
map.put("array"
,数组
);
可以使用
@Param
注解替换
map
集合中默认的
arg
键名。
比如int
类型,
参数占位符名称
叫什么都可以。尽量做到见名知意
二、注解完成CRUD
使用注解开发会比配置文件开发更加方便。如下就是使用注解进行开发
@Select
(
value
=
"select * from tb_user where id = #{id}"
)
public
User
select
(
int
id
);
注意:
- 注解是用来替换映射配置文件方式配置的,所以使用了注解,就不需要再映射配置文件中书写对应的 statement
Mybatis
针对
CURD
操作都提供了对应的注解,已经做到见名知意。如下:
- 查询 :@Select
- 添加 :@Insert
- 修改 :@Update
- 删除 :@Delete
接下来我们做一个案例来使用
Mybatis
的注解开发
代码实现:
- 将之前案例中 UserMapper.xml 中的 根据id查询数据 的 statement 注释掉
- 在 UserMapper 接口的 selectById 方法上添加注解
我们课程上只演示这一个查询的注解开发,其他的同学们下来可以自己实现,都是比较简单。
注意:
在官方文档中
入门
中有这样的一段话:
所以,
注解完成简单功能,配置文件完成复杂功能。
而我们之前写的动态
SQL
就是复杂的功能,如果用注解使用的话,就需要使用到
Mybatis
提供的
SQL
构建器来完成,而对应 的代码如下:
上述代码将
java
代码和
SQL
语句融到了一块,使得代码的可读性大幅度降低。
三、动态SQL