MyBatis day03
1 #{}和${}的区别
1.1 ${}绑定数据
-
不使用注解,${}中必须使用value
<select id="selectUserById" resultMap="userResultMap"> <include refid="user_select_sql"></include> where user_id = ${value} </select>
注意:高版本mybatis有优化,也可以随便写
-
使用注解时,${}中必须使用注解名
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 #{}和${}的区别
-
#{}底层使用?占位符绑定数据
<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
-
${}底层使用字符串拼接
<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
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bWwEJAAF-1630934207522)(MyBatis day03.assets/image-20210606224351443.png)]
动态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连接
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_student
<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>
</set>
where
student_id = #{studentId}
</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-yZ59JXr7-1630934207525)(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-FyO2Iy5u-1630934207528)(MyBatis day03.assets/image-20210606231336202.png)]
MyBatis中使用缓存的思路:
-
如何开启缓存
-
缓存的作用范围
-
脏数据的处理
脏数据:缓存中的数据来源于数据库,一旦对数据库进行增删改,缓存中的数据就可能跟数据库中不一致。缓存中和数据库中不一致的数据称之为脏数据。
4.1 一级缓存
-
如何开启
默认开启
-
作用范围
同1个SqlSession中
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TMei1mva-1630934207531)(MyBatis day03.assets/image-20210606233935474.png)]
-
脏数据的处理
一旦执行增删改,立刻清空缓存。
注意:一级缓存作用范围太小(一个SQL Session内,也就是一个事务内),没有实战价值。
4.2 二级缓存
-
如何开启
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)
-
作用范围
同1个namespace(同1个Mapper接口类型)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j4RAfk1X-1630934207533)(MyBatis day03.assets/image-20210607090651537.png)]
注意:需要执行sqlSession.commit()将数据保存到缓存中。
-
脏数据的处理
一旦执行增删改,默认清空同1个namespace下的二级缓存。
自定义清空缓存的策略:
insert delete update select标签都有flushCache属性 flushCache:true执行时清空缓存 false:不清空缓存 insert delete update标签的flushCache默认值:true select标签的flushCache默认值:false
4.3 二级缓存的问题
二级缓存是namespace(接口类型)级别,不同类型的Mapper查询结果在不同的二级缓存下。好处:粒度细。不足:在表连接时会出现问题。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LA69Wyvg-1630934207534)(MyBatis day03.assets/image-20210607092541053.png)]
解决方案:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8Ra5auzp-1630934207535)(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>
-
开启缓存
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根目录
-
作用范围
同1个namespace
-
脏数据的处理
一旦执行增删改, 默认清空同1个namespace下的缓存。
注意:ehcache和MyBatis集成后,也有二级缓存的问题,解决方案也是一样。
5 PageHelper分页插件
在Web系统中,分页是一种常见的功能,之前写的分页方法开发过程比较麻烦,且移植性也不高(分页语法和具体的数据库耦合了)。PageHelper利用mybatis拦截器的机制,实现分页查询,支持常见的 12 种数据库的物理分页,并提供了多种使用方式。
5.1 PageHelper引言
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7t93W3Vq-1630934207536)(MyBatis day03.assets/image-20210701220122371.png)]
存在的问题:
- 编写过程繁琐:需要显式提供2个查询语句
- 分页语句与数据库耦合:不同数据库的分页语法不同,移植性差。
解决方案:PageHelper分页插件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JlSFPrfx-1630934207537)(MyBatis day03.assets/image-20210701224343528.png)]
在mapper中,只需要做如下开发:
- 只需要提供查询数据的方法,不需要提供统计方法
- 查询数据的方法要定义2个分页参数,且Param注解必须为
pageNum
和pageNum
- 提供的sql语句不需要显示写出分页部分
5.2 PageHelper的使用
5.2.1 引入插件,并配置
-
pom.xml添加
PageHelper依赖
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.2.0</version> </dependency>
-
mybatis-config.xml配置,开启分页插件功能
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8dpumATB-1630934207537)(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 使用插件分页
-
编码方式
-
UserMapper.java和 UserMapper.xml
public interface UserMapper { public List<User> selectPageUsersWithNoArguments(); }
<select id="selectPageUsersWithNoArguments" resultType="user"> select * from t_user </select>
-
使用插件的编码
基本的使用:
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());
-
-
注解参数方式
-
UserMapper.java
public interface UserMapper { //将2个分页参数使用注解命名,必须为pageNum和pageSize public List<User> selectPageUsers(@Param("pageNum")Integer pageNum, @Param("pageSize")Integer pageSize); }
-
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
-
IDEA中安装lombok插件。IDEA2021版后已经内置该插件,可以不用手动安装
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8ttVQvwX-1630934207538)(MyBatis day03.assets/image-20210701235217440.png)]
-
pom.xml引入依赖
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.16</version> </dependency>
-
在实体类上,通过Lombok内置注解简化开发
@Data public class User implements Serializable { private Integer userId; private String username; private String password; }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mZRK0My5-1630934207539)(MyBatis day03.assets/image-20210702000155174.png)]
6.3 Lombok常用注解
注解 | 作用 |
---|---|
@Setter | 注解在类或属性上;为属性提供 setter方法 |
@Getter | 注解在类或属性上;为属性提供 getter方法 |
@NoArgsConstructor | 注解在类上;为类提供一个无参的构造方法 |
@AllArgsConstructor | 注解在类上;为类提供一个全参的构造方法 |
@EqualsAndHashCode | 注解在类上;为类提供一个equals和hashCode的方法 |
@ToString | 注解在类上;为类提供一个toString方法 |
@Data | 复合多个注解:@ToString , @EqualsAndHashCode , @Getter , @Setter |
典型案例:
@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
|
典型案例:
@Data//get、set、equals、hashCode、toString
@AllArgsConstructor//有参
@NoArgsConstructor//无参
public class User implements Serializable {
private Integer userId;
private String username;
private String password;
}