JSP&三层架构

JSP&三层架构

目标

  • 能够说出el表达式的作用
  • 能够使用el表达式获取javabean的属性
  • 能够使用jstl标签库的if标签
  • 能够使用jstl标签库的foreach标签
  • 能够使用三层架构模式完成显示用户案例
  • 能够使用ThreadLocal
  • 能够完成转账案例

第一章-EL表达式

知识点-EL表达式概述

1.目标

  • 能够说出el表达式的作用

2.讲解

2.1.什么是El表达式

​ Expression Language: 表达式语言, jsp2.0之后内置在jsp里面

​ 目的:为了使JSP写起来更加简单, 取值(取的域对象里面存的值)更加简单。(代替脚本 <% %>)

2.2. EL语法

​ ${el表达式}

2.3. EL表达式的用途

​ 1.获取数据. 获取的是域(request,session,ServletContext)对象中存储的数据

​ 2.EL执行运算

  1. 使用EL表达式获取cookie中的数据

3.小结

  1. EL表达式:表达式语言
  2. 语法:${el表达式}
  3. 作用:1. 获取域对象里面的数据 2. 执行运算

知识点-El获取数据

1.目标

  • 能够使用el表达式域里面的数据(先要把数据存到域对象里面)

2.路径

  • 获取简单数据类型数据(基本类型,字符串)
  • 获取数组
  • 获取list
  • 获取Map
  • 获取bean

3.讲解

3.1获取简单数据类型数据

​ 语法:${requestScope|sessionScope|applicationScope.属性名};

​ 快捷写法:${属性名}, 属性名就是存在域对象里面的key

<%--
  Created by IntelliJ IDEA.
  User: Fanyi Xiao
  Date: 2020/7/15
  Time: 8:52
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    使用EL表达式获取域对象中的简单类型的数据


    <%
        //往域对象存值
        application.setAttribute("msg","applicationValue");
        //session.setAttribute("msg","sessionValue");
        //request.setAttribute("msg","requestValue");

        //在我们开发过程中,是否会往多个域对象中存放同一个key??? 这是不会的
        //所以我们用el表达式获取域对象里面的数据,还可以简化成${key} 获取范围最小的域对象中的这个key对应的值
    %>
    获取的application域对象中的msg=${applicationScope.msg}
获取session域对象中的msg=${sessionScope.msg}
获取request域对象中的msg=${requestScope.msg}
获取存放在域对象中的msg=${msg}

3.2获取数组

​ 语法: ${key[下标]} key就是域对象里面存的key

3.3获取list

语法:${list属性名[index]}或者${list属性名.get(index)};list属性名就是存入域对象里面的key

3.4获取Map

语法:${map属性名.键}或者${map属性名.get("键")},map属性名就是存入域对象里面的key

3.5 获取bean

语法:${key.javabean属性}

​ 依赖getxxx()方法; eg: getPassword()—去掉get–>Password()----首字母小写—>password

<%@ page import="java.util.ArrayList" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.util.List" %>
<%@ page import="java.util.Map" %>
<%@ page import="com.itheima.pojo.User" %><%--
  Created by IntelliJ IDEA.
  User: Fanyi Xiao
  Date: 2020/7/15
  Time: 9:05
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    使用EL表达式获取存储在域对象中的复杂类型的数据


    <%
        //往域对象中存放数组类型的数据
        String[] arr = {"张三","李四","王五","赵六","田七","狗娃"};
        request.setAttribute("arr", arr);

        //往域对象中存放一个集合
        List list = new ArrayList();
        list.add("张三");
        list.add("李四");
        list.add("王五");
        list.add("赵六");
        list.add("田七");
        request.setAttribute("list", list);

        //往域对象中存放一个map
        Map map = new HashMap();
        map.put("name", "张三");
        map.put("password", "123456");
        map.put("nickname", "张三丰");
        request.setAttribute("map", map);

        //往域对象中存放一个pojo对象
        User user = new User(1, "jay", "台湾省");
        request.setAttribute("user",user);
    %>
    获取存放在request域对象中的数组中的第三个元素:${arr[2]}
<%-- 在el表达式中,只要是根据下标获取元素,都可以写[index] --%> 获取存放在request域对象中的集合中的第四个元素:${list[3]}
<%-- 在el表达式中,只要是根据对应的属性的get方法去获取数据,都可以写成".属性名" 或者 ["属性名"] --%> 获取存储在request域对象中的map中的nickname:${map.nickname}
获取存放在request域对象中的user的address属性的值:${user.address}

4.小结

4.1语法

  1. 获得简单类型的
${key}
  1. 获得数组类型的
${key[下标]}
  1. 获得List类型的
${key.get(index)} 或者${key[index]}
  1. 获得Map类型的
${key.get(键)} 或者${key.键} key是存到域对象里面的key
  1. 获得JavaBean类型的
${key.javaBean属性}

知识点-EL执行运算

1.目标

  • 掌握EL执行运算

2.讲解

JSP&三层架构_第1张图片

