缓存与分页

一、动态SQL

修改

Mapper中的修改,主要基于set标签;

功能:1.可根据上下文添加或去掉set指令; 2.根据上下文添加或去掉SQL语句后面的’,'

<update id="updateUser" parameterType="user">
    update user
    <set>
        <if test="name!=null">
            name=#{name},
        </if>
        <if test="password!=null">
            password=#{password},
        </if>
        <if test="sex!=null">
            sex=#{sex}
        </if>
    </set>
    where id=#{id}
</update>

批量删除

使用foreach标签来完成批处理操作;批处理操作包括,批量删除和批量添加

<!--
    foreach:进行批处理   collection:批处理传参类型-容器 array-数组
    open:开头  close:结尾  separator:分隔   item:循环遍历的名字
    <foreach collection="array" open="(" close=")" separator="," item="id">
        -->
<delete id="deleteByIds">
    delete from user where id in
    <foreach collection="array" open="(" close=")" separator="," item="id">
        #{id}
    </foreach>

</delete>

批量添加

使用foreach标签,批量添加的SQL语句如下:

insert into user(name,password,sex) values(?,?,?),(?,?,?),(?,?,?)…

<!--
    foreach:循环标签  collection:传的参数是集合  item:集合元素是user对象
            separator:按,分隔
    <foreach collection="list" item="u" separator=",">
            (#{u.name},#{u.password},#{u.sex})
        </foreach> -->
<insert id="addBatch">
    insert into user(name,password,sex) values
    <foreach collection="list" item="u" separator=",">
        (#{u.name},#{u.password},#{u.sex})
    </foreach>
</insert>

二、缓存

概述

内存中的一块存储空间,服务于某个应用程序,旨在将频繁读取的数据临时保存在内存中,便于二次快速访问。

注意:缓存就是查询缓存,和DML没有关系;且DML会影响缓存,会清除缓存

场景:当频繁需要查询,且DML操作较少时,使用查询缓存,可大大提升性能,因为有了缓存,则减少和数据库交互次数。

一级缓存

SQLSession级别的缓存,也就是同一个SqlSession对象的同构查询,会产生缓存,直接使用该缓存即可;当然,应用场景很少,只需了解即可。

一级缓存不用配置,直接就有,测试即可。

//测试一级缓存---同一个session(了解)
SqlSession session = MyBatisUtils.getSession();
UserDao mapper = session.getMapper(UserDao.class);
System.out.println(mapper.selectById(1));
System.out.println("----------一级缓存---------");
//session = MyBatisUtils.getSession(); //不同session对象,则重新到数据库中查询
mapper = session.getMapper(UserDao.class); //同一个session对象,则从缓存中取资源
System.out.println(mapper.selectById(1));

二级缓存

SqlSessionFactory级别的缓存,也就是同一个SqlSessionFactory对象的同构查询,会产生缓存,直接从缓存中获取资源即可。

二级缓存需要手动配置开启。且需要关闭该查询,缓存才起作用(且实体类需要序列化)

//二级缓存测试:
SqlSession session = MyBatisUtils.getSession();
UserDao mapper = session.getMapper(UserDao.class);
System.out.println(mapper.selectById(1));
session.close();  //关闭当前session,二级缓存才起作用

System.out.println("进行了修改操作...");
session = MyBatisUtils.getSession(); //不同session对象,也可从缓存中获取资源
mapper = session.getMapper(UserDao.class);
User user = new User(5,"小赵","999","男",null);
mapper.updateUser(user);
session.commit();  //修改后,进行提交和关闭; 清空了与上次操作表相关的缓存
session.close();

System.out.println("----------二级缓存---------");
session = MyBatisUtils.getSession(); //不同session对象,也可从缓存中获取资源
mapper = session.getMapper(UserDao.class); //同一个session对象,则从缓存中取资源
System.out.println(mapper.selectById(1));
session.close();  //关闭资源才会产生缓存


System.out.println("----------继续二级缓存---------");
session = MyBatisUtils.getSession(); //不同session对象,也可从缓存中获取资源
mapper = session.getMapper(UserDao.class); //同一个session对象,则从缓存中取资源
System.out.println(mapper.selectById(1));

查询缓存中的提示:

----------继续二级缓存---------
DEBUG [main] - Cache Hit Ratio [com.qf.dao.UserDao]: 0.3333333333333333  #清了缓存,三次命中一次
User(id=1, name=zs, password=123, sex=男, birthday=Wed Sep 27 17:20:30 CST 2023)

三、Druid连接池

概述

Druid连接池,是阿里巴巴提供的性能最好的一款连接池产品。mybatis中自带了连接池产品,如果需要提升性能,则可选择Druid连接池,替换原有的连接池产品。

应用

需要将原有连接池改为Druid的连接池,只需变更mybatis配置文件中的数据源即可。

引入Druid的依赖:

<!-- 阿里巴巴提供的druid驱动 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.18</version>
</dependency>

创建Druid连接池类,继承之前的类,更改数据源即可

//Druid连接池类,变更数据源
public class MyDruidPoolFactory extends PooledDataSourceFactory {
    public MyDruidPoolFactory(){
        //使用Druid的数据源替换原有的
        this.dataSource = new DruidDataSource();
    }
}

mybatis配置中,需要将druid数据源引入

 <!-- 从数据源工厂中取出数据源对象 -->
<dataSource type="com.qf.utils.MyDruidPoolFactory">
    <!-- 注意:属性name需要查找DruidDataSource提供的属性名:set/get-->
    <property name="driverClassName" value="${driver}"/>
    <property name="url" value="${url}"/>
    <property name="username" value="${username}"/>
    <property name="password" value="${password}"/>
</dataSource>

四、分页

概述

在之前的内容中进行了分页的操作,麻烦之处在于,需要手工创建Page实体,在业务层封装回传到前端的数据,非常麻烦。

现在有了分页插件后,即可直接在业务层完成分页的封装。

测试案例

在业务层封装,然后在测试类中直接进行分页查询即可

导入分页插件的依赖包:

<!-- 分页依赖包 -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.1.10</version>
</dependency>

在mybatis配置中,需要导入插件的配置:

<plugins>
    <!-- com.github.pagehelper为PageHelper类所在包名 -->
    <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>

编写业务层,进行分页封装;最终返回PageInfo分页实体

public class UserServiceImpl implements UserService {
    @Override  //参数1:当前页   参数2:页大小
    public PageInfo<User> selectByPage(int pageNum, int pageSize) {
        UserDao mapper = MyBatisUtils.getMapper(UserDao.class);
        //limit 0,4  拼接到了下面的sql语句中
        PageHelper.startPage(pageNum,pageSize);
        //select * from user
        List<User> list = mapper.selectByPage();
        //PageInfo相当于之前jdbc中自定义的Page实体; 注入list内容
        PageInfo<User> pageInfo = new PageInfo<>(list);
        return pageInfo;
    }
}

在测试类中即可打印分页信息:

//查询所有
UserService userService = new UserServiceImpl();
PageInfo<User> page = userService.selectByPage(2, 4);
System.out.println(page);

web分页

将上述测试案例,在web中完成数据的展示。

技术点:Servlet+mybatis+JSP+分页插件

步骤:

  1. 转web项目,将web环境搭建好
  2. 创建Servlet控制层
  3. 创建JSP进行展示

控制层的处理:

@WebServlet("/user")
public class UserController extends HttpServlet {
    private UserService userService = new UserServiceImpl();
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //从前端获取到参数
        int pageNum = Integer.parseInt(req.getParameter("pageNum"));
        int pageSize = Integer.parseInt(req.getParameter("pageSize"));
        //交给业务层处理,并返回分页实体
        PageInfo<User> pageInfo = userService.selectByPage(pageNum, pageSize);

        req.setAttribute("p",pageInfo);  //存值+转发
        req.getRequestDispatcher("page.jsp").forward(req,resp);
    }
}

jsp分页展示:

<table border="2">
    <tr>
        <td>ID号</td>
        <td>姓名</td>
        <td>密码</td>
        <td>性别</td>
        <td>生日</td>
        <td>操作</td>
    </tr>
    <c:forEach items="${p.list}" var="u">
        <tr>
            <td>${u.id}</td>
            <td>${u.name}</td>
            <td>${u.password}</td>
            <td>${u.sex}</td>
            <td>${u.birthday}</td>
            <td><a href="#">删除</a>|<a href="#">修改</a></td>
        </tr>
    </c:forEach>
</table>
<a href="user?pageNum=1&pageSize=4">首页</a>
<c:if test="${p.hasPreviousPage}">
    <a href="user?pageNum=${p.pageNum-1}&pageSize=4">上一页</a>
</c:if>
<c:if test="${p.hasNextPage}">
    <a href="user?pageNum=${p.pageNum+1}&pageSize=4">下一页</a>
</c:if>
<a href="user?pageNum=${p.pages}&pageSize=4">尾页</a>
共${p.pageNum}/${p.pages}页

注意事项:

五、mybatis注解

注解操作

mybatis中基本都是使用mapper配置方式完成SQL操作;除了该方式,也可以通过注解方式完成SQL;注解方式会简单很多,但只使用一些简单的SQL操作(关联查询与动态SQL不适合)

应用:

在mybatis配置中注册注解方式:

<mappers>
    <!-- 注册注解方式(接口全限定名) -->
    <mapper class="com.qf.dao.UserDao"></mapper>
</mappers>

在指定的UserDao接口中编写注解的SQL语句:

public interface UserDao {
    @Select("select * from user where id=#{id}")
    public User selectById(Integer id); //根据id获取对象

    @Select("select * from user where id=#{id} and password=#{pwd}")
    public List<User> selectByIdAndPwd(@Param("id")Integer id, @Param("pwd") String pwd);
}

后续的DML操作都和Mapper文件的方式类似,注解中更简便,只需关注SQL语句

#{}与$

在mybatis的SQL语句的传参中,都是使用#{},如果改为${},可以吗,先进行测试。

通过观察可知:

  1. 传一个参数时,会将参数当成实体对象;必须加@Param(“id”),去确定匹配参数
  2. ${}是使用普通的执行对象进行操作的,会有SQL注入的隐患
  3. ${}是字符串内容的直接替换(不带‘ ’, #{}则根据类型规则带‘ ’)
  4. ${}会优先取数据库连接中的数据,所以尽量避免和数据库中重名,例如,${username},${password}
@Select("select * from user where id=${id}")
public User selectById(@Param("id")Integer id); //根据id获取对象

//注意:使用${},尽量避免 数据库驱动中的名字,例如${username},${password},因为优先会取数据库中的名字
@Select("select * from user where id=${id} and password='${pwd}'")
public List<User> selectByIdAndPwd(@Param("id")Integer id, @Param("pwd") String pwd);

应用场景:只适合用在SQL中的字符串直接替换中,例如排序规则查询

//${}的场景:排序规则
@Select("select * from user order by id ${rule}")
public List<User> selectOrder(@Param("rule") String rule);

六、总结与作业

总结

1.动态SQL(重点)
修改、批量删除、批量添加
2.缓存(重点)
概述、一级缓存(了解),二级缓存(SqlSessionFactory级别)
3.Druid连接池(重点)
性能最好的连接池,应用
4.分页
按之前方式如何操作;转mybatis方式(分页插件)
测试案例,web分页
5.mybatis注解
注解操作-mybatis配置注解注册;在DAO接口中编写注解SQL
#{}与${}区别(重点-面试)

作业

完成分页中的删除和修改