实验( 三 )
实验名称 | 编写基于Ajax的联级下拉菜单实战 |
实验目的 : | |
| |
实验环境: | |
1、硬件要求:计算机一台 2、软件要求:Chrome浏览器、IE11浏览器、Firefox浏览器 页面1参考图 页面2参考图 只能通过页面1进入页面2,如果直接请求页面2的url,则使用servlet过滤器重定向到页面1。 菜单参考如上图片,通过不同条件查询该班所有人员名单,人员属性不少于两项(id,姓名),每个班级不少于两条数据,菜单不少于两项(至少2*2*2=8个班级表,每个表不少于两条数据) | |
实验步骤和内容: | |
1.数据库建立 建立一个名为test3的数据库,数据库中包含user和student两个表。 user表如图3-1所示。 图3-1 user表 student表如图3-2所示。 图3-2 student表 2.数据库连接 在druid.properties中进行数据库配置,确保数据库连接成功。数据库连接配置如图3-3所示。 图3-3 数据库连接配置 3.dao层编写 (1)BasicDAO编写 public class BasicDAO<T>: 这是一个泛型类,T 表示将要操作的对象的类型。通过泛型,可以使该 DAO 类适用于不同类型的对象。 private QueryRunner queryRunner = new QueryRunner();: 在这行代码中,创建了一个 QueryRunner 对象,它是 Apache Commons DbUtils 库中用于执行 SQL 查询和更新的类。 public int update(String sql, Object... parameters): 这个方法用于执行数据操作语言(DML)语句,例如 INSERT、UPDATE、DELETE 等。它接受一个 SQL 语句和可变参数 parameters,并返回受影响的行数。 public List<T> queryMulti(String sql, Class<T> clazz, Object... parameters): 这个方法用于执行查询语句,返回多行结果。它接受一个 SQL 语句、结果对象的类型 clazz 和可变参数 parameters,并返回一个包含查询结果的对象列表。 public T querySingle(String sql, Class<T> clazz, Object... parameters): 这个方法也用于执行查询语句,但是只返回一行结果。它接受一个 SQL 语句、结果对象的类型 clazz 和可变参数 parameters,并返回一个结果对象。 public Object queryScalar(String sql, Object... parameters): 这个方法用于执行查询语句,返回单个值。它接受一个 SQL 语句和可变参数 parameters,并返回一个单值结果。 核心代码如图3-4所示。 图3-4 BasicDAO核心代码 (2)StudentDAO层编写 编写一个用于查询学生信息的方法,它接受三个参数:专业(specialized)、年级(grade)和课程(lesson)。这个方法使用了一个SQL查询语句,通过传入的专业、年级和课程来筛选符合条件的学生信息。查询语句中的占位符(?)表示参数位置,用来防止SQL注入攻击。 接下来,使用了一个名为 queryMulti 的方法执行SQL查询,并将查询结果映射到 Student 类的对象列表中。这个方法可能是一个通用的查询方法,用来执行数据库查询并将结果映射到指定的实体类对象中。 核心代码如图3-5所示。 图3-5 StudentDAO核心代码 (3)UserDAO编写 public boolean queryUserByUsernameAndPassword(User user) {: 这是一个公有方法,返回布尔类型值,用于查询用户是否存在,方法接受一个User对象作为参数。 String sql = "select * from user where username = ? and password = ?";: 这里定义了一个 SQL 查询语句,用于在数据库中查询用户信息。这个查询语句使用了两个占位符?,分别用来表示用户名和密码。 User user1 = querySingle(sql, User.class, user.getUsername(), user.getPassword());: 这一行代码调用了一个叫做querySingle的方法来执行 SQL 查询,并将结果存储在一个User对象中。这个方法可能是一个数据库访问对象(DAO)中的一个方法,它的作用是执行查询并返回单个结果。 if (user1 != null) { return true; } else { return false; }: 这个条件语句检查了querySingle方法返回的结果。如果返回的user1对象不为null,意味着在数据库中找到了符合条件的用户,则返回true;否则返回false,表示未找到符合条件的用户。 核心代码如图3-6所示。 图3-6 UserDAO核心代码 4.bean层编写 (1)Student编写 类的属性包括: id:表示学生的唯一标识符,类型为Integer。 studentId:表示学生的学号,类型为String。 name:表示学生的姓名,类型为String。 specialized:表示学生的专业,类型为String。 grade:表示学生的年级,类型为String。 lesson:表示学生所修课程,类型为String。 类包含了以下方法: 一个无参构造方法,用于创建一个空的Student对象。 一个包含所有属性的构造方法,用于创建具有指定属性值的Student对象。 get和set方法用于获取和设置类的属性值。 toString方法,用于返回一个包含所有属性值的字符串表示 核心代码如图3-7所示。 图3-7 Student核心代码 (2)User编写 package com.rjxy.javabean;:声明了该类所属的包名。 public class User {:定义了一个名为 User 的类。 private Integer id;、private String username;、private String password;:这三行声明了该类的私有属性,分别表示用户的ID、用户名和密码。 public User() { }:无参构造方法,用于创建一个空的 User 对象。 public User(Integer id, String username, String password) { }:带参数的构造方法,用于创建一个具有指定ID、用户名和密码的 User 对象。 public Integer getId() { }、public void setId(Integer id) { }、public String getUsername() { }、public void setUsername(String username) { }、public String getPassword() { }、public void setPassword(String password) { }:这些是公有的 getter 和 setter 方法,用于获取和设置 User 对象的属性值。 @Override:这是 Java 中的注解,表示下面的方法是对父类(Object 类)中的方法进行了重写。 public String toString() { }:重写了 toString() 方法,以便在将 User 对象转换为字符串时,能够得到包含对象属性值的可读性较好的字符串表示形式。 核心代码如图3-8所示。 图3-8 User核心代码 5.utlis层编写 JDBCUtilsByDruid编写 package com.rjxy.utils;: 这是声明了该类所在的包名。 import com.alibaba.druid.pool.DruidDataSourceFactory;: 导入了Druid连接池工厂类,用于创建Druid数据源。 import javax.sql.DataSource;: 导入了Java数据库连接池的DataSource接口,用于获取数据库连接。 import java.sql.Connection;, import java.sql.ResultSet;, import java.sql.SQLException;, import java.sql.Statement;: 导入了Java数据库连接相关的类,用于执行SQL语句和处理结果。 import java.util.Properties;: 导入了Java的Properties类,用于加载配置文件。 public class JDBCUtilsByDruid {: 声明了一个名为JDBCUtilsByDruid的公共类。 private static DataSource dataSource;: 声明了一个静态的DataSource对象dataSource,用于保存数据库连接池。 static { ... }: 这是一个静态初始化块,在类加载时执行。在这里,使用了DruidDataSourceFactory根据配置文件创建了数据库连接池。 Properties properties = new Properties(); ...: 创建了一个Properties对象,加载了名为druid.properties的配置文件,并通过DruidDataSourceFactory创建了数据源。 public static Connection getConnection() throws SQLException { ... }: 提供了一个静态方法getConnection()用于获取数据库连接,抛出了可能的SQL异常。 return dataSource.getConnection();: 在getConnection()方法中,返回了从数据源获取的数据库连接。 public static void close(ResultSet resultSet, Statement statement, Connection connection) { ... }: 提供了一个静态方法close()用于关闭数据库连接、语句和结果集。 if (resultSet != null) { resultSet.close(); } ...: 在close()方法中,检查传入的参数是否为null,如果不为null则关闭相应的资源。资源关闭后可能会抛出SQL异常,这里被捕获并抛出了RuntimeException。 核心代码如图3-9所示。 图3-9 JDBCUtilsByDruid核心代码 6.servlet层和监听器编写 (1)ManageFilter编写 @WebFilter("/views/manage/*"): 这是一个注解,指示该过滤器要拦截所有以"/views/manage/"开头的请求。换句话说,它会拦截所有访问"/views/manage/"目录下的资源的请求。 public class ManageFilter implements Filter {: 这是过滤器类的定义,实现了javax.servlet.Filter接口。它包含了过滤器的初始化、销毁和过滤方法。 public void init(FilterConfig config) throws ServletException { }: 这是过滤器的初始化方法。在此方法中,可以执行任何在过滤器初始化时需要完成的操作。在这个例子中,该方法为空,即没有初始化操作。 public void destroy() { }: 这是过滤器的销毁方法。在此方法中,可以执行任何在过滤器被销毁前需要完成的操作。 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { }: 这是过滤器的核心方法,用于实际过滤请求。当一个请求被发送到与过滤器关联的 URL 时,容器将调用此方法。 核心代码如图3-10所示。 图3-10 ManageFilter核心代码 (2)OnlineCountListener编写 类声明和接口实现: OnlineCountListener 类实现了 HttpSessionAttributeListener 接口,这个接口定义了用于监听 HttpSession 属性变化的方法。 attributeAdded 方法: 当向 HttpSession 中添加属性时,attributeAdded 方法被调用。 首先,它检查被添加的属性是否为 "username",这通常是用来标识用户登录状态的属性。 如果是 "username" 属性被添加,它会获取 ServletContext 对象,这个对象在整个Web应用程序中是唯一的,用来存储应用程序级别的数据。 然后,它打印出用户上线的消息,并更新在线用户数量(OnlineCount): 如果 OnlineCount 还不存在,说明这是第一个用户登录,将在线人数设置为1; 否则,将在线人数加1。 最后,将更新后的在线人数存储在 ServletContext 中。 attributeRemoved 方法: 当从 HttpSession 中移除属性时,attributeRemoved 方法被调用。 同样地,它检查被移除的属性是否为 "username"。 如果是 "username" 属性被移除,它会打印用户下线的消息,并更新在线用户数量: 如果在线人数为0,保持为0; 否则,将在线人数减1。 最后,更新后的在线人数存储在 ServletContext 中。 核心代码如图3-11所示。 图3-11 OnlineCountListener核心代码 (3)StudentServlet编写 定义了一个 StudentServlet 类,继承自 HttpServlet 类。 在类中创建了一个 StudentDAO 的实例,用于处理与学生相关的数据访问操作。 重写了 doGet 方法,该方法用于处理HTTP GET请求。在该方法中: 设置请求和响应的字符编码为UTF-8。 从请求参数中获取 specialized、grade 和 lesson 的值。 调用 StudentDAO 的方法查询符合条件的学生信息。 将查询结果转换为JSON格式的字符串。 将JSON字符串作为响应返回给客户端。 重写了 doPost 方法,该方法直接调用了 doGet 方法,实现了HTTP POST请求的处理。 核心代码如图3-12所示。 图3-12 StudentServlet核心代码 (4)UserServlet编写 doGet 方法: 接收 HTTP GET 请求,用于用户提交登录表单。 首先设置请求的字符编码为 UTF-8,以确保正确处理中文字符。 从请求参数中获取用户名和密码。 创建一个 User 对象,将获取到的用户名和密码封装到该对象中。 调用 UserDAO 类的 queryUserByUsernameAndPassword 方法,查询数据库中是否存在该用户。 如果存在该用户(queryUserByUsernameAndPassword 返回 true): 将用户名存储到会话(Session)中,以便在用户的整个会话期间保持登录状态。 将请求重定向(redirect)到菜单页面(/views/manage/menu.jsp)。 如果不存在该用户(queryUserByUsernameAndPassword 返回 false): 在请求属性中设置错误消息(errormsg),提示用户账号或密码错误。 将用户名存储到请求属性中,以便在重新加载登录页面时填充用户名字段。 将请求转发(forward)到登录页面(/views/login.jsp)。 doPost 方法: 重写了 doPost 方法,使其调用 doGet 方法,以便处理 POST 请求。这是为了在表单提交时统一处理逻辑,无论是 GET 还是 POST 请求。 核心代码如图3-13所示。 图3-13 UserServlet核心代码 (5)UserSignOutServlet编写 @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("UserSignOutServlet被调用"); 这是一个被覆盖的 doGet 方法,用于处理 HTTP GET 请求。它首先打印一条日志,表明该 Servlet 被调用了。 request.getSession().removeAttribute("username"); 这一行代码从当前会话中移除了名为 "username" 的属性。通常情况下,"username" 是在用户登录时存储在会话中的,所以移除它相当于让用户从会话中注销。 request.getRequestDispatcher("/views/signout.html").forward(request,response); 这一行代码将请求转发到 "/views/signout.html" 页面,该页面通常用于展示用户已经成功注销的消息或者其他相关信息。 接下来的 doPost 方法调用了 doGet 方法,这是为了确保无论是 POST 请求还是 GET 请求,都能执行相同的注销逻辑。 核心代码如图3-14所示。 图3-14 UserSignOutServlet核心代码 7.jsp页面编写 (1)login.jsp编写 <title>登录页面</title>:设置页面的标题为"登录页面"。 <base href="<%=request.getContextPath() + "/"%>">:这是一个基准标签,用于设置页面中相对链接的基础路径。<%=request.getContextPath() %>用于获取当前web应用的上下文路径,然后在末尾添加斜杠,以确保相对路径的正确性。 .input1{width: 200px;}:定义了一个类名为input1的样式,设置其宽度为200像素。 <form action="userServlet" method="post">:一个表单元素,用于提交用户登录信息到名为"userServlet"的Servlet,并使用POST方法提交。 <h1>登录</h1>:页面标题,显示为"登录"。 用户名: <input type="text" class="input1" name="username" value="${requestScope.username}">:输入用户名的文本框,使用了类名为input1的样式,name属性设置为"username",并设置默认值为${requestScope.username},${}是EL(Expression Language)表达式,用于在JSP中动态地获取和展示数据。 密码: <input type="password" class="input1" name="password">:输入密码的文本框,类似地,但输入类型为密码,用户输入的字符会被隐藏。 <input type="submit" value="登录">:提交按钮,用户点击后将提交表单数据。 <span>${requestScope.errormsg}</span>:用于显示错误消息的span标签,内容为${requestScope.errormsg},即在Servlet中设置的错误消息。 核心代码如图3-15所示。 图3-15 login.jsp核心代码 (2)menu.jsp编写 基本结构: contentType="text/html;charset=UTF-8":指定页面内容类型和字符编码。 <base href="<%=request.getContextPath() + "/"%>">:设置页面中相对路径的基础路径为当前请求的上下文路径。 样式和脚本引入: 引入了jQuery库:<script type="text/javascript" src="script/jquery-3.6.0.min.js"></script>。 定义了一些样式,其中 .option1 类被用于调整下拉菜单的宽度。 JavaScript部分: 使用jQuery库,确保在文档加载完成后执行代码。 给按钮 #btn1 绑定了点击事件处理函数,当按钮被点击时,发送AJAX请求到 studentServlet。 AJAX请求中包括了三个下拉菜单中当前选择的值:specialized, grade, lesson。 请求成功时,通过循环遍历返回的数据,在表格中动态生成学生信息。 HTML部分: 一个显示当前在线人数的 <div> 元素,使用了EL表达式 ${applicationScope.OnlineCount} 来获取应用程序范围内的在线人数。 一个表单,其中包含三个下拉菜单和两个按钮: 下拉菜单 #specialized, #grade, #lesson 分别用于选择专业、年级和班级。 一个按钮 #btn1 用于触发AJAX请求,执行查询操作。 另一个按钮 #btn2 用于提交退出登录的表单。 一个表格用于显示学生信息,其中包含表头和一个空的 <tbody> 元素,该元素将在AJAX请求成功后动态填充学生信息。 核心代码如图3-16所示。 图3-16 menu.jsp核心代码 8.效果实现 (1)启动tomcat服务器,进入页面一。如图3-17所示。 图3-17 主页面 (2)当输入错误的用户名或者密码时,提示错误信息。如图3-18所示。 图3-18 登录错误提示 (3)当输入正确的用户名和密码后,进行页面跳转。如图3-19所示。 图3-19 跳转页面 (4)在下拉框筛选信息后,点击查询,即可获得相应查询结果。如图3-20所示。 图3-20 查询结果 (5)点击退出登录按钮,即可跳转到退出页面。如图3-21所示。 图3-21 退出页面 |