欢迎光临
我们一直在努力

MyBatis高级应用实战:动态SQL编写、映射延迟加载、缓存配置、PageHelper分页、注解式开发详解

一、动态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 分步查询

其他位置不需要修改,只需要修改以及添加以下三处:

  • association中select位置填写sqlId。sqlId=namespace+id(id为属性类型的select的id)。其中column属性作为这条⼦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"/>

    </resultMap>

    <select id="selectBySid" resultMap="studentResultMap">
    select s.* from t_student s where sid = #{sid}
    </select>

  • 在ClazzMapper接⼝中添加⽅法
  • // 根据cid获取Clazz信息
    Clazz selectByCid(Integer cid);

  • 在ClazzMapper.xml⽂件中进⾏配置
  • <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对象关闭或提交之后,⼀级缓存中的数据才会被写⼊到⼆级缓存当中。此时⼆级缓存才可⽤。

    ⼆级缓存的失效:只要两次查询之间出现了增删改操作。⼆级缓存就会失效。
    ⼆级缓存的相关配置的属性:

  • eviction:指定从缓存中移除某个对象的淘汰算法。默认采⽤LRU策略。
    • .LRU:Least Recently Used。最近最少使⽤。优先淘汰在间隔时间内使⽤频率最低的对象。(其实还有⼀种淘汰算法LFU,最不常⽤。)
    • FIFO:First In First Out。⼀种先进先出的数据缓存器。先进⼊⼆级缓存的对象最先被淘汰。
    • SOFT:软引⽤。淘汰软引⽤指向的对象。具体算法和JVM的垃圾回收算法有关。
    • WEAK:弱引⽤。淘汰弱引⽤指向的对象。具体算法和JVM的垃圾回收算法有关。
  • flushInterval:⼆级缓存的刷新时间间隔,单位毫秒。如果没有设置。就代表不刷新缓存,⼀直会向⼆级缓存中缓存数据。除⾮执⾏了增删改。
  • readOnly:
    • true:多条相同的sql语句执⾏之后返回的对象是共享的同⼀个。性能好。但是多线程并发可能会存在安全问题。
    • false:多条相同的sql语句执⾏之后返回的对象是副本,调⽤了clone⽅法。性能⼀般。但安全。
  • size:设置⼆级缓存中最多可存储的java对象数量。默认值1024。
  • 四、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);

    最后感谢动力节点提供的优质学习资源,让我们在技术道路上能够站在巨人的肩膀上继续前行。本资料仅为学习过程的副产品,希望能帮助到更多同样在努力学习的开发者。

    赞(0)
    未经允许不得转载:171主机测评 » MyBatis高级应用实战:动态SQL编写、映射延迟加载、缓存配置、PageHelper分页、注解式开发详解
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址