上述功能实现存在很大的问题。用户在输入条件时,肯定不会所有的条件都填写,这个时候我们的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 标签:
-
if 标签:条件判断
-
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 标签
-
作用:
-
替换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>
-
注意:需要给每个条件前都加上 and 关键字。
2.4 单个条件(动态SQL)
如上图所示,在查询时只能选择 品牌名称
、当前状态
、企业名称
这三个条件中的一个,但是用户到底选择哪儿一个,我们并不能确定。这种就属于单个条件的动态SQL语句。
这种需求需要使用到 choose(when,otherwise)标签
实现, 而 choose
标签类似于Java 中的switch语句。
通过一个案例来使用这些标签
2.4.1 编写接口方法
在 BrandMapper
接口中定义单条件查询的方法。
/**
* 单条件动态查询
* @param brand
* @return
*/
List<Brand> selectByConditionSingle(Brand brand);
2.4.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>
2.4.3 编写测试方法
在 test/java
下的 org.example.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();
}
执行测试方法结果如下:
2.5 添加数据
如上图是我们平时在添加数据时展示的页面,而我们在该页面输入想要的数据后添加 提交
按钮,就会将这些数据添加到数据库中。接下来我们就来实现添加数据的操作。
-
编写接口方法
-
参数:除了id之外的所有的数据。id对应的是表中主键值,而主键我们是 ==自动增长== 生成的。
-
编写SQL语句
-
编写测试方法并执行
明确了该功能实现的步骤后,接下来我们进行具体的操作。
2.5.1 编写接口方法
在 BrandMapper
接口中定义添加方法。
/**
* 添加
*/
void add(Brand brand);
2.5.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>
2.5.3 编写测试方法
在 test/java
下的 org.example.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();
}
执行结果如下:
2.5.4 添加-主键返回
上述是获取不到插入的数据的id的
测试方法
@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);
System.out.println("id:"+brand.getId());
//提交事务
sqlSession.commit();
//5. 释放资源
sqlSession.close();
}
执行结果
id:null
在数据添加成功后,有时候需要获取插入数据库数据的主键(主键是自增长)。
比如:添加订单和订单项,如下图就是京东上的订单
订单数据存储在订单表中,订单项存储在订单项表中。
-
添加订单数据
添加订单项数据,订单项中需要设置所属订单的id
明白了什么时候 主键返回
。接下来我们简单模拟一下,在添加完数据后打印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 :指定将获取到的主键值封装到哪儿个属性里
再次执行testAdd()方法
执行结果
id:13
2.6修改
如图所示是修改页面,用户在该页面书写需要修改的数据,点击 提交
按钮,就会将数据库中对应的数据进行修改。注意一点,如果哪儿个输入框没有输入内容,我们是将表中数据对应字段值替换为空白还是保留字段之前的值?答案肯定是保留之前的数据。
接下来我们就具体来实现
2.6.1 编写接口方法
在 BrandMapper
接口中定义修改方法。
/**
* 修改
*/
void update(Brand brand);
上述方法参数 Brand 就是封装了需要修改的数据,而id肯定是有数据的,这也是和添加方法的区别。
2.6.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 标签可以用于动态包含需要更新的列,忽略其它不更新的列。
2.6.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
标签的作用。
2.7 删除一行数据
如上图所示,每行数据后面都有一个 删除
按钮,当用户点击了该按钮,就会将改行数据删除掉。那我们就需要思考,这种删除是根据什么进行删除呢?是通过主键id删除,因为id是表中数据的唯一标识。
接下来就来实现该功能。
2.7.1 编写接口方法
在 BrandMapper
接口中定义根据id删除方法。
/**
* 根据id删除
*/
void deleteById(int id);
2.7.2 编写SQL语句
在 BrandMapper.xml
映射配置文件中编写删除一行数据的 statement
<delete id="deleteById">
delete from tb_brand where id = #{id};
</delete>
2.7.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();
}
运行过程只要没报错,直接到数据库查询数据是否还存在。
2.8 批量删除
如上图所示,用户可以选择多条数据,然后点击上面的 删除
按钮,就会删除数据库中对应的多行数据。
2.8.1 编写接口方法
在 BrandMapper
接口中定义删除多行数据的方法。
/**
* 批量删除
*/
void deleteByIds(int[] ids);
参数是一个数组,数组中存储的是多条数据的id
2.8.2 编写SQL语句
在 BrandMapper.xml
映射配置文件中编写删除多条数据的 statement
。
编写SQL时需要遍历数组来拼接SQL语句。Mybatis 提供了 foreach
标签供我们使用
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>
假如数组中的id数据是{1,2,3},那么拼接后的sql语句就是:
-
delete from tb_brand where id in (1,2,3);
2.8.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(); }