一、动态SQL
1.1 if标签
多条件查询。
// 根据品牌(brand)、指导价格(guide_price)、汽⻋类型(car_type)查询Car
List<Car> selectByMultiCondition(@Param("brand") String brand, @Param("guidePrice") Double guidePrice,
@Param("carType") String carType);
对应的SQL语句:
<mapper namespace="com.powernode.mybatis.mapper.CarMapper">
<select id="selectByMultiCondition" resultType="car">
select * from t_car where 0 = 0
<if test="brand != null and brand != ''">
and brand like #{brand}"%"
</if>
<if test="guidePrice != null and guidePrice != ''">
and guide_price >= #{guidePrice}
</if>
<if test="carType != null and carType != ''">
and car_type = #{carType}
</if>
</select>
</mapper>
注:在where后面添加‘0 = 0’,可以避免第一个选项为空的情况。
1.2 where标签
where标签的作⽤:让where⼦句更加动态智能。
- 所有条件都为空时,where标签保证不会⽣成where⼦句。
- ⾃动去除某些条件前⾯多余的and或or。
// 根据多条件查询Car,使⽤where标签
List<Car> selectByMultiConditionWithWhere(@Param("brand") String brand, @Param("guidePrice") Double guidePrice,
@Param("carType") String carType);
对应的SQL语句:
<select id="selectByMultiConditionWithWhere" resultType="car">
select * from t_car
<where>
<if test="brand != null and brand != ''">
and brand like #{brand}"%"
</if>
<if test="guidePrice != null and guidePrice != ''">
and guide_price >= #{guidePrice}
</if>
<if test="carType != null and carType != ''">
and car_type = #{carType}
</if>
</where>
</select>
1.3 trim标签
trim标签的属性:
- prefix:在trim标签中的语句前添加内容
- suffix:在trim标签中的语句后添加内容
- prefixOverrides:前缀覆盖掉(去掉)
- suffixOverrides:后缀覆盖掉(去掉)
// 根据多条件查询Car,使⽤trim标签
List<Car> selectByMultiConditionWithTrim(@Param("brand") String brand, @Param("guidePrice") Double guidePrice,
@Param("carType") String carType);
对应的SQL语句:
<select id="selectByMultiConditionWithTrim" resultType="car">
select * from t_car
<trim prefix="where" suffixOverrides="and|or">
<if test="brand != null and brand != ''">
brand like #{brand}"%" and
</if>
<if test="guidePrice != null and guidePrice != ''">
guide_price >= #{guidePrice} and
</if>
<if test="carType != null and carType != ''">
car_type = #{carType}
</if>
</trim>
</select>
注:当最后一个条件为空时,添加的 suffixOverrides条件会将SQl语句后缀and给覆盖掉。
1.4 set标签
主要使⽤在update语句当中,⽤来⽣成set关键字,同时去掉最后多余的“,”
// 更新信息,使⽤set标签
int updateWithSet(Car car);
对应的SQL语句:
<update id="updateWithSet">
update t_car
<set>
<if test="carNum != null and carNum != ''">
car_num = #{carNum},
</if>
<if test="brand != null and brand != ''">
brand = #{brand},
</if>
<if test="guidePrice != null and guidePrice != ''">
guide_price = #{guidePrice},
</if>
<if test="produceTime != null and produceTime != ''">
produce_time = #{produceTime},
</if>
<if test="carType != null and carType != ''">
car_type = #{carType}
</if>
</set>
where id = #{id}
</update>
1.5 choose when otherwise
这三个标签是在⼀起使⽤的:
// 下面为语法格式,效果等同于if、else if、else
<choose>
<when></when>
<when></when>
<when></when>
<otherwise></otherwise>
</choose>
先根据品牌查询,如果没有提供品牌,再根据指导价格查询,如果没有提供指导价格,就根据⽣产⽇期查询。
List<Car> selectWithChoose(@Param("brand") String brand, @Param("guidePrice") Double guidePrice,
@Param("produceTime") String produceTime);
对应的SQL语句:
<select id="selectWithChoose" resultType="car">
select * from t_car
<where>
<choose>
<when test="brand != null and brand != ''">
brand like #{brand}"%"
</when>
<when test="guidePrice != null and guidePrice != ''">
guide_price >= #{guidePrice}
</when>
<otherwise>
produce_time >= #{produceTime}
</otherwise>
</choose>
</where>
</select>
1.6 foreach标签
1.6.1 批量删除
// 批量删除,通过foreach标签
int deleteBatchByForeach(@Param("ids") Long[] ids);
对应的SQL语句:
<!–
collection:集合或数组
item:集合或数组中的元素
separator:分隔符
open:foreach标签中所有内容的开始
close:foreach标签中所有内容的结束
–>
<!–方法一:⽤in来删除–>
<delete id="deleteBatchByForeach">
delete from t_car where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
<!–方法二:⽤or来删除–>
<delete id="deleteBatchByForeach2">
delete from t_car where
<foreach collection="ids" item="id" separator="or">
id = #{id}
</foreach>
</delete>
1.6.1 批量添加
// 批量添加,使⽤foreach标签
int insertBatchByForeach(@Param("cars") List<Car> cars);
对应的SQL语句:
<insert id="insertBatchByForeach">
insert into t_car values
<foreach collection="cars" item="car" separator=",">
(null,#{car.carNum},#{car.brand},#{car.guidePrice},#{car.produceTime},#{car.carType})
</foreach>
</insert>
1.7 sql标签与include标签
sql标签⽤来声明sql⽚段
include标签⽤来将声明的sql⽚段包含到某个sql语句当中
<sql id="carCols">
id,car_num carNum,brand,guide_priceguidePrice,produce_time produceTime,car_type carType
</sql>
<select id="selectAllRetMap" resultType="map">
select <include refid="carCols"/> from t_car
</select>
<select id="selectAllRetListMap" resultType="map">
select <include refid="carCols"/> carType from t_car
</select>
<select id="selectByIdRetMap" resultType="map">
select <include refid="carCols"/> from t_car where id = #{id}
</select>
二、MyBatis的高级映射及延迟加载
2.1 多对一
多种⽅式,常⻅的包括三种:
- 第⼀种⽅式:⼀条SQL语句,级联属性映射。
- 第⼆种⽅式:⼀条SQL语句,association。
- 第三种⽅式:两条SQL语句,分步查询。(这种⽅式常⽤:优点⼀是可复⽤。优点⼆是⽀持懒加载。)
2.1.1 级联属性映射
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private Integer sid;
private String sname;
private Clazz clazz;
}
<mapper namespace="com.powernode.mybatis.mapper.StudentMapper">
<resultMap id="studentResultMap" type="Student">
<id property="sid" column="sid"/>
<result property="sname" column="sname"/>
<result property="clazz.cid" column="cid"/>
<result property="clazz.cname" column="cname"/>
</resultMap>
<select id="selectBySid" resultMap="studentResultMap">
select s.*, c.* from t_student s join t_clazz c on s.cid = c.cid where sid = #{sid}
</select>
</mapper>
2.1.2 association(关联)
其他位置都不需要修改,只需要修改resultMap中的配置:association即可。
<resultMap id="studentResultMap" type="Student">
<id property="sid" column="sid"/>
<result property="sname" column="sname"/>
<association property="clazz" javaType="Clazz">
<id property="cid" column="cid"/>
<result property="cname" column="cname"/>
</association>
</resultMap>
2.1.3 分步查询
其他位置不需要修改,只需要修改以及添加以下三处:
<resultMap id="studentResultMap" type="Student">
<id property="sid" column="sid"/>
<result property="sname" column="sname"/>
<association property="clazz"
select="com.powernode.mybatis.mapper.ClazzMapper.selectByCid"
column="cid"/>
</resultMap>
<select id="selectBySid" resultMap="studentResultMap">
select s.* from t_student s where sid = #{sid}
</select>
// 根据cid获取Clazz信息
Clazz selectByCid(Integer cid);
<mapper namespace="com.powernode.mybatis.mapper.ClazzMapper">
<select id="selectByCid" resultType="Clazz">
select * from t_clazz where cid = #{cid}
</select>
</mapper>
2.2 多对一延迟加载
要想⽀持延迟加载,只需要在association标签中添加fetchType="lazy"即可。
将上面分步查询StudentMapper.xml⽂件修改:
<!–这里我们只查询学⽣名字,并不会执⾏关联的sql语句–>
<resultMap id="studentResultMap" type="Student">
<id property="sid" column="sid"/>
<result property="sname" column="sname"/>
<association property="clazz"
select="com.powernode.mybatis.mapper.ClazzMapper.selectByCid"
column="cid"
fetchType="lazy"/>
</resultMap>
如果在mybatis中开启全局的延迟加载,需要在mybatis配置文件中添加setting配置:
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
注:如果某个sql你不希望它⽀持延迟加载,可以将该语句的fetchType设置为eager
2.3 一对多
⼀对多的实现,通常是在⼀的⼀⽅中有List集合属性。
如:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Clazz {
private Integer cid;
private String cname;
private List<Student> stus;
}
2.3.1 collection
public interface ClazzMapper {
// 根据cid获取Clazz信息
Clazz selectByCid(Integer cid);
// 根据班级编号查询班级信息。同时班级中所有的学⽣信息也要查询。
Clazz selectClazzAndStusByCid(Integer cid);
}
对应的SQL语句:
<resultMap id="clazzResultMap" type="Clazz">
<id property="cid" column="cid"/>
<result property="cname" column="cname"/>
<collection property="stus" ofType="Student">
<id property="sid" column="sid"/>
<result property="sname" column="sname"/>
</collection>
</resultMap>
<select id="selectClazzAndStusByCid" resultMap="clazzResultMap">
select * from t_clazz c join t_student s on c.cid = s.cid where c.cid =#{cid}
</select>
2.3.2 分步查询
修改以下三个位置即可:
<resultMap id="clazzResultMap" type="Clazz">
<id property="cid" column="cid"/>
<result property="cname" column="cname"/>
<!–主要看这⾥–>
<collection property="stus"
select="com.powernode.mybatis.mapper.StudentMapper.selectByCid"
column="cid"/>
</resultMap>
<!–sql语句也变化了–>
<select id="selectClazzAndStusByCid" resultMap="clazzResultMap">
select * from t_clazz c where c.cid = #{cid}
</select>
// 根据班级编号获取所有的学⽣
List<Student> selectByCid(Integer cid);
在StudentMapper.xml文件中添加:
<select id="selectByCid" resultType="Student">
select * from t_student where cid = #{cid}
</select>
2.4 一对多延迟加载
⼀对多延迟加载机制和多对⼀是⼀样的。同样是通过两种⽅式:
- fetchType="lazy"
- 修改全局的配置setting,lazyLoadingEnabled=true,如果开启全局延迟加载,想让某个sql不使⽤延迟加载:fetchType=“eager”
三、MyBatis的缓存
mybatis的缓存:将select语句的查询结果放到缓存(内存)当中,下⼀次还是这条select语句的话,直接从缓存中取,不再查数据库。⼀⽅⾯是减少了IO。另⼀⽅⾯不再执⾏繁琐的查找算法。效率⼤⼤提升。(缓存机制只对应select语句)
- ⼀级缓存:将查询到的数据存储到SqlSession中。
- ⼆级缓存:将查询到的数据存储到SqlSessionFactory中。
- 或者集成其它第三⽅的缓存
3.1 一级缓存
⼀级缓存默认是开启的。不需要做任何配置。
原理:只要使⽤同⼀个SqlSession对象执⾏同⼀条SQL语句,就会⾛缓存。
什么情况下不⾛缓存?
- 不同的SqlSession对象。
- 查询条件变化了。
⼀级缓存失效情况包括两种:
- 第⼀次查询和第⼆次查询之间,使用sqlSession.clearCache();⼿动清空了⼀级缓存。
- 第⼀次查询和第⼆次查询之间,执⾏了增删改操作。(这个增删改和哪张表没有关系,只要有insert delete update操作,⼀级缓存就失效。)
3.2 二级缓存
⼆级缓存的范围是SqlSessionFactory,使⽤⼆级缓存需要具备以下⼏个条件:
- . 全局性地开启或关闭所有映射器配置⽂件中已配置的任何缓存。默认就是true,⽆需设置。
- 在需要使⽤⼆级缓存的SqlMapper.xml⽂件中添加配置:
- . 使⽤⼆级缓存的实体类对象必须是可序列化的,也就是必须实现java.io.Serializable接⼝public class Car implements Serializable { …}
- SqlSession对象关闭或提交之后,⼀级缓存中的数据才会被写⼊到⼆级缓存当中。此时⼆级缓存才可⽤。
⼆级缓存的失效:只要两次查询之间出现了增删改操作。⼆级缓存就会失效。
⼆级缓存的相关配置的属性:
- .LRU:Least Recently Used。最近最少使⽤。优先淘汰在间隔时间内使⽤频率最低的对象。(其实还有⼀种淘汰算法LFU,最不常⽤。)
- FIFO:First In First Out。⼀种先进先出的数据缓存器。先进⼊⼆级缓存的对象最先被淘汰。
- SOFT:软引⽤。淘汰软引⽤指向的对象。具体算法和JVM的垃圾回收算法有关。
- WEAK:弱引⽤。淘汰弱引⽤指向的对象。具体算法和JVM的垃圾回收算法有关。
- true:多条相同的sql语句执⾏之后返回的对象是共享的同⼀个。性能好。但是多线程并发可能会存在安全问题。
- false:多条相同的sql语句执⾏之后返回的对象是副本,调⽤了clone⽅法。性能⼀般。但安全。
四、MyBatis使用PageHelper
4.1 limit分页
mysql的limit后⾯两个数字:
- 第⼀个数字:startIndex(起始下标,下标从0开始)
- 第⼆个数字:pageSize(每⻚显示的记录条数)
假设已知⻚码pageNum,还有每⻚显示的记录条数pageSize,可以知道该页码的起始下标startIndex = (pageNum – 1) * pageSize。
// 通过分⻚的⽅式获取Car列表
List<Car> selectAllByPage(@Param("startIndex") Integer startIndex,
@Param("pageSize") Integer pageSize);
对应的SQl语句:
<mapper namespace="com.powernode.mybatis.mapper.CarMapper">
<select id="selectAllByPage" resultType="Car">
select * from t_car limit #{startIndex},#{pageSize}
</select>
</mapper>
4.2 PageHelper插件
第⼀步:引⼊依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.3.1</version>
</dependency>
第⼆步:在mybatis-config.xml⽂件中配置插件
// typeAliases标签下⾯进⾏配置
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
</plugin>
</plugins>
第三步:编写Java代码
CarMapper接口中:
List<Car> selectAll();
对应的sql语句:
<select id="selectAll" resultType="Car">
select * from t_car
</select>
@Test
public void testPageHelper() throws Exception{
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
SqlSession sqlSession = sqlSessionFactory.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
// 开启分⻚
PageHelper.startPage(2, 2);
// 执⾏查询语句
List<Car> cars = mapper.selectAll();
// 获取分⻚信息对象
PageInfo<Car> pageInfo = new PageInfo<>(cars, 5);
System.out.println(pageInfo);
}
关键在于在查询语句之前开启分⻚功能PageHelper.startPage(页码, 每页显示条数);,在查询语句之后封装PageInfo对象PageInfo<Car> pageInfo = new PageInfo<>(cars, 导航页码的数量);。
五、MyBatis的注解式开发
mybatis中也提供了注解式开发⽅式,采⽤注解可以减少Sql映射⽂件的配置。使⽤注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂⼀点的语句,Java 注解不仅⼒不从⼼,还会让你本就复杂的 SQL 语句更加混乱不堪。 因此,如果你需要做⼀些很复杂的操作,最好⽤ XML 来映射语句。原则:简单sql可以注解,复杂sql使⽤xml。
5.1 @Insert
public interface CarMapper {
@Insert(value="insert into t_car values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})")
int insert(Car car);
}
测试代码:
@Test
public void testSelectById() throws Exception{
// 获取sqlSession对象,根据CarMapper接口
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(Resources.getResourceAsStream("mybatis-config.xml"));
SqlSession sqlSession = sqlSessionFactory.openSession();
CarMapper carMapper = sqlSession.getMapper(CarMapper.class);
Car car = carMapper.selectById(88L);
System.out.println(car);
}
5.2 @Delete
@Delete("delete from t_car where id = #{id}")
int deleteById(Long id);
5.3 @Update
@Update("update t_car set car_num=#{carNum},brand=#{brand},guide_price=#{guidePrice},produce_time=#{produceTime},car_type=#{carType} where id=#{id}")
int update(Car car);
5.4 @ Select
@Select("select * from t_car where id = #{id}")
@Results({
@Result(column = "id", property = "id", id = true),
@Result(column = "car_num", property = "carNum"),
@Result(column = "brand", property = "brand"),
@Result(column = "guide_price", property = "guidePrice"),
@Result(column = "produce_time", property = "produceTime"),
@Result(column = "car_type", property = "carType")
})
Car selectById(Long id);
最后感谢动力节点提供的优质学习资源,让我们在技术道路上能够站在巨人的肩膀上继续前行。本资料仅为学习过程的副产品,希望能帮助到更多同样在努力学习的开发者。



