AJAX与JSON

AJAX概述

  • 概念

    • Asynchronous JavaScript And XML 异步的JavaScript和XML
  • 同步和异步的区别

    • 同步方式:BS项目都是同步的。浏览器与服务器是串行操作,浏览器工作的时候,服务器闲置的,服务器工作的时候,浏览器是等待的。
    • 异步方式:
      • 并行操作:浏览器与服务器是并行操作的,浏览器工作的时候,服务器也可以工作。
      • 后台发送:浏览器的请求是后台发送给服务器的,用户在前端操作的时候感觉不到请求已经发送了。服务器处理完结果以后也是后台将处理结果发送给浏览器。
      • 局部刷新:浏览器接收结果以后进行页面的局部刷新。

        好处:用户的体验会更好,以后很多时候我们会使用异步的方式开发。

  • ajax使用的技术

    • JavaScript:后台发送请求和接收服务器的响应都是JS完成。
    • XML:因为服务端的编程语言与浏览器端的编程语言不同,不同的语言间进行数据交换,使用XML从服务器端返回的数据,在浏览器再解析获取数据。
      但是:因为XML的生成和解析过于繁琐,目前已经很少用了,取而代之的是JSON格式。
      在这里插入图片描述

原生ajax的应用场景和访问流程

  • AJAX的应用场景
    1、检查用户名是否已经被注册
    2、省市下拉框联动
    3、内容自动补全

  • AJAX的执行流程
    在这里插入图片描述

  • 流程说明

    • 用户在浏览器上操作的收获,由JS后台创建XMLHttpRequest对象。
    • 设置请求对象的回调函数(事件处理函数),准备状态发生变化时激活的函数。
    • 后台发送请i去给服务器
    • 在服务器进行数据的处理。
    • 服务器将处理结果以XML的方式发送给浏览器
    • 数据发送回来会激活状态改变事件
    • 在回调函数中处理页面的更新,实现局部刷新。
    • 用户就可以看到最新的处理结果

XMLHttpRequest对象的事件、方法和属性

  • 语法

    创建XMLHttpRequest对象说明
    new XMLHttpRequest()通过无参的构造方法创建对象
    XMLHttpRequest对象的事件说明
    onreadystatechange如果它的准备状态发生了变化就会激活这个事件
    在发送请求,接收数据等情况下这个状态会发生变化
    XMLHttpRequest对象的属性说明
    readyState获取准备状态的值:
    0 还未开始
    1 开始发送请求
    2 请求结束
    3 开始接收响应
    4 接收响应完成
    status获取服务器状态码,如:200服务器正常响应
    responseText获取服务器响应的文本
    XMLHttpRequest对象的方法说明
    open(“GET”,“URL”,true)打开服务器的连接
    GET:请求的方式,还可以有POST
    URL:访问服务器地址
    true:异步,false表示同步
    send()发送请求

案例:使用原生的AJAX判断用户名是否存在、

  • 服务端
    • 步骤

      • 编写一个Servlet(Demo1UserServlet)
      • 设置响应的类型为text/plain;charset=utf-8,纯文本的数据。
      • 得到客户端发送过来的数据:request.getParameter()
      • 如果用户名忽略大小写比较等于newboy,则向客户端打印“用户已经存在”,否则打印“恭喜你,可以注册”;
    • 实现

      • Serclet
        package servlet;
        
        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.io.PrintWriter;
        
        @WebServlet("/register")
        public class Demo1RegisterServlet extends HttpServlet {
        
            @Override
            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                //设置响应类型和编码,注:输出的类型是纯文本
                response.setContentType("text/plain;charset=utf-8");
                PrintWriter out = response.getWriter();
        
                request.setCharacterEncoding("utf-8");
                //1.获取浏览器发送的数据
                String username = request.getParameter("username");
                //2.判断用户是否存在
                if ("newboy".equals(username)) {
                    //3.将结果打印给浏览器
                    out.print("用户名已经存在");
                } else {
                    out.print("恭喜你可以注册");
                }
            }
        }
        
        
    • 客户端

      • 步骤
        • 文本框失去焦点,得到文本框中的姓名
        • 创建XMLHttpRuquest请求对象
        • 设置请求对象的onreadystatechange事件,即"准备状态改变"事件。
        • 当readyState等于4,并且服务器status响应码为200则表示成功。
        • 通过responseText得到响应的字符串。
        • 如果用户存在,在后面的span显示"用户已经存在"。
        • 不存在,在后面的span中显示“恭喜你,可以注册”。
        • 设置请求的URL,将用户以url参数传递
        • 调用open方法,设置提交给服务器的请求方式和地址
        • 调用send方法发送请求
      • 实现
        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>使用原生的Ajax</title>
        </head>
        <body>
        <h2>用户注册</h2>
        用户名:
        <input type="text" name="user" id="user"> <span id="info"></span>
        </body>
        <script type="text/javascript">
            //文本框失去焦点事件
            document.getElementById("user").onblur = function () {
                //1. 获取文本框的值
                let username = this.value;
                //2. 创建请求对象
                let request = new XMLHttpRequest();
                //3. 设置状态改变的回调函数
                request.onreadystatechange = function () {
                    //准备状态等于4而且服务器的状态码等于200就表示服务器正常响应结束
                    if (request.readyState == 4 && request.status == 200) {
                        //获取服务器响应的结果
                        let data = request.responseText;
                        //显示在后面span中
                        document.getElementById("info").innerText = data;
                    }
                }
                //4. 发送请求给服务器,指定servlet的访问地址,还要指定参数
                let url = "register?username=" + username;
                request.open("GET", url, true);
                request.send();
            }
        </script>
        </html>
        
  • 小结
    • 浏览器端的访问流程

      获取文本框的值
      创建请求对象
      设置状态改变的回调函数
      发送请求给服务器,指定Servlet的访问地址,还要指定参数。

