三,MyBatis动态SQL,缓存和分页插件

1 #{}和${}的区别

1.1 ${}绑定数据

  1. 不使用注解,${}中必须使用value

    <select id="selectUserById" resultMap="userResultMap">
    		<include refid="user_select_sql"></include>
    		where user_id = ${value}
    	</select>
    

    注意:高版本mybatis有优化,也可以随便写

  2. 使用注解时,${}中必须使用注解名

    public User selectUserById(@Param("id")Integer id);
    
    <select id="selectUserById" resultMap="userResultMap">
    		<include refid="user_select_sql"></include>
    		where user_id = ${id}
    	</select>
    

2.2 #{}和${}的区别

  1. #{}底层使用?占位符绑定数据

    <select id="selectUserById" resultMap="userResultMap">
    		select * from t_user
    		where user_id = #{id}
    	</select>
    

    ​ 日志细节:

    DEBUG [main] - ==>  Preparing: select * from t_user where user_id = ? 
    DEBUG [main] - ==> Parameters: 2(Integer)
    DEBUG [main] - <==      Total: 1
    
  2. ${}底层使用字符串拼接

    <select id="selectUserById" resultMap="userResultMap">
        select * from t_user
        where user_id = ${id}
    </select>
    

    日志细节:

    DEBUG [main] - ==>  Preparing: select * from t_user where user_id = 2 
    DEBUG [main] - ==> Parameters: 
    DEBUG [main] - <==      Total: 1
    

#{}和${}的区别本质上是?占位符和字符串拼接的区别:

?占位符的特点:

  • 可以防止sql注入攻击
  • 只能绑定数据,不能绑定 表名、列名、关键字、运算符

字符串拼接的特点:

  • 容易被sql注入攻击
  • 可以绑定 表名、列名、关键字、运算符

2 获取插入时的自增的主键值