2.1算数运算

​ +,-,*,/

  • +不能拼接字符串,如果+两端是字符串,那么会将字符串转换成数字之后再进行加法运算,如果+两端的字符串无法转换成数字,则会报错

2.2 逻辑运算

​ < >= <= != ==

2.3 关系运算

​ && || !

2.4 非空判断【重点】

​ empty,1. 判断一个对象是否为null, 2. 判断集合长度是否为0, 3. 判断一个字符串是否为空字符串

​ not empty

​ 语法: ${empyt 属性名};属性名 就是域对象里面的key值

<%@ page import="java.util.ArrayList" %>
<%@ page import="java.util.List" %>
<%@ page import="com.itheima.pojo.User" %><%--
  Created by IntelliJ IDEA.
  User: Fanyi Xiao
  Date: 2020/7/15
  Time: 9:35
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    empty 运算符的介绍


    <%
        //el表达式中的empty可以判断一个字符串是否为空字符串,一个对象是否为null,一个集合的长度是否为0
        List list = new ArrayList();
        list.add("张三丰");
        request.setAttribute("list", list);

        request.setAttribute("msg","requestValue");

        User user = new User();
        request.setAttribute("u",user);
    %>
    判断域对象中的list集合的长度是否为0: ${empty list}
判断域对象中的msg字符串是否为空字符串: ${empty msg}
判断域对象中的user是否为null : ${empty u}

3.小结

  1. 注意的地方: +只能做加法运算,不能拼接字符串

  2. empty【重点】

    • 语法
      • ${empty key}
      • ${not empty key}
  • 作用
    • 判断一个对象是否为null
    • 判断一个集合长度是否为0
    • 判断一个字符串是否是""
    • 注意
      • 如果是集合, 集合为null 是empty
      • 如果是集合, 集合不为null 但是长度为0 还是empty

知识点-使用EL表达式获取存放在cookie中的数据(扩展)

​ 语法:${cookie.cookie的name.value}

<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    使用el表达式获取存储在cookie中的数据


    <%--
        jsp里面是内置session对象,有了session对象,那么浏览器就会携带一个名为"JSESSIONID"的cookie
        我们的目标就是获取"JSESSIONID"的值
    --%>
    <%
        Cookie[] cookies = request.getCookies();
        String cookieValue = null;
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals("JSESSIONID")) {
                    cookieValue = cookie.getValue();
                }
            }
        }
    %>
    使用原始方式获取的JSESSIONID的值为: <%=cookieValue%>
<%-- ${cookie}表示获取这次请求中的所有cookie对象 ${cookie.JSESSIONID}表示获取名为"JSESSIONID"的cookie对象 ${cookie.JSESSIONID.value}表示获取名为"JSESSIONID"的cookie对象的value --%> 使用EL表达式获取JSESSIONID的值为: ${cookie.JSESSIONID.value}

第二章-JSTL标签库

知识点-JSTL标签库概述

1.目标

  • 掌握什么是JSTL标签库

2.讲解

2.1 什么是JSTL标签库

​ JSTL(JSP Standard Tag Library,JSP标准标签库)是一个不断完善的开放源代码的JSP标签库,是由apache的jakarta小组来维护的。这个JSTL标签库没有集成到JSP的, 要使用的话, 需要导jar包.

2.2 JSTL标签库的作用

​ 为了简化在jsp页面上操作数据; eg: 遍历数据 判断数据等

2.3 JSTL标签库的类别

JSP&三层架构_第2张图片

3.小结

  1. JSTL: JSP标准的标签库. 也就意味着我们可以在jsp里面使用除了html以外的其它的标签
  2. 作用: 遍历, 判断… 代替脚本

知识点-JSTL核心标签库

1.目标

  • 掌握if,foreach标签的使用

2.讲解

2.1核心标签库使用步骤

  1. 导入jar包

在这里插入图片描述

  1. 在JSP页面上导入核心标签库<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

2.3if标签

JSP&三层架构_第3张图片

  • 语法


  • 实例
<%--
  Created by IntelliJ IDEA.
  User: Fanyi Xiao
  Date: 2020/8/27
  Time: 10:10
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>


    使用jstl中的if标签进行判断


    <%
        //往域对象中存储一个age
        request.setAttribute("age",17);

        //目标:判断age的值,如果大于等于18,则在浏览器页面上输出"已成年",否则输出"未成年"
        //jstl的使用步骤:1. 导入jar包  2. 在jsp页面通过taglib指令引入核心标签库  3. 使用标签
    %>
    <%--
        if标签有一个属性叫做test,它表示判断表达式,需要结合el一起使用
        如果要表示相反的判断,则再添加一个if标签,然后写相反的条件就行

        if标签还有一个属性叫做var,表示将判断结果存储进域对象时候的key(了解)

        if标签的第三个属性叫做scope,表示将判断结果存储进哪个域对象(了解)
    --%>
    
        已成年
    

    
        未成年
    
    