传统的$.get()和$.post()方法的使用

  • 与ajax操作相关的jQuery方法
    在这里插入图片描述

  • 语法

    • 中括号表示可选参数 在这里插入图片描述
      在这里插入图片描述
      参数说明
      名称解释
      l服务器访问地址
      ta浏览器发送的数据,有两种格式:
      1. 键=值&键=值
      2. {键:值,键:值}
      llback回调函数,回调函数的参数就是服务器返回的数据
      pe服务器返回的数据类型,取值可以是 xml, html, script, json, text, _default等
  • 演示

    • 步骤
      • 导入jQuery框架的js文件
      • 编写文本框失去焦点blur()事件
      • 得到文本框中的值
      • 使用$.get方法发送请求给服务器,回调函数的参数就是返回值。
      • 根据返回的结果,在回调函数中设置span的text。
    • 实现
      • HTML代码
        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>使用原生的Ajax</title>
            <script src="js/jquery-3.3.1.js"></script>
        </head>
        <body>
        <h2>用户注册</h2>
        用户名:
        <input type="text" name="user" id="user"> <span id="info"></span>
        </body>
        <script type="text/javascript">
            //失去焦点的事件
            $("#user").blur(function () {
                //获取文本框的值
                let username = $(this).val();
                //后台访问服务器:url, data, callback
                let data = "username=" + username;
                //回调函数的参数就是服务器返回的数据
                $.post("register", data, function (result) {
                    $("#info").text(result);
                })
            });
        </script>
        </html>
        
      • 服务器端代码
        
        package servlet;
        
        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.io.PrintWriter;
        
        @WebServlet("/register")
        public class Demo1RegisterServlet extends HttpServlet {
        
            @Override
            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                //设置响应类型和编码,注:输出的类型是纯文本
                response.setContentType("text/plain;charset=utf-8");
                PrintWriter out = response.getWriter();
        
                System.out.println("请求的方法:" + request.getMethod());
        
                //request.setCharacterEncoding("utf-8");
                //1.获取浏览器发送的数据
                String username = request.getParameter("username");
                //没有汉字乱码的问题
                System.out.println("用户名:" + username);
        
                //2.判断用户是否存在
                if ("newboy".equals(username)) {
                    //3.将结果打印给浏览器
                    out.print("用户名已经存在");
                } else {
                    out.print("恭喜你可以注册");
                }
            }
        }
        
  • 小结

    参数名称解释
    url服务器访问地址
    data发送给服务器的数据
    callback回调函数,它的参数是:服务器返回的数据
    type指定服务器返回的数据类型,可省