<insert id="insertUser" useGeneratedKeys="true" keyColumn="user_id" keyProperty="userId">
    <!-- 
   在插入时使用id自增生成主键,
   插入结束,将主键值返回,赋值给userId
   -->
    insert into t_user values(null,#{username},#{password})
</insert>

3 动态SQL

在这里插入图片描述

动态SQL的思路:

RecommendStudentMapper.xml
通过在mapper.xml添加动态sql标签进行条件判断,从而产生不同的sql

1.1 if标签

语法:

<if test="条件">
	满足条件执行的sql
</if>

示例:

RecommendStudentMapper.java

public List<RecommendStudent> selectConditionStudents(@Param("studentName")String studentName,@Param("teacherName")String teacherName,@Param("money")Double money);

RecommendStudentMapper.xml

<select id="selectConditionStudents" resultMap="recommendStudentResultMap">
    select * from t_recommend_student
    where
    <if test="studentName != null and !studentName.isEmpty()">
        student_name like concat('%',#{studentName},'%')
    </if>
    <if test="teacherName != null and !teacherName.isEmpty()">
        and  teacher_name like concat('%',#{teacherName},'%')
    </if>
    <if test="money != null">
        and money = #{money}
    </if>
</select>
注意:test中可以直接调用对象的方法
     多个条件之间使用and或者or连接

CREATE TABLE t_recommend_student (
  `id` INT NOT NULL AUTO_INCREMENT,
  `studentName` VARCHAR (255),
  `teacherName` VARCHAR (255),
  `money` DOUBLE,
  `age` INT,
  `sex` VARCHAR (255),
  PRIMARY KEY (`id`)
)

1.2 where标签

语法:

<where>
	<if test="条件">
    	sql条件
    </if>
    <if test="条件2">
    	and |or sql条件2
    </if>
</where>

作用:

  • 去除条件sql中多余的and或者or前缀
  • 在条件sql前添加WHERE关键字

示例:

 <select id="selectConditionStudents" resultMap="recommendStudentResultMap">
        select * from t_recommend_student
        <where>
            <if test="studentName != null and !studentName.isEmpty()">
                student_name like concat('%',#{studentName},'%')
            </if>
            <if test="teacherName != null and !teacherName.isEmpty()">
               and  teacher_name like concat('%',#{teacherName},'%')
            </if>
            <if test="money != null">
                and money = #{money}
            </if>
        </where>
    </select>

注意:where标签使用的前提:and或者or在连接条件时,跟在第2个条件前

1.3 set标签

语法:

<set>
	<if test="条件1">
    	列名=#{参数},
    </if>
    <if test="条件2">
    	列名=#{参数},
    </if>
    ...
</set>

作用:

  • 自动去除条件更新语句多余的逗号后缀
  • 在更新语句前添加SET关键字

示例:

<update id="updateStudent">
        update t_recommend_student
        <set>
            <if test="studentName != null">
                studentName = #{studentName},
            </if>
            <if test="age !=null">
                age = #{age},
            </if>
            <if test="sex != null">
                sex = #{sex},
            </if>

            <if test="teacherName != null">
                teacherName = #{teacherName},
            </if>
            <if test="money != null">
                money = #{money}
            </if>
        </set>
        where
        id = #{id}
    </update>

注意:set标签使用的签替:更新语句中逗号分隔符一定跟在更新语句后,成为后缀。

1.4 trim标签(了解)

where标签的trim写法:

<select id="selectConditionStudents" resultMap="studentResultMap">
		select * from t_student
    	<!--
 			prefixOverrides:要除去的多余的前缀
			prefix:要添加的前缀
		-->
		<trim prefixOverrides="and |or " prefix="where">
			<if test="name !=null and name.length() > 0">
				student_name like '%'||#{name}||'%'
			</if>
			<if test="money != null">
				and money ${op} #{money}
			</if>
		</trim>
	</select>

set标签的trim写法:

<update id="updateStudent">
		update t_student
    	<!-- suffixOverrides:要去除的多余的后缀-->
		<trim suffixOverrides="," prefix="set">
			<if test="studentName != null">
				student_name = #{studentName},
			</if>
			<if test="age !=null">
				age = #{age},
			</if>
			<if test="sex != null">
				sex = #{sex},
			</if>
			<if test="mobile != null">
				mobile = #{mobile},
			</if>
			<if test="teacherName != null">
				teacher_name = #{teacherName},
			</if>
			<if test="money != null">
				money = #{money}
			</if>
		</trim>
		where
			student_id = #{studentId}
	</update>

1.5 foreach标签

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mdp2ZcFp-1672888735416)(MyBatis%20day03.assets/image-20210617152544434.png)]

语法:

<foreach item="变量名" collection="集合|数组" separator="分隔符">
	#{变量名}
</foreach>
注意:如果参数没有使用注解,collection属性值规则:
                              数组=>array List=>list
     如果参数使用注解,colleciton属性值必须为注解名

示例:

public void deleteAnyStudents(@Param("ids")List<Integer> ids);
<delete id="deleteAnyStudents">
		delete from t_student
		where student_id in 
		<foreach item="id" collection="ids" separator="," open="(" close=")">
			#{id}
		</foreach>
	</delete>

注意:List参数即使是1个也建议使用注解。

4 Cache(缓存)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cjqxufda-1672888735417)(MyBatis day03.assets/image-20210606231336202.png)]

MyBatis中使用缓存的思路:

  1. 如何开启缓存

  2. 缓存的作用范围

  3. 脏数据的处理

    脏数据:缓存中的数据来源于数据库,一旦对数据库进行增删改,缓存中的数据就可能跟数据库中不一致。缓存中和数据库中不一致的数据称之为脏数据。

4.1 一级缓存

  1. 如何开启

    ​ 默认开启

  2. 作用范围

    ​ 同1个SqlSession中

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nj1U7Pjs-1672888735417)(MyBatis day03.assets/image-20210606233935474.png)]

  3. 脏数据的处理

    一旦执行增删改,立刻清空缓存。

    注意:一级缓存作用范围太小(一个SQL Session内,也就是一个事务内),没有实战价值。

