MyBatis
Mybatis是一款优秀的持久层框架,用于简化JDBC开发
使用Mybatis查询user表中所有数据
- 创建user表,添加数据
- 创建模块,导入坐标
- 编写MyBatis核心配置文件(mybatis-config.xml)—> 替换链接信息 解决硬编码问题
- 编写SQL映射文件(userMapper.xml)—> 统一管理sql语句,解决编码问题
- 编码:定义POJO类(User.java)
加载核心配置文件,获取SqlSessionFactory对象(MyBatisDemo.java)
获取SqlSession对象,执行SQL语句
释放资源
Mapper代理开发
- 定义与SQL映射文件同名的Mapper接口,并且将Mapper接口和SQL映射文件放置在同一目录下
- 设置SQL映射文件的namespace属性为Mapper接口全限定名
- 在Mapper接口种定义方法,方法名就是SQL映射文件中的SQL语句的id,并保持参数类型和返回值类型一致
- 编码:
通过SqlSession的getMapper方法获取Mapper接口的代理对象
调用对应方法完成sql的排序
条件查询
数据库表的字段名称和实体类的属性名称不一样,则不能自动封装数据:
-
起别名:对不一样的列名起别名,让别名和实体类的属性名一样
缺点:每一次查询都要定义一次别名
使用sql片段起别名
缺点:不灵活<!--sql片段--> <sql id="goods_colum"> id, title as name, price </sql> <select id="selectAllGooods" resultType="Goods"> select <include refid="goods_colum" /> from tb_goods; </select>
-
resultMap:
1.定义标签
2.在标签中,使用resultMap属性替换 resultType属性<!-- resultMap: id ;唯一的标识 type:映射的类型,支持别名 --> <resultMap id="goodsResultMap" type="goods"> <!-- id:完成主键字段的映射 colum:表的列名 property:实体类的属性名 result:完成一般字段的映射 colum:表的列名 property:实体类的属性名 --> <result column="title" property="name"></result> </resultMap> <select id="selectAllGooods" resultMap="goodsResultMap"> select * from tb_goods; </select>
参数占位符:
- #{}:预编译,会将其替换为?,为了防止SQl注入
- ${}:字符串替换,拼接sql,会存在SQL注入问题
- 使用时机:
*参数传递的时候:#{}
*表名或者列名不固定的情况下:${}会存在SQL注入的问题
传入参数类型parameterType:可以省略
特殊字符处理:
-
转义字符:小于号尖括号左端(<)在xml中会去寻找右括号(>)所以不会单独存在会产生错误
(<)
<select id="selectById" parameterType="int" resultMap="goodsResultMap"> select * from tb_goods where id < #{id}; </select>
-
CDATA区:当作纯文本处理
<select id="selectById" parameterType="int" resultMap="goodsResultMap"> select * from tb_goods where id <![CDATA[ < ]]> #{id}; </select>
多条件查询:
参数接收:
- 散装参数:如果方法中有多个参数,需要使用@Param(“SQL参数占位符名称”)
List<Goods> selectByCondition(@Param("price1")double prise, @Param("name")String title);
- 对象参数:对象的属性名称要和参数占位符一致,只需要保证SQL中的参数名和实体类属性名对应上
List<Goods> selectByCondition(Goods goods);//将参数封装
- map集合参数:只需要保证SQL中的参数名和map集合的键名的名称对应上
List<Goods> selectByCondition(Map map);
动态查询
-
if语句:
<select id="selectByCondition" resultMap="goodsResultMap"> select * from tb_goods where <if test="price1 > 0 and price1 != null"> price < #{price1} </if> <if test="price2 >= price1 and price2 > 0 and price2 != null"> and price > #{price2} </if> <if test="name != null and name != ''"> and title like #{name}; </if> </select>
test:逻辑表达式
问题第一个参数不能为空
会出现错误,解决:- 1=1恒等式:
<select id="selectByCondition" resultMap="goodsResultMap"> select * from tb_goods where 1=1 <if test="price1 > 0 and price1 != null"> and price < #{price1} </if> <if test="price2 >= price1 and price2 > 0 and price2 != null"> and price > #{price2} </if> <if test="name != null and name != ''"> and title like #{name}; </if> </select>
- 替换 where 关键字
<select id="selectByCondition" resultMap="goodsResultMap"> select * from tb_goods <where> <if test="price1 > 0 and price1 != null"> and price < #{price1} </if> <if test="price2 >= price1 and price2 > 0 and price2 != null"> and price > #{price2} </if> <if test="name != null and name != ''"> and title like #{name}; </if> </where> </select>
-
choose(单条件的动态查询)
<select id="selectByConditionSingle" resultMap="goodsResultMap"> select * from tb_goods # where <!--无参数参数传入时会报错,使用<where>解决--> <where> <choose><!--相当于switch--> <when test="price > 0 and price != null"> price < #{price} </when><!--case--> <when test="name != null and name != ''"> title like #{name} </when> </choose> </where> </select>
多个条件传入也只有第一个会使用到,使用可避免无参数传入时出现异常
添加
<insert id="add">
insert into tb_goods(title, price)
value (#{name},#{price});
</insert>
步骤:
- 编写接口方法:Mapper接口
void add(Goods good);
- 参数:除了id之外的所有数据
- 结果:void
- 编写SQL语句:SQL映射文件
(代码如上) - 执行方法,测试
- MyBatis事务:
- openSession():默认开启事务,进行增删改操作后需要使用
sqlSession.commit();
手动提交事务 - openSession(true):可以设置为自动提交事务(关闭事务)
返回添加数据的主键:
<insert id="add" useGeneratedKeys="true" keyProperty="id">
insert into tb_goods(title, price)
value (#{name}, #{price});
</insert>
修改
修改全部字段
- 编写接口方法:Mapper方法
-
参数:所有数据
-
结果:int
void update(Goods good);
-
编写SQL语句:SQL映射文件
<update id="updateAllById"> update tb_goods set title = #{name}, price = #{price} where id = #{id}; </update>
-
执行方法,测试
修改动态字段
<update id="updateAllById">
update tb_goods
<set>
<if test="name != null and name != ''">
title = #{name},
</if>
<if test="price != null and price != 0">
price = #{price}
</if>
</set>
where id = #{id};
</update>
删除
删除一个
<delete id="deleteById">
delete
from tb_goods
where id = #{id};
</delete>
步骤:
- 编写接口方法:Mapper接口
- 参数:id
- 结果:void
- 编写SQL语句:SQL映射文件
- 执行方法,测试
批量删除
int deleteAnyId(@Param("ids")int[] ids);//如果没有Param那么GoodsMapper.xml中就必须collection="array"
<delete id="deleteAnyId">
delete
from tb_goods
where id in
(
<foreach collection="ids" separator="," item="id">
#{id}
</foreach>
);
</delete>
<delete id="deleteAnyId">
delete
from tb_goods
where id in
<foreach collection="ids" separator="," item="id" open="(" close=")">
#{id}
</foreach>
;
</delete>
必须使用item不然会有异常
步骤:
- 编写接口方法:Mapper接口
- 参数:id数组
- 结果:void
- 编写SQL语句:SQL映射文件
- mybatis会将数组参数,封装为一个Map集合:
- 默认:array = 数组
- 使用@Param注解会改变map集合默认的key名称
- 执行方法,测试
MyBatis参数传递
MyBatis接口方法中可以接收各种各样的参数,MyBatis底层对这些参数进行不同的封装处理方式
- 单个参数:(使用@Param(“SQL语句参数占位符一致”)注解的形式,不使用默认)
- POJO类型:直接使用,属性名 和 参数占位符名称一致
- Map集合:直接使用,键名 和 参数占位符一致
- Collection:封装成Map集合
map.put(“arg0”,collection集合);
map.put(“collection”,collection集合); - List:封装成Map集合
map.put(“arg0”,list集合);
map.put(“collection”,list集合);
map.put(“list”,list集合); - Array:封装成Map集合
map.put(“arg0”,数组);
map.put(“array”,数组); - 其他类型:直接使用
- 多个参数:
map.put(“arg0”,参数值1);
map.put(“param1”,参数值1);
map.add(“param2”,参数值2);
map.add(“arg1”,参数值2);
使用Param是替用arg命名
map.put(“username”,参数值1);
map.put(“param1”,参数值1);
map.add(“param2”,参数值2);
map.add(“password”,参数值2);
使用ParamNameResolver类来进行参数封装,
@Param(“SQL语句参数占位符一致”)
使用注解完成增删改查
面试题
面试题三:#{}和${}的区别是什么?
${}是字符串替换,#{}是预处理;
Mybatis在处理 时,就是把 {}时,就是把 时,就是把{}直接替换成变量的值。而Mybatis在处理#{}时,会对sql语句进行预处理,将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
使用#{}可以有效的防止SQL注入,提高系统安全性。
面试题四:Mybatis的Xml映射文件中,不同的Xml映射文件,id是否可以重复?
不同的Xml映射文件,如果配置了namespace,那么id可以重复;如果没有配置namespace,那么id不能重复;
原因就是namespace+id是作为Map的key使用的,如果没有namespace,就剩下id,那么,id重复会导致数据互相覆盖。有了namespace,自然id就可以重复,namespace不同,namespace+id自然也就不同。
备注:在旧版本的Mybatis中,namespace是可选的,不过新版本的namespace已经是必须的了。