${flag}
  • 小结

    • 语法
    
    
    • 特点
      • 如果test里面的是true, if标签体里面的就会执行
      • 如果test里面的是false, if标签体里面的就不会执行
      • 没有else的

2.4choose标签

  • 实例
<%--
  Created by IntelliJ IDEA.
  User: Fanyi Xiao
  Date: 2020/7/15
  Time: 9:59
  To change this template use File | Settings | File Templates.
--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    jstl中的choose标签的使用介绍


    <%
        request.setAttribute("course","PHP");
    %>
    
        <%--
            一个when标签表示一个条件
        --%>
        
            学习Java
        

        
            学习Android
        

        
            学习C++
        

        
            学习,学个屁!!!
        
    


2.5 foreach标签

JSP&三层架构_第4张图片

  • 简单的使用:

 <%--
        jstl中的forEach标签是用来代替for循环语句

        目标1: 在浏览器上显示0-9的数字
            begin属性: 从哪个下标开始遍历, 如果不写默认是从0开始
            end属性: 到哪个下标结束遍历,如果不写默认是遍历到集合/数组的最后一个元素
            step属性: 表示遍历时候的步长,默认步长是1
            var属性: 表示将遍历的结果存放进域对象时候的key
    --%>

	${i}

  • 复杂的使用遍历集合:

    <%
        //往域对象存储一个集合
        List list = new ArrayList();
        list.add("张三");
        list.add("李四");
        list.add("王五");
        list.add("赵六");
        list.add("田七");
        request.setAttribute("list", list);
    %>
    
    
        ${i}
    
    <%-- 通过items属性指定遍历域对象里面的list 通过var属性指定遍历出来的每个数据存储到域对象时候的key --%> ${username}
  • c:forEach中的varStatus属性。

    指向一个字符串,该字符串引用一个对象。  map.put("vs",一个对象);
    
     	  这个对象记录着当前遍历的元素的一些信息:
    
       		   index:返回索引。从0开始
    
        		   count:返回计数。从1开始
    
       		    last:是否是最后一个元素
    
        		   first:是否是第一个元素	
    
<%@ page import="java.util.ArrayList" %>
<%@ page import="java.util.List" %><%--
  Created by IntelliJ IDEA.
  User: Fanyi Xiao
  Date: 2020/7/15
  Time: 10:15
  To change this template use File | Settings | File Templates.
--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    forEach 标签的varStatus属性的介绍


    <%
        //往域对象存储一个集合
        List list = new ArrayList();
        list.add("张三");
        list.add("李四");
        list.add("王五");
        list.add("赵六");
        list.add("田七");
        request.setAttribute("list", list);
    %>

    <%--
        forEach标签的varStatus属性:指定将遍历出来的每一个元素的状态存储进域对象时候的key
            遍历出来的每一个元素都有一些状态(属性),比如:
                下标 index:
                计数 count:
                当前元素的值 current:
                是否是第一个元素:
                是否是最后一个元素
    --%>
    
            
下标 计数 姓名 是否是第一个元素 是否是最后一个元素
${vst.index} ${vst.count} ${vst.current} ${vst.first} ${vst.last}

3.小结

  1. foreach标签
  • 简单使用

	//每遍历一次 foreach里面就执行一次

  • 复杂使用

	//每遍历一次 foreach里面就执行一次

回顾el和JSTL的内容

el

  1. el的语法: ${el表达式}
  2. el的作用:
    1. 获取域对象中的数据,${key}
    2. 执行运算,el表达式中可以使用运算符:±*/><与或非等等运算符都能使用,它还有一个特殊的运算符叫做empty
    3. 获取cookie中的数据: ${cookie.cookie的名字.value}

jstl

  1. jstl的使用步骤:
    1. 导入jar包
    2. 在要使用jstl的jsp页面通过tablib指令引入核心标签库
    3. 使用jstl的核心标签库中的标签
  2. jstl的常用标签:
    1. if
    2. choose
    3. forEach
  3. if标签
    1. 作用: 代替jsp页面中的if语句
    2. 属性:
      1. test(必须的属性): 结合el表达式编写判断条件
      2. var: 将判断结果存储进域对象时候的key
      3. scope:将判断条件存储进哪个域对象
  4. choose标签:
    1. 作用:代替多条件判断
    2. 它需要和when标签以及otherwise标签一起使用
  5. forEach标签
    1. 作用: 代替jsp页面的for循环语句
    2. 属性:
      1. begin 开始遍历的下标,如果没写,则从0开始遍历
      2. end 结束遍历的下标,如果没写,则遍历到数组或者集合的最后一个元素
      3. step 遍历的步长,如果没写就是1
      4. var 遍历出来的每一个数据存储进域对象时候的key
      5. varStatus 遍历出来的每一个数据的状态
        1. index 下标
        2. count 序号
        3. first 是否是第一个元素
        4. last 是否是最后一个元素
        5. current 遍历出来的当前元素

第三章-综合案例和开发模式

案例-完成转账的案例v1

一.需求

  • 当单击提交按钮,付款方向收款方安照输入的金额转账。