4.2 二级缓存

  1. 如何开启

    mybatis-config.xml

    <settings>
    		<setting name="cacheEnabled" value="true"/>
    </settings>
    在properties和typeAliases标签中添加settings标签
    

    xxxMaper.xml

    <cache size="1024" 
           eviction="LRU" 
           flushInterval="60000"/>
    size:缓存的最大数量
    eviction:缓存达到上限时的淘汰策略
    flushInterval:定义清空缓存的时间间隔(ms)
    
  2. 作用范围

    同1个namespace(同1个Mapper接口类型)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BmlIoywn-1672888735418)(MyBatis day03.assets/image-20210607090651537.png)]

    注意:需要执行sqlSession.commit()将数据保存到缓存中。

  3. 脏数据的处理

    一旦执行增删改,默认清空同1个namespace下的二级缓存。

    自定义清空缓存的策略:

    insert delete update select标签都有flushCache属性
    flushCache:true执行时清空缓存  false:不清空缓存
    insert delete update标签的flushCache默认值:true
    select标签的flushCache默认值:false
    

4.3 二级缓存的问题

二级缓存是namespace(接口类型)级别,不同类型的Mapper查询结果在不同的二级缓存下。好处:粒度细。不足:在表连接时会出现问题

在这里插入图片描述

解决方案:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nTL4qLqW-1672888735419)(MyBatis day03.assets/image-20210607093955624.png)]

cache-ref代替cache标签,和别的Mapper使用同1个二级缓存。

4.4 ehcache

ehcache是由Java编写成熟的缓存组件,功能强大。

准备工作:导入ehcache相关的jar包

pom.xml
<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.2.0</version>
</dependency>

  1. 开启缓存

    mybatis-config.xml

    <settings>
    		<setting name="cacheEnabled" value="true"/>
    </settings>
    在properties和typeAliases标签中添加settings标签
    

    xxxMapper.xml

    <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
    

    ehcache.xml

    直接复制到 main/resources根目录

  2. 作用范围

    同1个namespace

  3. 脏数据的处理

    一旦执行增删改, 默认清空同1个namespace下的缓存。

注意:ehcache和MyBatis集成后,也有二级缓存的问题,解决方案也是一样。

5 PageHelper分页插件

在Web系统中,分页是一种常见的功能,之前写的分页方法开发过程比较麻烦,且移植性也不高(分页语法和具体的数据库耦合了)。PageHelper利用mybatis拦截器的机制,实现分页查询,支持常见的 12 种数据库的物理分页,并提供了多种使用方式。

5.1 PageHelper引言

在这里插入图片描述

存在的问题:

  • 编写过程繁琐:需要显式提供2个查询语句
  • 分页语句与数据库耦合:不同数据库的分页语法不同,移植性差。

解决方案:PageHelper分页插件
在这里插入图片描述

在mapper中,只需要做如下开发:

  • 只需要提供查询数据的方法,不需要提供统计方法
  • 查询数据的方法要定义2个分页参数,且Param注解必须为 pageNumpageNum
  • 提供的sql语句不需要显示写出分页部分

5.2 PageHelper的使用

5.2.1 引入插件,并配置
  1. pom.xml添加 PageHelper依赖

    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper</artifactId>
        <version>5.2.0</version>
    </dependency>
    
  2. mybatis-config.xml配置,开启分页插件功能

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x5U33Etl-1672888735421)(MyBatis day03.assets/image-20210701225827308.png)]

    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
            <!-- helperDialect 设置数据库类型-->
            <property name="helperDialect" value="mysql"/>
            <!-- 开启注解式分页功能-->
            <property name="supportMethodsArguments" value="true"/>
        </plugin>
    </plugins>
    
