【Mybatis】配置文件完成增删改查
MyBatis的配置参数内容可以参考配置_MyBatis中文网
目标
- 查询
- 查询所有数据
- 查看详情
- 条件查询
- 添加
- 修改
- 修改全部字段
- 修改动态字段
- 删除
- 删除一个
- 批量删除
准备环境
- 数据库表tb_brand
- 实体类 Brand
- 测试用例
- 安装 MyBatisX 插件
数据库表
创建如下内容的表,并添加数据
drop table if exists tb_brand;
create table tb_brand(
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
创建含有如下字段的实体类Brand,getter和setter方法以及toString方法可以通过idea快捷键生成,就不写了
public class Brand {
private int id;
private String brandName;
private String companyName;
private int ordered;
private String description;
private int status;
}
测试用例
在测试test文件夹下创建一个测试文件
MybatisX插件
简介
MyBatisX是一款基于IDEA的快速开发插件,为效率而生,主要功能有:
- xml和接口方法相互跳转
- 根据接口方法生成statement
安装
直接在idea的plugin下搜索就可以了
查询
查询所有数据
步骤
- 编写接口方法:Mapper接口
- 参数:无
- 结果:
List<Brand>
public interface BrandMapper {
/**
* 查询所有
*/
List<Brand> selectAll();
}
- 编写sql语句:sql映射文件
这一步MyBatisX帮了很大的忙,安装插件后,在BrandMapper.java中就会出现这样一只小鸟,如果selectAll还没有对应的statement,它就会提示你给你生成一个statement(但是sql语句需要自己写)
点击这个小鸟会跳到BrandMapper.xml文件
查询所有,这个需求比较简单,但有需要注意的地方,如果用传统方法,实体类属性名和数据库表的字段名称其实是不一样的,因此后面查询是不会自动封装数据的,有两种可以解决的方法,这里只介绍最常用的resultMap方法:
在
<resultMap>
标签内填写列名和实体类字段名对应的关系,如<result>
标签的内容。
<resultMap>
标签有属性id:用于表示这个map的名称,type:指向对应的实体类
<result>
标签的属性column:表示数据库列名,property:表示实体类的属性名
接着在select标签内去掉resultType
属性,而是用resultMap
属性,指向对应的Map即可
<mapper namespace="com.test.mapper.BrandMapper">
<!--传统方法-->
<!-- <select id="selectAll" resultType="com.test.pojo.Brand">-->
<!-- select * from tb_brand;-->
<!-- </select>-->
<resultMap id="brandResultMap" type="com.test.pojo.Brand">
<result column="brand_name" property="brandName"/>
<result column="company_name" property="companyName"/>
</resultMap>
<select id="selectAll" resultMap="brandResultMap">
select * from tb_brand;
</select>
</mapper>
- 执行方法,测试
大部分的内容都是一致的,到获取sqlsession这里都是可以重复使用的,具体见下面:
public class MyBatisTest {
@Test
public void testSelectAll() throws IOException {
//加载mybatis的核心配置文件
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取sqlsessionFactory对象,执行sql
SqlSession sqlSession = sqlSessionFactory.openSession();
BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
List<Brand> brands = mapper.selectAll();
System.out.println(brands);
}
}
执行的结果是:
[Brand{id=1, brandName='三只松鼠', companyName='三只松鼠股份有限公司', ordered=5, description='好吃不上火', status=0}, Brand{id=2, brandName='华为', companyName='华为技术有限公司', ordered=100, description='华为致力于把数字世界带入每个人、每个家庭、每个组织,构建万物互联的智能师姐', status=1}, Brand{id=3, brandName='小米', companyName='小米科技有限公司', ordered=50, description='are you ok', status=1}]
查看详情
需求
通过参数id,查询某一条指定的brand
步骤
- 编写接口方法:Mapper接口
- 参数:id
- 结果:
Brand
- 编写sql语句
<select id="selectById" resultMap="brandResultMap">
select * from tb_brand where id=#{id};
</select>
具体讲一下sql语句的一些细节,在mapper.xml中有这样一个占位符的用法,首先讲一下占位符。
占位符
在这个映射文件中有两种形式的占位符:#和$,两者的区别在于:
#
:会将参数替换为?,为了防止sql注入$
:只是将sql进行拼接,不替换,会存在sql注入的问题
因此它们的使用场景为:参数传递的时候使用#{},而${}也不是完全没有用,也可以用在表名或列名不固定的情况,但是依然存在sql注入的可能性
指定参数类型
可以在<select>
标签添加属性:parameterType,以指定传入的参数的数据类型,但是可以省略
特殊字符处理
由于是在xml文件中写sql语句,因此当遇到要查询<
条件的时候,xml就会认为是某一个标签的开始,从而导致报错,为了解决这个问题,可以采用两种方式:
- 转移符号
- CDATA区
在CDATA区中,里面的内容都会被当作纯文本
<select id="selectById" resultMap="brandResultMap">
select * from tb_brand where id
<![CDATA[
<
]]>
#{id};
</select>
- 执行方法,测试
条件查询
多条件查询
需求
用户可以根据状态、企业名称、品牌名称进行搜索,其中企业名称和品牌名称进行模糊搜索。
步骤
- 编写接口方法:Mapper接口
- 参数:所有查询条件
- 结果:
List<Brand>
在这一节中重点讲一下多条件查询的三种不同接收方式:
@Param()
采用@Param()的散装参数模式,其中@Param()中的值要和对应的占位符的值一致
List<Brand> selectByCondition(@Param("status") int status, @Param("companyName") String companyName, @Param("brandName") String brandName);
对象参数
在接口中设置传入的参数为具体的pojo实体类,注意对象的属性名称和参数占位符名称一致。这里的pojo类需要在代码内封装后传入。
List<Brand> selectByCondition(Brand brand);
- 编写SQL语句:SQL映射文件
<select id="selectByCondition" resultMap="brandResultMap">
select *
from tb_brand
where status = #{status}
and company_name like #{companyName}
and brand_name like #{brandName}
</select>
- 执行方法,测试
这里的写测试方法和前面的大差不差,主要是由于这里需要模糊查询,在sql映射文件中并没有办法知道具体的模糊查询语句是什么,因此需要在接收参数之后对参数进行处理,如这里的需求是包含有"华为"的内容都要查询,因此在companyName
和brandName
的前后都加上%
。
public class MyBatisTest {
@Test
public void testSelectAll() throws IOException {
//接收参数
int status = 1;
String companyName = "华为";
String brandName = "华为";
//处理参数,由于是模糊查询,需要加上%
companyName = "%" + companyName + "%";
brandName = "%" + brandName + "%";
//采用对象参数,需要将参数封装进对象内,对象参数方式
Brand b = new Brand();
b.setStatus(status);
b.setCompanyName(companyName);
b.setBrandName(brandName);
//采用map集合,只要保证sql的占位符名称和map的key对应上即可
Map map = new HashMap();
map.put("status", status);
map.put("companyName", companyName);
map.put("brandName", brandName);
//加载mybatis的核心配置文件
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取sqlsessionFactory对象,执行sql
SqlSession sqlSession = sqlSessionFactory.openSession();
BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
List<Brand> brands=mapper.selectByCondition(status,companyName,brandName);//@Param()方式
List<Brand> brands=mapper.selectByCondition(b);//对象参数方式
List<Brand> brands = mapper.selectByCondition(map);//map集合方式
System.out.println(brands);
}
}
动态条件查询
需求:由于用户查询的时候不一定会用上所有的条件,所以需要动态条件查询,这就需要sql语句根据用户的输入或者外部条件的变化而变化,这被称为动态sql
其他的可以参考动态SQL_MyBatis中文网
动态sql需要在sql映射中添加if
条件标签,然后在test
后面接逻辑表达式。但是直接像下面这种写法,如果没有满足第一个条件,那么sql语句where后面就会以and company_name
开始,导致sql语句错误。
<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>
为了满足语法要求,可以做如下改变:
- 在where语句后添加 1=1 恒等式
- 将and放到每一个条件之前
如下图所示:
代码为:
<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>
可以便捷地解决上面这个问题,在MyBatis中文网中有如下描述:
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
单条件查询
用一个<choose>
里面嵌套多个<when>
来进行单条件查询,如果符合when当中的其中一个就执行,但是需要防止一种情况,如果没有一个条件符合的话,就会报错,因此需要写一个otherwise,当没有一个条件符合,就会执行otherwise的东西
添加
步骤
- 编写接口方法:Mapper接口
- 参数:除了id之外的所有数据
- 结果:void
- 编写sql语句:sql映射文件
- 执行方法,测试
1.编写接口方法
根据要求编写如下接口方法:
void add(Brand brand);
2.编写sql语句
<insert id="add">
insert into tb_brand(brand_name,company_name,ordered,description,status)
values (#{brandName},#{companyName},#{ordered},#{description},#{status});
</insert>
执行方法,测试
测试方法和前面基本一样,但是注意如果不设置sqlSessionFactory.openSession()
的参数为true,则需要在后面手动提交事务,否则无法更新到数据库里。
public class MyBatisTest {
@Test
public void testAdd() throws IOException {
//接收参数
int status = 1;
String companyName = "波导手机";
String brandName = "波导";
String description = "手机中的战斗机";
int ordered = 100;
//采用对象参数,需要将参数封装进对象内
Brand b = new Brand();
b.setStatus(status);
b.setBrandName(brandName);
b.setCompanyName(companyName);
b.setDescription(description);
b.setOrdered(ordered);
//加载mybatis的核心配置文件
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取sqlsessionFactory对象,执行sql
SqlSession sqlSession = sqlSessionFactory.openSession();//传入参数true就代表自动提交事务
BrandMapper mapper = sqlSession.getMapper(BrandMapper.class);
mapper.add(b);
//提交事务,否则不会加入到数据库中
sqlSession.commit();
//释放资源
sqlSession.close();
}
}
主键返回
主键返回是指在数据添加成功后,需要获取插入数据库的主键的值,比如:添加订单和订单项,并返回订单id
在insert标签里填写这两个属性,就可以绑定id,当插入成功后返回id。
useGeneratedKeys="true" keyProperty="id"
修改
修改全部字段
- 编写接口方法:Mapper接口
- 参数:除了id之外的所有数据
- 结果:void
- 编写sql语句:sql映射文件
- 执行方法,测试
编写接口方法
int update(Brand brand);//返回影响的行数
编写sql语句
<update id="update">
update tb_brand
set brand_name=#{brandName},
company_name=#{companyName},
ordered=#{ordered},
description=#{description},
status=#{status}
where id=#{id};
</update>
修改动态字段
- 编写接口方法:Mapper接口
- 参数:部分数据,封装到对象中
- 结果:void
- 编写sql语句:sql映射文件
- 执行方法,测试
编写sql语句
使用<set>
和<if>
标签嵌套。
这里有个问题:如果封装进来的内容不包含数字部分,数字部分就会被置为0。可能需要设置默认值?
<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>
删除
删除一个
编写接口方法
void deleteById(int id);
编写sql语句
<delete id="deleteById">
delete from tb_brand where id=#{id};
</delete>
批量删除
编写接口方法
这里和前面不一样的地方是,接受的参数是一个数组
void deleteByIds(@Param("ids")int[]ids);
编写sql语句:sql映射文件
<delete id="deleteByIds">
delete from tb_brand
where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
通过标签<foreach>
可以用来遍历传入的数组的元素,其中collection
代表要遍历的集合,item
是集合元素的代称。separator
是分隔符,因为不加分隔符,foreach
就会生成多个#{id},没有用逗号间隔开。open
是遍历开始之前添加的符号,close
是遍历结束后添加的符号。