JSP&三层架构_第5张图片

二,分析

JSP&三层架构_第6张图片

三,实现

1.案例的准备工作

  • 数据库的准备

    create database day29;
    use day29;
    create table account(
        id int primary key auto_increment,
        name varchar(20),
        money double
    );
    
    insert into account values (null,'jay',1000);
    insert into account values (null,'aobama',1000);
    insert into account values (null,'ww',1000);
    
  • 页面

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    
    
      
      Insert title here
    
    
    
    
    付款方
    收款方
    金额
  • jar包

  • 工具类

  • 配置文件

2.代码实现

  • AccountServlet
package com.itheima.web.servlet;

import com.itheima.utils.DruidUtil;
import org.apache.commons.dbutils.QueryRunner;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 包名:${PACKAGE_NAME}
 *
 * @author Leevi
 * 日期2020-07-15  10:55
 */
@WebServlet("/account")
public class AccountServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        //1. 获取请求参数
        String fromName = request.getParameter("from");
        String toName = request.getParameter("to");
        Double money = Double.valueOf(request.getParameter("money"));

        //2. 执行转账的SQL语句
        //2.1 转出账户扣款
        QueryRunner queryRunner = new QueryRunner(DruidUtil.getDataSource());
        String sql1 = "update account set money=money-? where name=?";

        try {
            queryRunner.update(sql1,money,fromName);

            //2.2 转入账户收款
            String sql2 = "update account set money=money+? where name=?";
            queryRunner.update(sql2,money,toName);

            //3. 转账成功
            response.getWriter().write("转账成功!!!");
        } catch (Exception e) {
            e.printStackTrace();
            //转账失败
            response.getWriter().write("转账失败!!!");
        }
    }
}

四.小结

  1. 转账: 一个用户的钱减少, 一个用户的钱增加

知识点-开发模式

1.目标

  • 理解模式二(MVC)和模式三(三层架构)

2.讲解

2.1 JSP的开发模式一【了解】

javaBean:实体类。特点:私有化的属性、公共的getter setter方法、无参的构造。

JSP&三层架构_第7张图片

2.2 JSP的开发模式二

​ JSP + Servlet + JavaBean 称为MVC的开发模式.

MVC:开发模式

​ M:model 模型 (javaBean:封装数据)

​ V:View 视图 (JSP:展示数据)

​ C:controller 控制器 (Servlet:处理逻辑代码,做为控制器)

JSP&三层架构_第8张图片

2.3模式三: 三层架构

  • 软件中分层:按照不同功能分为不同层,通常分为三层:表现层(web层),业务层,持久(数据库)层。

JSP&三层架构_第9张图片

  • 不同层次包名的命名
分层 包名(公司域名倒写)
表现层(web层) com.itheima.web
业务层(service层) com.itheima.service
持久层(数据库访问层) com.itheima.dao
JavaBean com.itheima.bean
工具类 com.itheima.utils
  • 分层的意义:
    1. 解耦:降低层与层之间的耦合性。 (以后面向接口编程)
    2. 可维护性:提高软件的可维护性,对现有的功能进行修改和更新时不会影响原有的功能。
    3. 可扩展性:提升软件的可扩展性,添加新的功能的时候不会影响到现有的功能。
    4. 可重用性:不同层之间进行功能调用时,相同的功能可以重复使用。

JSP&三层架构_第10张图片

3.小结

  1. 模式一: JSP+JavaBean【了解】

  2. 模式二: MVC

    • M model JavaBean
    • V View JSP
    • C Controller Servlet
  3. 三层架构

    • WEB层
      • 获得请求参数
      • 调用业务
      • 响应
    • 业务层
      • 处理业务
      • 调用Dao
    • 持久层
      • 操作数据库
    • 三层架构中的包名:
      • 表现层: web
      • 业务层: service
      • 数据访问层/持久层: dao

案例-完成转账的案例v2

一.需求

  • 使用三层架构改写转账案例

JSP&三层架构_第11张图片

二,分析

JSP&三层架构_第12张图片

三,实现

  • UserServlet
package com.itheima.web.servlet;

import com.itheima.service.AccountService;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 包名:${PACKAGE_NAME}
 *
 * @author Leevi
 * 日期2020-07-15  10:55
 */
@WebServlet("/account")
public class AccountServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        //1. 获取请求参数
        String fromName = request.getParameter("from");
        String toName = request.getParameter("to");
        Double money = Double.valueOf(request.getParameter("money"));

        try {
            //2. 调用业务层的AccountService的方法处理请求执行转账
            AccountService accountService = new AccountService();
            accountService.transfer(fromName,toName,money);

            //3. 转账成功
            response.getWriter().write("转账成功!!!");
        } catch (Exception e) {
            e.printStackTrace();
            //转账失败
            response.getWriter().write("转账失败!!!");
        }
    }
}
  • AccountService
package com.itheima.service;

import com.itheima.dao.AccountDao;

import java.sql.SQLException;