5.2.2 使用插件分页
  1. 编码方式

    1. UserMapper.java和 UserMapper.xml

      public interface UserMapper {
          public List<User> selectPageUsersWithNoArguments();
      }
      
      <select id="selectPageUsersWithNoArguments" resultType="user">
      	select * from t_user
      </select>
      
    2. 使用插件的编码

      基本的使用:

      PageHelper.startPage(1,5);//设置pageNum和pageSize
      List<User> users = userMapper.selectPageUsersWithNoArguments();//执行mapper的查询方法
      
       //将list包装成pageInfo方便获取更多分页数据,第1个参数限制生成的导航超链接页码的总个数
      PageInfo<User> pageInfo = new PageInfo<>(users,10);
      //2个核心方法
      pageInfo.getTotal();//获取总条数
      pageInfo.getPages();//获取总页数
      
      //1个实用方法
      //获取导航超链接页码数组,最多不会超过设置的10个页码
      int[] navigatepageNums = pageInfo.getNavigatepageNums();
      
      //多个封装的简化方法
      pageInfo.isIsFirstPage();//当前页是否是第1页
      pageInfo.isIsLastPage();//当前页是否是最后1页
      pageInfo.getPrePage();//前一页的页码
      pageInfo.getNextPage();//下一页的页码
      
      

      简洁的使用:

      PageInfo<User> pageInfo2 = PageHelper.startPage(1, 5)
                      .doSelectPageInfo(() -> userMapper.selectPageUsersWithNoArguments());
      

      另一个工具方法:

      //offsetPage接收的不是pageNum和pageSize,而是limit后需要的2个数值
      PageInfo<User> pageInfo3 = PageHelper.offsetPage(0, 5)
                      .doSelectPageInfo(() -> userMapper.selectPageUsersWithNoArguments());
      
  2. 注解参数方式

    1. UserMapper.java

      public interface UserMapper {
      	//将2个分页参数使用注解命名,必须为pageNum和pageSize
          public List<User> selectPageUsers(@Param("pageNum")Integer pageNum,
                                            @Param("pageSize")Integer pageSize);
      }
      
    2. UserMapper.xml

      <select id="selectPageUsers" resultType="user">
      	select * from t_user
      </select>
      

6 Lombok工具

Lombok 是一种 Java™ 实用工具,可用来帮助开发人员消除 Java 的某些冗长代码,尤其是对于简单的 Java 对象(POJO),常用于实体类的简化开发。

6.1 Lombok引言

之前编写的实体类

public class User implements Serializable {
    private Integer userId;
    private String username;
    private String password;

    public User() {
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "userId=" + userId +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

实体类在开发时,通常会有一些共性的要求:

  • 属性提供无参和有参构造方法
  • 为所有属性提供get和set方法
  • 重写equls、hashCode和toString方法

即使可以通过IDE快速生成,仍很麻烦而且后续维护成本也比较高:一旦增删改一些属性,就要重写编写相关方法。Lombok就是为了简化Java中的模板代码而生的。

6.2 使用Lombok

  1. IDEA中安装lombok插件。IDEA2021版后已经内置该插件,可以不用手动安装

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pvGOSRt7-1672888735421)(MyBatis day03.assets/image-20210701235217440.png)]

  2. pom.xml引入依赖

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.16</version>
    </dependency>
    
  3. 在实体类上,通过Lombok内置注解简化开发

    @Data
    public class User implements Serializable {
        private Integer userId;
        private String username;
        private String password;
    }
    

在这里插入图片描述

6.3 Lombok常用注解

注解作用
@Setter注解在类或属性上;为属性提供 setter方法
@Getter注解在类或属性上;为属性提供 getter方法
@NoArgsConstructor注解在类上;为类提供一个无参的构造方法
@AllArgsConstructor 注解在类上;为类提供一个全参的构造方法
@EqualsAndHashCode 注解在类上;为类提供一个equals和hashCode的方法
@ToString 注解在类上;为类提供一个toString方法
@Data复合多个注解:@ToString, @EqualsAndHashCode, @Getter , @Setter

更多注解 (projectlombok.org)

典型案例:

@Data//get、set、equals、hashCode、toString
@AllArgsConstructor//有参
@NoArgsConstructor//无参
public class User implements Serializable {
    private Integer userId;
    private String username;
    private String password;

}

;为类提供一个全参的构造方法 |
| @EqualsAndHashCode | 注解在类上;为类提供一个equals和hashCode的方法 |
| @ToString | 注解在类上;为类提供一个toString方法 |
| @Data | 复合多个注解:@ToString, @EqualsAndHashCode, @Getter , @Setter |

更多注解 (projectlombok.org)

典型案例:

@Data//get、set、equals、hashCode、toString
@AllArgsConstructor//有参
@NoArgsConstructor//无参
public class User implements Serializable {
    private Integer userId;
    private String username;
    private String password;

}

源代码地址:https://download.csdn.net/download/qq_36827283/87364348

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

斑马有点困

原创不易,多谢打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值