$.ajax()方法的使用

  • 语法
    在这里插入图片描述
    参数说明

    属性名称解释
    url服务器访问的地址
    async异步或同步,默认是异步,设置为false表示同步
    method提交的方法:get或post
    data浏览器发送的数据,有两种格式:
    1. 键=值&键=值
    2. {键:值,键:值}
    dataType指定服务器返回的数据类型,可省
    success服务器正常响应的回调函数,参数就是服务器返回的数据
    error服务器出现异常时的调函数,参数是XMLHttpRequest对象
  • 案例:使用AJAX实现后台用户登录的功能

    • 技术点
      在这里插入图片描述
    • 服务器
      • 步骤
        • 设置响应类型text/plain;charset=utf-8,得到打印流
        • 得到用户名和密码
        • 判断用户名和密码是否正确
        • 如果正确,则打印:欢迎您,username ,登录成功
        • 错误则打印:登录失败
      • 实现
        package servlet;
        
        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.io.PrintWriter;
        
        @WebServlet("/login")
        public class Demo2LoginServlet extends HttpServlet {
        
            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                response.setContentType("text/plain;charset=utf-8");
                PrintWriter out = response.getWriter();
        
                //1. 获取用户名和密码
                String username = request.getParameter("username");
                String password = request.getParameter("password");
                //2. 登录
                if ("newboy".equals(username) && "123".equals(password)) {
                    out.print("欢迎您!" + username + "登录成功");
                }
                //3. 输出登录成功或失败
                else {
                    out.print("登录失败");
                }
            }
        
        }
        
    • 客户端
      • 步骤
      1. 页面代码如下:login.html,页面上有一个登录的表单数据。注:登录按钮是一个普通的button
      2. 给登录按钮添加点击事件
      3. 得到表单中所有的数据项: serialize()
      4. 使用$.ajax方法提交数据给服务器
        1. 设置url请求地址
        2. data数据
        3. success成功的回调函数
        4. 在回调函数中直接使用alert弹出服务器返回的数据
      • 实现
        <!DOCTYPE html>
        <html lang="zh-CN">
        <head>
            <meta charset="UTF-8">
            <title>用户登录</title>
            <script src="js/jquery-3.3.1.js"></script>
        </head>
        <body>
        <h2>用户登录</h2>
        <form id="loginForm">
            <table>
                <tr>
                    <td>用户名</td>
                    <td><input type="text" name="username" id="username"/></td>
                </tr>
                <tr>
                    <td>密码</td>
                    <td><input type="password" name="password" id="password"/></td>
                </tr>
                <tr>
                    <td colspan="2" align="center"><input type="button" value="登录" id="btnLogin"/></td>
                </tr>
            </table>
        </form>
        
        <script type="text/javascript">
            //点击事件,ES6中匿名函数的写法,类似于java中lambda
            $("#btnLogin").click(() => {
                //表单对象的序列化的方法,作用:将整个表单拼接成一个字符串
                let param = $("#loginForm").serialize();
                //后台访问
                $.ajax({
                    url: "login",
                    data: param,
                    //正常响应的回调函数,参数是服务器返回数据
                    success: (result) => {
                        alert(result);
                    }
                });
            });
        </script>
        </body>
        </html>
        

jQuery3.0的$.get()和$.post方法实现登录

  • 语法
    • 与$.ajax方法完全一样
  • 案例:GET新增签名方式实现上面的登录
    • 步骤
    1. 给登录按钮添加点击事件
    2. 得到表单中所有的数据项
    3. 使用$.get()方法发送请求
      1. 设置url请求地址
      2. 设置发送的数据
      3. 设置success回调函数
      4. 在回调函数中处理返回的数据
    • 实现
      • html
        <!DOCTYPE html>
        <html lang="zh-CN">
        <head>
            <meta charset="UTF-8">
            <title>用户登录</title>
            <script src="js/jquery-3.3.1.js"></script>
        </head>
        <body>
        <h2>用户登录</h2>
        <form id="loginForm">
            <table>
                <tr>
                    <td>用户名</td>
                    <td><input type="text" name="username" id="username"/></td>
                </tr>
                <tr>
                    <td>密码</td>
                    <td><input type="password" name="password" id="password"/></td>
                </tr>
                <tr>
                    <td colspan="2" align="center"><input type="button" value="登录" id="btnLogin"/></td>
                </tr>
            </table>
        </form>
        
        <script type="text/javascript">
            //点击事件,ES6中匿名函数的写法,类似于java中lambda
            $("#btnLogin").click(() => {
                //表单对象的序列化的方法,作用:将整个表单拼接成一个字符串
                let param = $("#loginForm").serialize();
                //后台访问
                $.post({
                    url: "login",
                    async: false,  //默认是异步(并行), false是同步:会等到服务器响应回来以后才继续向后执行
                    method: "get",  //以这个为准
                    data: param,
                    //正常响应的回调函数,参数是服务器返回数据
                    success: (result) => {
                        alert(result);
                    },
                    //参数是:XMLHttpRequest对象
                    error: (request) => {
                        //获取服务器状态码
                        alert("服务器出现异常:" + request.status);
                    }
                });
                //----------
                //alert("后面的代码继续执行");
            });
        </script>
        </body>
        </html>
        
      • 服务器
        package servlet;
        
        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.io.PrintWriter;
        
        @WebServlet("/login")
        public class Demo2LoginServlet extends HttpServlet {
        
            protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                response.setContentType("text/plain;charset=utf-8");
                PrintWriter out = response.getWriter();
        
                //1. 获取用户名和密码
                String username = request.getParameter("username");
                String password = request.getParameter("password");
                //2. 登录
                if ("newboy".equals(username) && "123".equals(password)) {
                    out.print("欢迎您!" + username + "登录成功");
                }
                //3. 输出登录成功或失败
                else {
                    out.print("登录失败");
                }
            }
        
        }
        
  • 小结
    属性名称解释
    url访问服务器地址
    async异步或同步
    data发送的数据
    method请求的方式
    dataType返回数据类型
    success正常响应的回调函数
    error异常的回调函数