/**
 * 包名:com.itheima.service
 *
 * @author Leevi
 * 日期2020-07-15  11:19
 */
public class AccountService {
    private AccountDao accountDao = new AccountDao();
    public void transfer(String fromName,String toName,Double money) throws SQLException {
        //2.1 调用AccountDao的方法进行转出账户扣款
        accountDao.updateAccount(fromName,-money);
        //2.2 调用AccountDao的方法进行转入账户收款
        accountDao.updateAccount(toName,money);
    }
}
  • UserDao
package com.itheima.dao;

import com.itheima.utils.DruidUtil;
import org.apache.commons.dbutils.QueryRunner;

import java.sql.SQLException;

/**
 * 包名:com.itheima.dao
 *
 * @author Leevi
 * 日期2020-07-15  11:19
 */
public class AccountDao {
    private QueryRunner queryRunner = new QueryRunner(DruidUtil.getDataSource());
    /**
     * 修改账户信息
     */
    public void updateAccount(String name,Double money) throws SQLException {
        String sql = "update account set money=money+? where name=?";
        queryRunner.update(sql,money,name);
    }
}

四.小结

  • WEB层 com.itheima.web
    • 获得请求参数
    • 调用业务
    • 响应
  • 业务层 com.itheima.service xxService
    • 处理业务
    • 调用Dao
  • 持久层 com.itheima.dao xxDao
    • 操作数据库

案例-完成转账的案例v3

一.需求

  • 当单击提交按钮,付款方向收款方安照输入的金额转账。 使用手动事务进行控制

JSP&三层架构_第13张图片

二,分析

1.DBUtils实现事务管理

API 说明
QueryRunner() 创建QueryRunner对象. 手动提交事务时使用
query(connection,String sql, Object[] params, ResultSetHandler rsh) 查询(需要传入Connection)
update(connection,String sql, Object… params) 更新

2.思路

JSP&三层架构_第14张图片

三,实现

  • UserService
package com.itheima.service;

import com.itheima.dao.AccountDao;
import com.itheima.utils.DruidUtil;

import java.sql.Connection;
import java.sql.SQLException;

/**
 * 包名:com.itheima.service
 *
 * @author Leevi
 * 日期2020-07-15  11:19
 */
public class AccountService {
    private AccountDao accountDao = new AccountDao();
    public void transfer(String fromName,String toName,Double money) {
        Connection conn = null;
        try {
            //逻辑操作开始之前开启事务: connection.setAutoCommit(false)
            conn = DruidUtil.getDataSource().getConnection();
            conn.setAutoCommit(false);

            //2.1 调用AccountDao的方法进行转出账户扣款
            accountDao.updateAccount(conn,fromName, -money);

            //模拟转账过程中出现异常
            int num = 10 / 0;

            //2.2 调用AccountDao的方法进行转入账户收款
            accountDao.updateAccount(conn,toName, money);

            //逻辑操作执行完毕没有异常,提交事务: connection.commit()
            conn.commit();
        } catch (Exception e) {
            e.printStackTrace();
            //逻辑操作执行过程中遇到异常,则在catch里面回滚事务: connection.rollback()
            try {
                conn.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
            throw new RuntimeException("转账失败");
        }
    }
}
  • UserDao
package com.itheima.dao;

import com.itheima.utils.DruidUtil;
import org.apache.commons.dbutils.QueryRunner;

import java.sql.Connection;
import java.sql.SQLException;

/**
 * 包名:com.itheima.dao
 *
 * @author Leevi
 * 日期2020-07-15  11:19
 */
public class AccountDao {
    private QueryRunner queryRunner = new QueryRunner(DruidUtil.getDataSource());
    /**
     * 修改账户信息
     * 指定使用某个连接Connection执行SQL语句
     */
    public void updateAccount(Connection connection,String name, Double money) throws SQLException {
        String sql = "update account set money=money+? where name=?";
        queryRunner.update(connection,sql,money,name);
    }
}

四.小结

  1. 思想1: service层将异常try起来了,怎么才能让servlet还能够获取异常呢?
在catch里面抛运行时异常
  1. 思想2: 如果在service和dao共享一个Connection对象

    通过调用方法的时候将connection作为参数传递给Dao
    
  2. 技术点1 : 怎么开启、提交、回滚事务

    connection.setAutoCommit(false)开启事务
    connection.commit()提交事务
    connection.rollback()回滚事务
    注意: 开启事务的连接和执行SQL语句的连接要是同一个
    
  3. 技术点2 : 在使用DBUtils执行SQL语句的时候,怎么才能指定使用哪个连接呢?

    调用queryRunner对象的update或者query方法的时候,可以传入connection
    

案例-完成转账的案例v4

一.需求

  • 当单击提交按钮,付款方向收款方安照输入的金额转账。 使用事务进行控制

JSP&三层架构_第15张图片

二,分析

1.ThreadLocal

​ 在“事务传递参数版”中,我们必须修改方法的参数个数,传递链接,才可以完成整个事务操作。如果不传递参数,是否可以完成?在JDK中给我们提供了一个工具类:ThreadLocal,此类可以在一个线程中共享数据。

​ java.lang.ThreadLocal,该类提供了线程局部 (thread-local) 变量,用于在当前线程中共享数据。ThreadLocal工具类底层就是一个Map,key存放的当前线程,value存放需要共享的数据

package com.itheima;

/**
 * 包名:com.itheima
 *
 * @author Leevi
 * 日期2020-07-15  14:58
 * ThreadLocal是线程本地变量,它的作用是用于在保证同一个线程中的对象使用的是同一份数据
 */
public class TestMain {
    public static void main(String[] args) {
        //ThreadLocal的使用
        ThreadLocal<String> threadLocal = new ThreadLocal<>();
        //在主线程中往ThreadLocal对象中存储一个字符串"jay"
        threadLocal.set("jay");
        //在主线称重往ThreadLocal中存入一个字符串"aobama"
        threadLocal.set("aobama");

        //新线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                //在新线程中,往ThreadLocal中存入"jay"
                threadLocal.set("jay");

                //在新线程中获取ThreadLocal中的值
                String s = threadLocal.get();
                System.out.println("在新线程中获取ThreadLocal中的值为:" + s);
            }
        }).start();


        //在主线程中取出ThreadLocal中的值
        String str = threadLocal.get();
        System.out.println("在主线程中获取ThreadLocal对象中的值:" + str);
    }
}

2.思路

JSP&三层架构_第16张图片

三,代码

  • DruidUtil
package com.itheima.utils;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

/**
 * 包名:com.itheima.utils
 *
 * @author Leevi
 * 日期2020-07-06  11:45
 */