JSON的格式介绍

  • 概述

    • JSON (JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交换格式。采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
      在JSON出现之前,大家一直用XML来传递数据。因为XML是一种纯文本格式,所以它适合在网络上交换数据。XML本身不算复杂,但你会发现XML中大量的数据是描述信息,真正的数据只占了一小部分,而且它的解析和生成很繁琐。
  • 几种格式的对比

    • Java类
      class User {
         Integer id = 1;
         String name = "小乔";
         Boolean sex = false;
      }
      
    • XML文件
      <user>
          <id>1</id>
          <name>小乔</name>
          <sex>false</sex>
      </user>
      
    • JSON格式
      • JSON只在JS中有效,Java是不支持JSON对象的,只能生成JSON格式的字符串
        {
          id:1,
          name: "小乔",
          sex: false
        }
        
  • 创建一个JSON对象

    • 步骤
    1. 创建这种格式的对象:{"param":[{key:value,key:value},{key:value,key:value}]}
    2. 一个JSON对象,有一个brothers属性,属性值是一个数组
    3. 数组中的每个元素是一个JSON对象,属性名为:userName,realName,age。
    4. 数组中一共3个元素,给每个元素赋值
    5. 得到JSON对象的brothers属性,返回一个数组,对数组进行遍历输出每个元素中的属性值
    • 实现
      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>创建JSON对象</title>
      </head>
      <body>
      <script type="text/javascript">
          //创建一个person对象的JSON对象,属性名:userName、realName、age给三个属性赋值。对应服务器端的一个Java对象
          let person = {
              userName: "弼马温",
              realName: "孙悟空",
              age: 501
          };
          //输出每个属性,取值:对象名.属性名
          document.write(`昵称:${person.userName} 真名:${person.realName} 年龄:${person.age}<br/>`);
      
          document.write("<hr/>");
          /*
          创建一个数组,其中每个元素是JSON对象
          对应服务器端的List<Java对象>
           */
          let brothers = [{
              userName: "弼马温",
              realName: "孙悟空",
              age: 501
          }, {
              userName: "天蓬元帅",
              realName: "猪八戒",
              age: 360
          }, {
              userName: "卷帘大将",
              realName: "沙悟净",
              age: 240
          }];
          //输出每个元素
          for (let brother of brothers) {
              document.write(`昵称:${brother.userName} 真名:${brother.realName} 年龄:${brother.age}<br/>`);
          }
      
          document.write("<hr/>");
      
          //一个对象某个属性是一个集合,标准的JSON在服务器生成的时候,属性名必须加引号,在浏览器端是可以省略的
          let master = {
              "name": "唐三藏",
              "age": 25,
              //某个属性是一个集合,前面的是名字,后面的是值
              "brothers": brothers
          };
          document.write(`名字:${master.name} 年龄:${master.age} 他的兄弟:<br/>`);
          for (let brother of master.brothers) {
              document.write(`昵称:${brother.userName} 真名:${brother.realName} 年龄:${brother.age}<br/>`);
          }
      </script>
      </body>
      </html>
      
  • 小结

    • JSON三种常用的格式:
      1. 直接是一个对象 {键:值,键:值}
      2. 数组或集合中包含其它的对象 [{},{},{}]
      3. 对象包含了集合 {键:[]}

JS中操作JSON的方法

  • 代码
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>与JSON有关的函数</title>
    </head>
    <body>
    <script type="text/javascript">
        /**
         * 我们以后很少直接去使用,因为框架在低层已经转换好了
          */
        //字符串
        let str = '{"name": "张三", "age": 20}';
        //将字符串转成对象
        let json = JSON.parse(str);
        document.write(json.name + "," + json.age + "<br/>");
        //转对象转成字符串
        let s2 = JSON.stringify(json);
        document.write(s2 + "<br/>");
    </script>
    </body>
    </html>
    
  • 小结
    语法功能
    JSON.parse(字符串)将字符串转成对象
    JSON.stringify(JSON对象)将对象转成字符串

将Java对象转成JSON字符串

  • 准备实体类

    package entity;
    
    public class User {
    
        private int id;
        private String username;
        private int age;
    
        public User(int id, String username, int age) {
            this.id = id;
            this.username = username;
            this.age = age;
        }
    
        public User() {
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }
    
  • 服务器端 json的转换工具

    • json转换工具的概述
      • json的转换工具是通过java封装好的一些jar工具包,直接将java对象或集合转换成json格式的字符串。
    • 常见的json转换工具
      在这里插入图片描述
  • 转换代码实现

    • 步骤
    1. 创建一个User类:包含id,username,age三个属性
    2. 在main函数中,实例化User对象,给属性赋值。使用JSON格式打印到控制台。
    3. 创建一个字符串数组对象,包含三个字符串
    4. 创建一个List<User>,包含三个用户对象
    5. 创建一个Map对象,键名为:user,键为User对象
    6. 实例化ObjectMapper对象,调用writeValueAsString()方法输出转换后的字符串对象
    • 实现
      package json;
      
      import com.fasterxml.jackson.core.JsonProcessingException;
      import com.fasterxml.jackson.databind.ObjectMapper;
      import entity.User;
      
      import java.util.ArrayList;
      import java.util.HashMap;
      import java.util.List;
      
      public class JsonParse {
      
          public static void main(String[] args) throws JsonProcessingException {
              //1.创建映射对象
              ObjectMapper mapper = new ObjectMapper();
      
              User user = new User(1, "牛魔王", 520);
              //转成JSON字符串
              //2.调用方法转换
              String json1 = mapper.writeValueAsString(user);
              System.out.println(json1);
      
              //转换数组
              String[] arr = {"牛魔王", "铁扇公主", "红孩儿", "太上老君"};
              String json2 = mapper.writeValueAsString(arr);
              System.out.println(json2);
      
              //转换集合
              List<User> users = new ArrayList<>();
              //按alt选中多行,可以一次输入多行
              users.add(new User(1, "牛魔王", 520));
              users.add(new User(2, "铁扇公主", 28));
              users.add(new User(3, "红孩儿", 5));
              String json3 = mapper.writeValueAsString(users);
              System.out.println(json3);
      
              //转map对象
              HashMap<String, User> map = new HashMap<>();
              map.put("user1", new User(2, "铁扇公主", 28));
              map.put("user2", new User(3, "孙悟空", 30));
              String json4 = mapper.writeValueAsString(map);
              System.out.println(json4);
          }
      }
      

案例:省市级联环境搭建

  • 分析
    在这里插入图片描述
  • 代码
    • Sql语句

      -- 同一张表中包含省和市
      create table `area` (
      `id` int primary key,
      `name` varchar (50),
      `pid` int   -- 父级的id,如果为0,表示没有父元素
      ); 
      
      -- pid为0表示省,否则为某一个省下面的市
      insert into `area` (`id`, `name`, `pid`) values(1,'广东省',0);
      insert into `area` (`id`, `name`, `pid`) values(2,'湖南省',0);
      insert into `area` (`id`, `name`, `pid`) values(3,'广西省',0);
      insert into `area` (`id`, `name`, `pid`) values(4,'深圳',1);
      insert into `area` (`id`, `name`, `pid`) values(5,'广州',1);
      insert into `area` (`id`, `name`, `pid`) values(6,'东莞',1);
      insert into `area` (`id`, `name`, `pid`) values(7,'长沙',2);
      insert into `area` (`id`, `name`, `pid`) values(8,'株洲',2);
      insert into `area` (`id`, `name`, `pid`) values(9,'湘潭',2);
      insert into `area` (`id`, `name`, `pid`) values(10,'南宁',3);
      insert into `area` (`id`, `name`, `pid`) values(11,'柳州',3);
      insert into `area` (`id`, `name`, `pid`) values(12,'桂林',3);
      
      select * from `area`;
      
    • Area实体类

      package entity;
      
      /**
       * 区域
       */
      public class Area {
      
          private int id;  //主键
          private String name;  //名字
          private int pid;  //上一级的id
      
          @Override
          public String toString() {
              return "Area{" +
                      "id=" + id +
                      ", name='" + name + '\'' +
                      ", pid=" + pid +
                      '}';
          }
      
          public int getId() {
              return id;
          }
      
          public void setId(int id) {
              this.id = id;
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      
          public int getPid() {
              return pid;
          }
      
          public void setPid(int pid) {
              this.pid = pid;
          }
      }
      
    • 核心配置文件mybatis-config.xml

      <?xml version="1.0" encoding="UTF-8" ?>
      <!DOCTYPE configuration
              PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
              "http://mybatis.org/dtd/mybatis-3-config.dtd">
      <configuration>
      
          <settings>
              <!--在控制台显示SQL语句-->
              <setting name="logImpl" value="STDOUT_LOGGING"/>
          </settings>
      
          <!--定义实体类别名-->
          <typeAliases>
              <package name="entity"/>
          </typeAliases>
      
          <environments default="default">
              <!--环境变量-->
              <environment id="default">
                  <!--事务管理器-->
                  <transactionManager type="JDBC"/>
                  <!--数据源-->
                  <dataSource type="POOLED">
                      <property name="driver" value="com.mysql.jdbc.Driver"/>
                      <property name="url" value="jdbc:mysql://localhost:3306/day34"/>
                      <property name="username" value="root"/>
                      <property name="password" value="root"/>
                  </dataSource>
              </environment>
          </environments>
      
          <!--加载其它映射文件-->
          <mappers>
              <package name="dao"/>
          </mappers>
      </configuration>
      
    • 会话工具类:SqlSessionUtils

      package utils;
      
      import org.apache.ibatis.io.Resources;
      import org.apache.ibatis.session.SqlSession;
      import org.apache.ibatis.session.SqlSessionFactory;
      import org.apache.ibatis.session.SqlSessionFactoryBuilder;
      
      import java.io.IOException;
      import java.io.InputStream;
      
      /**
       * 会话工具类
       */
      public class SqlSessionUtils {
      
          //会话工厂的创建类->会话工厂->会话
          private static SqlSessionFactory factory;
      
          static {
              //1.会话工厂的创建类
              SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
              //2.得到配置文件的输入流
              try (InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml")) {
                  //3.创建会话工厂
                  factory = builder.build(inputStream);
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
      
          /**
           * 得到会话对象
           * @return
           */
          public static SqlSession getSession() {
              return factory.openSession();
          }
      
      
      }
      

通用的DAO接口代理类

  • 动态代理的好处

    1. 接口的代理对象由程序在执行的过程中动态生成,不用我们自己去写一个类实现接口中所有的方法
    2. 可以动态生成任意接口的对象
  • Proxy类中的方法
    在这里插入图片描述

  • InvocationHandler接口
    在这里插入图片描述

    package dao.impl;
    
    import dao.AreaDao;
    import entity.Area;
    import utils.SqlSessionUtils;
    import org.apache.ibatis.session.SqlSession;
    
    import java.util.List;
    
    public class AreaDaoImpl implements AreaDao {
        /**
         * 查询相应的省份或城市
         * @param pid
         */
        @Override
        public List<Area> findAllByPid(int pid) {
            //1.获取会话对象
            SqlSession session = SqlSessionUtils.getSession();
            //2.得到dao的代理对象
            AreaDao areaDao = session.getMapper(AreaDao.class);
            //3.调用接口中方法,查询得到结果
            List<Area> areaList = areaDao.findAllByPid(pid);
            //4.关闭会话
            session.commit();
            session.close();
            //5.返回
            return areaList;
        }
    }
    
  • 步骤

    1. 创建类:DaoInstanceFactory,使用动态代理创建DAO的实现类

    2. 创建静态方法得到DAO的实现类,方法签名:

      public static <T> T getBean(Class<T> daoInterface)
      
    3. 在每个被代理的方法中

      1. 得到会话对象
      2. 通过会话得到Mapper对象
      3. 调用原来DAO接口中的方法
      4. 提交事务
      5. 关闭会话
      6. 返回原来方法的值
  • 代码

    package utils;
    
    import org.apache.ibatis.session.SqlSession;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.util.Arrays;
    
    /**
     * 所有DAO接口的代理实现类
     */
    public class DaoInstanceFactory {
    
        /**
         * 获取DAO接口的代理对象
         */
        public static <T> T getBean(Class<T> interfaceClass) {
            /**
             * 参数1:类加载器,我们自己写的类是同一个类加载器
             * 参数2:实现的接口
             * 参数3:回调函数,接口中每个方法都会调用一次
             */
            return (T) Proxy.newProxyInstance(
                    DaoInstanceFactory.class.getClassLoader(),
                    new Class[]{interfaceClass},
                    new InvocationHandler() {
                        /**
                         * 每个被代理的方法都会调用一次
                         * @param proxy 代理对象
                         * @param method 被代理的方法
                         * @param args 方法参数
                         * @return 方法的返回值
                         */
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            //1.获取会话对象
                            SqlSession session = SqlSessionUtils.getSession();
                            //2.得到dao的代理对象
                            T dao = session.getMapper(interfaceClass);
                            //3.调用接口中方法,查询得到结果
                            //参数1:调用方法的对象,参数2:方法参数数组
                            System.out.println("调用了DAO的方法:" + method.getName() + ",参数是:" + Arrays.toString(args));
                            Object result = method.invoke(dao, args);
                            //4.关闭会话
                            session.commit();
                            session.close();
                            //5.返回
                            return result;
                        }
                    });
        }
    }
    
    
  • AreaDao

    package dao;
    
    import entity.Area;
    import org.apache.ibatis.annotations.Select;
    
    import java.util.List;
    
    /**
     * 持久层
     */
    public interface AreaDao {
    
        /**
         * 查询相应的省份或城市
         */
        @Select("SELECT * FROM `area` WHERE pid=#{pid}")
        List<Area> findAllByPid(int pid);
    }
    
    
  • AreaService接口

    package service;
    
    import entity.Area;
    
    import java.util.List;
    
    /*
    业务层
     */
    public interface AreaService {
    
        /**
         * 查询相应的省份或城市
         * @param pid
         */
        List<Area> findCitiesByPid(int pid);
    
    }
    
    
  • AreaServiceImpl实现类

    package service.impl;
    
    import dao.AreaDao;
    import entity.Area;
    import service.AreaService;
    import utils.DaoInstanceFactory;
    
    import java.util.List;
    
    public class AreaServiceImpl implements AreaService {
    
        //依赖于dao层
        private AreaDao areaDao = DaoInstanceFactory.getBean(AreaDao.class);
    
        /**
         * 查询相应的省份或城市
         * @param pid
         */
        @Override
        public List<Area> findCitiesByPid(int pid) {
            return areaDao.findAllByPid(pid);
        }
    }
    
    

省市级联Servlet

  • 步骤

    1. 得到省份pid的值
    2. 查询某个省下所有的城市
    3. 转成JSON字符串输出到浏览器
  • 实现代码

    package servlet;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import entity.Area;
    import service.AreaService;
    import service.impl.AreaServiceImpl;
    
    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.io.PrintWriter;
    import java.util.List;
    
    @WebServlet("/area")
    public class AreaServlet extends HttpServlet {
    
        private AreaService areaService = new AreaServiceImpl();
    
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
           //1. 得到pid
            int pid = Integer.parseInt(request.getParameter("pid"));
            //2. 调用业务层查询,通过pid来查询城市或省份
            List<Area> areaList = areaService.findCitiesByPid(pid);
            //3. 将查询的结果转成JSON字符串
            String json = new ObjectMapper().writeValueAsString(areaList);
            //4. 以JSON格式发送给浏览器
            response.setContentType("application/json;charset=utf-8");
            PrintWriter out = response.getWriter();
            out.print(json);
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doPost(request, response);
        }
    }
    
    

省市级联HTML

  • 步骤

    1. 加载所有的省份
      1. 页面加载访问服务器,传递的数据是pid=0
      2. 在回调函数中操作,除了第1项option之外删除
      3. 遍历集合,每个元素添加成一个option。值是id属性,文本内容是name属性
    2. 使用省份的改变事件
      1. 访问服务器,传递的数据是pid当前选中的值
      2. 在回调函数中操作,除了第1项option之外删除
      3. 遍历集合,每个元素添加成一个option。值是id属性,文本内容是name属性
  • 实现

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>省市级联</title>
        <script type="text/javascript" src="js/jquery-3.3.1.js"></script>
        <script type="text/javascript">
            //页面加载完毕就加载所有的省份
            $(function () {
    
                //加载省份
                $.get({
                    url: "area", //访问地址
                    data: "pid=0",  //发送的数据
                    success: function (areas) { //返回所有的省份
                        for (let area of areas) {
                            $("#province").append(`<option value="${area.id}">${area.name}</option>`);
                        }
                    }
                });
    
                //加载城市,省份的改变事件
                $("#province").change(function () {
                    $.get({
                        url: "area", //访问地址
                        data: "pid=" + $(this).val(),  //发送的数据是当前选中的省份的id值
                        success: function (areas) { //返回所有的省份
                            //除了第一项的元素全部清空
                            $("#city>option:gt(0)").remove();
                            //填充
                            for (let area of areas) {
                                $("#city").append(`<option value="${area.id}">${area.name}</option>`);
                            }
                        }
                    });
                });
    
            })
        </script>
    </head>
    <body>
    
    <select id="province">
        <option>---请选择省---</option>
    </select>
    <select id="city">
        <option>---请选择城市---</option>
    </select>
    </body>
    </html>
    

自动补全项目搭建

  • 分析
    在这里插入图片描述
  • 步骤
    1. 查询以输入的关键字开头的用户
    2. 文本框使用keyup事件,得到文本框中的值,去掉前后空格。如果文本框的内容为空则不提交给服务器。
    3. 查询成功的回调函数中如果返回的用户数大于0才进行div的拼接,拼接以后显示在div中。
    4. 否则要隐藏div。
    5. 给大div中的子div绑定鼠标点击事件,点击某个名字则将div的文本内容显示在文本框中,并隐藏div
  • 实现
    • 导入数据库脚本

      CREATE TABLE `user` (
        `id` int(11)  PRIMARY KEY AUTO_INCREMENT,
        `name` varchar(32) 
      );
      
      INSERT INTO `user` (`name`) VALUES ('张三');
      INSERT INTO `user` (`name`) VALUES ('李四');
      INSERT INTO `user` (`name`) VALUES ('王五');
      INSERT INTO `user` (`name`) VALUES ('赵六');
      INSERT INTO `user` (`name`) VALUES ('田七');
      INSERT INTO `user` (`name`) VALUES ('孙八');
      INSERT INTO `user` (`name`) VALUES ('张三丰');
      INSERT INTO `user` (`name`) VALUES ('张无忌');
      INSERT INTO `user` (`name`) VALUES ('李寻欢');
      INSERT INTO `user` (`name`) VALUES ('王维');
      INSERT INTO `user` (`name`) VALUES ('李白');
      INSERT INTO `user` (`name`) VALUES ('杜甫');
      INSERT INTO `user` (`name`) VALUES ('李贺');
      INSERT INTO `user` (`name`) VALUES ('李逵');
      INSERT INTO `user` (`name`) VALUES ('宋江');
      INSERT INTO `user` (`name`) VALUES ('王英');
      INSERT INTO `user` (`name`) VALUES ('鲁智深');
      INSERT INTO `user` (`name`) VALUES ('武松');
      INSERT INTO `user` (`name`) VALUES ('张薇');
      INSERT INTO `user` (`name`) VALUES ('张浩');
      INSERT INTO `user` (`name`) VALUES ('刘小轩');
      INSERT INTO `user` (`name`) VALUES ('刘浩宇');
      
      
    • 实体类

      public class User {
      
      	private int id;
      	private String name;
      	
      	public int getId() {
      		return id;
      	}
      	public void setId(int id) {
      		this.id = id;
      	}
      	public String getName() {
      		return name;
      	}
      	public void setName(String name) {
      		this.name = name;
      	}
      }
      
    • UserDao接口

      package dao;
      
      import entity.User;
      import org.apache.ibatis.annotations.Select;
      
      import java.util.List;
      
      /**
       * 用户数据访问层 Data Access Object(持久层)
       */
      public interface UserDao {
      
          /**
           * 查询指定字符串开头的用户列表
           */
          @Select("SELECT * FROM `user` WHERE `name` LIKE #{name}\"%\"")
          List<User> findUserByPrefix(String name);
      
      }
      
      
    • UserService接口

      package service;
      
      import entity.User;
      
      import java.util.List;
      
      public interface UserService {
      
          /**
           * 查询指定字符串开头的用户信息
           */
          List<User> findUserByPrefix(String name);
      
      }
      
      
    • UserServiceImpl实现类

      package service.impl;
      
      import dao.UserDao;
      import entity.User;
      import service.UserService;
      import utils.DaoInstanceFactory;
      
      import java.util.List;
      
      /**
       * 业务层
       */
      public class UserServiceImpl implements UserService {
      
          private UserDao userDao = DaoInstanceFactory.getBean(UserDao.class);
          /**
           * 查询指定字符串开头的用户信息
           * @param name
           */
          @Override
          public List<User> findUserByPrefix(String name) {
              return userDao.findUserByPrefix(name);
          }
      }
      
      

自动补全Servlet

  • 步骤
    1. 要将响应的类型设置成:application/json;charset=utf-8
    2. 得到客户端提交的用户名
    3. 调用业务层查询以这个字符串开头的用户,返回List<User>
    4. 调用ObjectMapper对象中方法将List转成JSON字符串
    5. 发送给浏览器
  • 实现
    package servlet;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import entity.User;
    import service.UserService;
    import service.impl.UserServiceImpl;
    
    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.io.PrintWriter;
    import java.util.List;
    
    @WebServlet("/user")
    public class UserServlet extends HttpServlet {
    
        private UserService userService = new UserServiceImpl();
    
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            //1.获取提交的用户名
            String username = request.getParameter("username");
            //2.调用业务层查询以这个名字开头的用户
            List<User> userList = userService.findUserByPrefix(username);
            //3.转成JSON字符串
            String json = new ObjectMapper().writeValueAsString(userList);
    
            response.setContentType("application/json;charset=utf-8");
            PrintWriter out = response.getWriter();
            //4.打印到浏览器端
            out.print(json);
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doPost(request, response);
        }
    }
    
    

HTML页面代码

  • 步骤

    1. 编写文本框的keyup事件
    2. 得到word文本框的值
    3. 去掉值前后的空格,判断值是否为空,如果为空,则返回不再继续。
    4. 否则使用$.post方法发送请求给服务器
    5. 在success回调函数中得到服务器返回数据:JSON格式用户集合
    6. 如果集合不为空,进行字符串拼接。拼接完成以后使用html()方法填充到#show的div中
    7. 如果集合为空,则隐藏#show的div
  • 实现

    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <title>自动完成</title>
        <style type="text/css">
            .content {
                width: 400px;
                margin: 30px auto;
                text-align: center;
            }
    
            input[type='text'] {
                box-sizing: border-box;
                width: 280px;
                height: 30px;
                font-size: 14px;
                border: 1px solid #38f;
            }
    
            input[type='button'] {
                width: 100px;
                height: 30px;
                background: #38f;
                border: 0;
                color: #fff;
                font-size: 15px;
            }
    
            #show {
                box-sizing: border-box;
                position: relative;
                left: 7px;
                font-size: 14px;
                width: 280px;
                border: 1px solid dodgerblue;
                text-align: left;
                border-top: 0;
                /*一开始是隐藏不可见*/
                display: none;
            }
    
            #show div {
                padding: 4px;
                background-color: white;
            }
    
            #show div:hover {
                /*鼠标移上去背景变色*/
                background-color: #3388ff;
                color: white;
            }
        </style>
        <script type="text/javascript" src="js/jquery-3.3.1.js"></script>
    </head>
    <body>
    <div class="content">
        <img alt="传智播客" src="img/logo.png"><br/><br/>
        <input type="text" name="word" id="word">
        <input type="button" value="搜索一下">
        <div id="show"></div>
    </div>
    
    <script type="text/javascript">
        //文本框中松开按钮事件
        $("#word").keyup(function () {
            //要判断文本框中是否有值
            let username = $(this).val().trim();
            if (username == "") {
                //隐藏大的div
                $("#show").hide();
                return;
            }
            //有值就访问服务器
            $.get({
                url: "user",
                data: "username=" + username,
                success: function (users) { //返回匹配的用户对象
                    //判断如果没有元素就隐藏大的div
                    if (users.length > 0) {
                        //生成所有的子div
                        let html = ``;
                        for (let user of users) {
                            html+=`<div>${user.name}</div>`;
                        }
                        //放在大的div中
                        $("#show").html(html);
                        //显示出来
                        $("#show").show();
                    }
                    else {
                        //没有元素就隐藏
                        $("#show").hide();
                    }
                }
            });
        });
    </script>
    </body>
    </html>
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值