public class DruidUtil {
    private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
    private static DataSource dataSource;
    static {
        try {
            //1. 创建Properties对象
            Properties properties = new Properties();
            //2. 将配置文件转换成字节输入流
            InputStream is = DruidUtil.class.getClassLoader().getResourceAsStream("druid.properties");
            //3. 使用properties对象加载is
            properties.load(is);
            //druid底层是使用的工厂设计模式,去加载配置文件,创建DruidDataSource对象
            dataSource = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static DataSource getDataSource(){
        return dataSource;
    }

    /**
     * 让该方法获取的连接对象是同一个对象
     * @return
     * @throws SQLException
     */
    public static Connection getConnection() throws SQLException {
        Connection connection = threadLocal.get();
        if (connection == null) {
            //说明threadLocal中还没有存储连接
            connection = dataSource.getConnection();
            //将连接存储到threadLocal中
            threadLocal.set(connection);
        }
        return connection;
    }

    public static void clear() throws SQLException {
        //先将conn的autoCommit属性设置回true
        threadLocal.get().setAutoCommit(true);
        threadLocal.get().close();
        //将conn从ThreadLocal中移除
        threadLocal.remove();
    }
}
  • AccountService
package com.itheima.service;

import com.itheima.dao.AccountDao;
import com.itheima.utils.DruidUtil;

import java.sql.Connection;
import java.sql.SQLException;

/**
 * 包名:com.itheima.service
 * @author Leevi
 * 日期2020-08-27  11:50
 * 业务层的类:处理请求(执行具体的业务逻辑)
 * 如果在处理请求的过程中,需要执行SQL语句,则调用dao层的方法执行SQL语句
 *
 * service层为什么要try异常: 为了在catch里面进行回滚操作
 * service层又要把异常抛给servlet
 *
 * 怎么添加事务:
 * 1. 开启事务: 业务逻辑开始之前,connection.setAutoCommit(false);
 * 2. 提交事务: 业务逻辑执行完毕,没有出现异常,connection.commit()
 * 3. 回滚事务: 业务逻辑执行过程中出现异常,connection.rollback()
 *
 * 注意点: 执行事务的connection对象和执行SQL语句的connection对象必须是同一个connection
 *
 * 进一步优化
 * 1.使用ThreadLocal存储连接,实现业务层和持久层使用的是相同连接对象
 * 2.
 */
public class AccountService {
    private AccountDao accountDao = new AccountDao();
    public void transfer(String fromName,String toName, Double money){
        Connection conn = null;
        try {
            //开启事务
            conn = DruidUtil.getConnection();
            conn.setAutoCommit(false);

            //1. 调用dao层的方法,执行付款方扣款
            accountDao.updateAccount(fromName, -money);

            //出现异常
            int num = 10 / 0;

            //2. 调用dao层的方法,执行收款方收款
            accountDao.updateAccount(toName, money);

            //提交事务
            conn.commit();
        } catch (Exception e) {
            e.printStackTrace();
            try {
                //回滚事务
                conn.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
            throw new RuntimeException(e.getMessage());
        }finally {
            try {
                //1. 设置连接的autoCommit为true   2. 将连接对象归还到连接池   3. 清除ThreadLocal中的连接对象
                DruidUtil.clear();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
  • AccountDao
package com.itheima.dao;

import com.itheima.utils.DruidUtil;
import org.apache.commons.dbutils.QueryRunner;

import java.sql.SQLException;

/**
 * 包名:com.itheima.dao
 * @author Leevi
 * 日期2020-08-27  11:51
 * 数据访问层的类:执行数据库的增删改查的SQL语句
 */
public class AccountDao {
    private QueryRunner queryRunner = new QueryRunner(DruidUtil.getDataSource());

    /**
     * 根据账户的name,更新账户的money
     * @param name
     * @param money
     * @throws SQLException
     */
    public void updateAccount(String name, Double money) throws SQLException {
        String sql = "update account set money=money+? where name=?";
        queryRunner.update(DruidUtil.getConnection(),sql,money,name);
    }
}

四.小结

  1. TheadLocal: jdk提供的一个对象. 只要是在同一个线程里面, 是可以共用的.
  2. 抽取了DruidUtil的getConnectionFromThreadLocal()方法, service和Dao里面的Connection都是从getConnectionFromThreadLocal()方法获取

补充案例: 显示所有用户

目标

在list.jsp页面中显示user表中的所有用户的信息

分析

JSP&三层架构_第17张图片

实现

  1. 拷贝jar、配置文件、工具类
  2. 创建index.jsp和list.jsp
  3. 创建包结构、pojo类
  4. 创建ShowAllServlet、UserService、UserDao

index.jsp代码

<%--
  Created by IntelliJ IDEA.
  User: Fanyi Xiao
  Date: 2020/7/15
  Time: 12:08
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    首页


    查看所有用户信息


ShowAllServlet代码

package com.itheima.web.servlet;

import com.itheima.pojo.User;
import com.itheima.service.UserService;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

/**
 * 包名:${PACKAGE_NAME}
 *
 * @author Leevi
 * 日期2020-07-15  12:08
 */
@WebServlet("/showAll")
public class ShowAllServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        try {
            //1. 调用业务层的方法,处理查询所有用户的请求
            UserService userService = new UserService();
            List<User> userList = userService.findAll();

            //2. 将user的集合存储到request域对象中
            request.setAttribute("list",userList);

            //3. 跳转到list.jsp页面
            request.getRequestDispatcher("/list.jsp").forward(request, response);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

UserService代码

package com.itheima.service;

import com.itheima.dao.UserDao;
import com.itheima.pojo.User;

import java.sql.SQLException;
import java.util.List;

/**
 * 包名:com.itheima.service
 *
 * @author Leevi
 * 日期2020-07-15  12:09
 */
public class UserService {
    private UserDao userDao = new UserDao();
    public List<User> findAll() throws SQLException {
        //调用dao对象的findAll()方法查询所有用户信息
        List<User> userList = userDao.findAll();
        return userList;
    }
}

UserDao代码

package com.itheima.dao;

import com.itheima.pojo.User;
import com.itheima.utils.DruidUtil;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;

import java.sql.SQLException;
import java.util.List;

/**
 * 包名:com.itheima.dao
 *
 * @author Leevi
 * 日期2020-07-15  12:09
 */
public class UserDao {
    private QueryRunner queryRunner = new QueryRunner(DruidUtil.getDataSource());
    public List<User> findAll() throws SQLException {
        String sql = "select * from user";
        List<User> userList = queryRunner.query(sql, new BeanListHandler<>(User.class));
        return userList;
    }
}

list.jsp代码

<%--
  Created by IntelliJ IDEA.
  User: Fanyi Xiao
  Date: 2020/7/15
  Time: 12:08
  To change this template use File | Settings | File Templates.
--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    展示页面


    
        <%--
            遍历出域对象里面的list中的每一个user
        --%>
        
            
序号 用户名 密码 地址 昵称 性别 邮箱
${vst.count} ${user.username} ${user.password} ${user.address} ${user.nickname} ${user.gender} ${user.email}

注册登录案例改成三层架构

RegisterServlet代码

package com.itheima.web.servlet;

import com.itheima.bean.User;
import com.itheima.service.UserService;
import org.apache.commons.beanutils.BeanUtils;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;

/**
 * @author Leevi
 * 日期2020-08-24  16:13
 * 处理注册的Servlet
 */
@WebServlet("/register")
public class RegisterServlet extends HttpServlet {
    private UserService userService = new UserService();
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //0. 解决请求参数的中文乱码问题
        request.setCharacterEncoding("UTF-8");
        //0. 解决响应的中文乱码问题
        response.setContentType("text/html;charset=utf-8");
        //1. 获取所有请求参数
        Map<String, String[]> map = request.getParameterMap();
        //2. 将所有请求参数封装到user对象中
        User user = new User();
        try {
            BeanUtils.populate(user,map);
            //设置激活状态为0
            user.setStatus("0");

            //3. 调用业务层的方法,处理注册请求
            userService.register(user);

            //如果在这个过程中没有出现异常,则注册成功
            //跳转到登录页面---->重定向跳转
            response.sendRedirect("login.html");
        } catch (Exception e) {
            e.printStackTrace();
            //如果在这个过程中出现了异常,则注册失败
            response.getWriter().write("注册失败");
        }
    }
}

LoginServlet代码

package com.itheima.web.servlet;
import com.itheima.bean.User;
import com.itheima.service.UserService;
import com.itheima.utils.CookieUtil;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
/**
 * @author Leevi
 * 日期2020-08-24  16:24
 */
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    private UserService userService = new UserService();
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //0. 解决乱码
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        //1. 获取客户端提交的用户名和密码
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        // 获取客户端输入的验证码
        String checkCode = request.getParameter("checkCode");

        // 获取服务器生成的验证码
        //从session中获取服务器端生成的验证码
        HttpSession session = request.getSession();
        String code = (String) session.getAttribute("code");

        //获取是否记住用户名
        String remember = request.getParameter("remember");
        //校验验证码
        if (code.equalsIgnoreCase(checkCode)) {
            //验证码正确,当验证码正确了,才进行用户名和密码的校验
            try {
                //调用业务层的方法,校验登录(校验用户名和密码)
                User user = userService.login(username, password);

                if (user != null) {
                    //登录成功
                    //判断是否需要记住用户名
                    if (remember != null) {
                        //需要记住用户名: 将用户名存储到cookie中,并且传到浏览器保存
                        Cookie cookie = CookieUtil.createAndSetCookie("username", username, 7 * 24 * 60 * 60, request.getContextPath());
                        response.addCookie(cookie);
                    }else {
                        //说明不需要记住用户: 需要将之前保存在cookie中的用户名删除
                        //往浏览器存储一个同名、同路径但是maxAge为0 的cookie
                        Cookie cookie = CookieUtil.createAndSetCookie("username", username, 0, request.getContextPath());
                        response.addCookie(cookie);
                    }

                    //保存登录状态: 目的是在这次会话中访问任何页面都是已登录状态
                    //其实就是保存当前登录的user,保存在session域对象中
                    session.setAttribute("user",user);

                    //跳转到success.jsp页面: 重定向
                    response.sendRedirect("success.jsp");
                }else {
                    //登录失败:用户名或密码错误
                    String errorMsg = "用户名或密码错误";
                    //将errorMsg字符串存储到哪个域对象??? request
                    request.setAttribute("errorMsg",errorMsg);

                    //优化登录:跳转回到登录页面,并且进行错误信息的提示
                    //跳转方式有两种:1. 重定向 2. 请求转发

                    request.getRequestDispatcher("login.jsp").forward(request, response);
                }
            } catch (Exception e) {
                e.printStackTrace();
                //登录失败: 登录失败
                //登录失败:验证码错误
                String errorMsg = "登录失败";
                //将errorMsg存储到request域对象
                request.setAttribute("errorMsg",errorMsg);
                //跳转到login.jsp
                request.getRequestDispatcher("login.jsp").forward(request, response);
            }
        }else {
            //登录失败:验证码错误
            String errorMsg = "验证码错误";
            //将errorMsg存储到request域对象
            request.setAttribute("errorMsg",errorMsg);
            //跳转到login.jsp
            request.getRequestDispatcher("login.jsp").forward(request, response);
        }
    }
}

UserService代码

package com.itheima.service;

import com.itheima.bean.User;
import com.itheima.dao.UserDao;

/**
 * 包名:com.itheima.service
 * @author Leevi
 * 日期2020-08-27  12:09
 */
public class UserService {
    private UserDao userDao = new UserDao();
    public void register(User user) throws Exception {
        //调用dao层的方法,将用户信息存储到数据库
        userDao.saveUser(user);
    }

    public User login(String username,String password) throws Exception {
        //调用dao层的方法校验用户名和密码
        return userDao.findUser(username,password);
    }
}

UserDao代码

package com.itheima.dao;

import com.itheima.bean.User;
import com.itheima.utils.DruidUtil;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;

import java.sql.SQLException;

/**
 * 包名:com.itheima.dao
 * @author Leevi
 * 日期2020-08-27  12:09
 */
public class UserDao {
    /**
     * 执行添加用户的SQL语句的方法
     * @param user
     * @throws SQLException
     */
    public void saveUser(User user) throws Exception {
        QueryRunner queryRunner = new QueryRunner(DruidUtil.getDataSource());
        String sql = "insert into user values (null,?,?,?,?,?,?,?)";
        queryRunner.update(sql,user.getUsername(),user.getPassword(),user.getAddress(),user.getNickname(),user.getGender(),user.getEmail(),user.getStatus());
    }

    /**
     * 根据username和password执行查询用户信息
     * @param username
     * @param password
     * @return
     * @throws SQLException
     */
    public User findUser(String username,String password) throws Exception {
        QueryRunner queryRunner = new QueryRunner(DruidUtil.getDataSource());
        String sql = "select * from user where username=? and password=?";
        User user = queryRunner.query(sql, new BeanHandler<>(User.class), username, password);
        return user;
    }
}

你可能感兴趣的:(jsp,三层架构)