Web开发课程实验(二):Servlet+DAO实现数据库基本交互

实验内容: 使用servlet+DAO实现基本数据库交互

具体要求
编写一个静态网页,网页命名:student.html

编写一个Servlet,命名:StudentServlet

创建hit数据库(PostgreSQL或MySQL均可),其中有两个表:student和major。student表中gender字段存储性别用m和f表示男和女,存储专业编号的mid字段对应于专业表major中的专业编号mid。

运行student.html,网页打开后以表格的方式读取到数据库中所有学生数据,其中性别要显示男或女,专业显示专业名称(mname)。

在student.html的某一个位置有一个用于新增学生信息的区域:其中性别是单选按钮,显示“男”和“女”,默认选中“男”;专业以下拉列表方式,显示major表中的专业名称(mname)信息,其中默认选中第一项,第一项内容是:-请选择专业-。录入过程中当输入学号时,数据库中如果有已存在的学号要在学号的输入框后提示:该学号已存在。

生日使用H5的input(type=date)元素,年龄使用input(type=number)即可。

提交数据前在前端要使用JS进行数据验证,提交后在后端(Servlet)也要进行数据验证,后端数据验证不合法不能将数据保存到数据库,并要返回给前端错误信息并在适当的位置显示错误信息。

数据录入成功后在提示区域显示:新增学生成功,表格中需要把新录入的数据显示出来(重新加载学生数据)。

前端所有请求均使用AJAX(建议使用jQuery AJAX),后端使用JDBC + DAO。

程序运行过程中浏览器地址始终显示student.html,页面不能跳转或刷新。

项目中的文件命名要求:

student.html
StudentServlet.java
Student.java
StudentDao.java
StudentDaoImpl.java
Major.java
MajorDao.java
MajorDaoImpl.java
DBUtil.java

吐槽:并没有听课,踩着ddl边学边做边写blog(

前置:java(不咋会,但是可以硬干,然后就会了(((


0.整体结构 环境配置

前端是html,后端是servlet,后面那一串文件是在分层开发。比如Student.java里面定义跟student表对应的student实体类,StudentDao.java定义DAO接口,Impl给出DAO接口函数的具体实现,然后servlet脚本就可以调用DAO类。DBUtil是数据库连接。

什么是JDBC:一个用来访问数据库的java类库,拿来写DBUtil

什么是DAO:一种模式,大概意思就是封装数据库操作,方便调用。

运行servlet项目需要用到tomcat作服务器,配置方法可以看这两篇:Tomcat安装及配置Eclipse开发Servlet项目详细教程

新版eclipse创建的动态web项目结构跟老版的不太一样,注意原来的webcontent对应到src/main/webapp就行。

目录结构

先从tomcat安装路径的lib文件夹里复制servlet-api.jar,放到src/main/webapp/WEB-INF/lib下面。

下载mysql驱动jar包,放到WEB-INF下的lib文件夹中。

src/main/java下新建四个包:servlet,util,dao,entity,分别存放servlet类,数据库连接类,DAO类,实体类,然后把要求的文件先建好(在对应包下面新建Class)。


新建一个student.html放在webapp下面。


接着在web.xml里面修改一下配置。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" id="WebApp_ID" version="4.0">
  <display-name>web_2</display-name>
  <welcome-file-list>
    <welcome-file>student.html</welcome-file>  <!-- 这里是默认页面 -->
  </welcome-file-list>
<!-- servlet配置 -->
<servlet>
    <servlet-name>StudentServlet</servlet-name>
    <servlet-class>servlet.StudentServlet</servlet-class> <!-- 包名.类名 -->
</servlet>
<servlet-mapping>
    <servlet-name>StudentServlet</servlet-name>
    <url-pattern>/StudentServlet</url-pattern> <!-- 访问servlet的url -->
</servlet-mapping>
</web-app>

现在项目的目录结构长这样,可以先启动一下服务器试试(项目上右键run as->run on server),应该显示的就是student.html。

目录结构

1.数据库建立

这里笔者用的是mysql,在数据库里面建两个表,按要求来就行。

student

major

然后手动往里面加点数据用来测试。

2.html文件

页面元素:

  • 一个展示信息的表格
  • 一个提交信息的表单

前端js代码模块:

  • 查询
  • 提交
  • 校验

要提交多种请求,所以发送的时候手动加个参数action,方便后端判断。拼接字符串的时候尤其要小心。

student.html

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>学生信息</title>
	<script src="https://lib.sinaapp.com/js/jquery/2.0.2/jquery-2.0.2.min.js"></script>
	<style>
		form {
			margin-left: 10px;
			margin-top: 10px;
		}
		table,td,th {
			table-layout: fixed;
			width: 200;
		}
		#new {
			margin-left: 10px;
		}
		#err {
			color: red;
		}
		#suc {
			color: green;
		}
		#nam {
			color: red;
		}
	</style>
	<script>
		function stuquery()
		{
			$("#info").empty();
			$("#info").append("<tr><th>学号</th><th>姓名</th><th>性别</th><th>年龄</th><th>生日</th><th>专业</th></tr>");
			$.ajax({
				url: "http://localhost:8080/web_2/StudentServlet",
				type: "post",
				data: "action=qs",
				dataType: "json",
				success: (studata) => {
					var str="";
					for (var i=0; i<studata.length; i++) {
						str = "<tr><td>" + studata[i].sid + "</td><td>" + studata[i].sname + "</td><td>" + studata[i].gender + "</td><td>" + studata[i].age + "</td><td>" + studata[i].birthday + "</td><td>" + studata[i].mname + "</td>";
						$("#info").append(str);
					}
				},
				error: (msg) => {
					alert("Ajax Error: "+msg);
				}
			})
		}
		function majorquery()
		{
			$.ajax({
				url: "http://localhost:8080/web_2/StudentServlet",
				type: "post",
				data: "action=qm",
				dataType: "json",
				success: (mjdata) => {
					var str='';
					for (var i=0; i<mjdata.length; i++) {
						str = '<option value="'+ mjdata[i].mid + '">' + mjdata[i].mname + '</option>';
						$("#mj").append(str);
					}
				},
				error: (msg) => {
					alert("Ajax Error: "+msg);
				}
			})
		}
		$(document).ready(() => {
			stuquery();
			majorquery();
		});
		function datacheck()
		{
			let sid = $("#ssid").val();
			let sname = $("#ssname").val();
			$("#err").empty();
			if (sid.length > 10) {
				$("#err").append("学号不得超过10位");
				return(false);
			}
			if (sname.length > 20) {
				$("#err").append("姓名不得超过20位");
				return(false);
			}
			return(true);
		}
		function send_data() {
			var formdata = $('#new form').serialize();
			formdata = formdata + "&action=send";
    		var check = datacheck();
    		if (check == true) {
    			$.ajax({
    				url: "http://localhost:8080/web_2/StudentServlet",
    				type: "post",
    				data: formdata,
    				success: (ret)=>{
    					if (ret == '0') 
    					{
    						$("#err").empty();
    						$("#nam").empty();
    						$("#suc").append("提交成功");
    					    stuquery();
    					}
    					else if (ret == '1')
    					{
    						$("#suc").empty();
    						$("#err").append("校验失败:姓名不合法");
    					}
    					else if (ret == '2')
    					{
    						$("err").empty();
    						$("#suc").empty();
    						$("#nam").append("学号已存在");
    					}
    				},
    				error: (msg) => {
						alert("Ajax Error: "+msg);
					}
    			});
    		}
		}

</script>
</head>
<body>
	<table border="1" id="info">
	</table>
	<br><br>
	<div id="new">
		新增学生信息
		<form action="">
			<table>
				<tr>
					<td>学号:</td>
					<td><input type="text" name="sid" id="ssid"></td>
					<td><div id="nam"></div></td>
				</tr>
				<tr>
					<td>姓名:</td>
					<td><input type="text" name="sname" id="ssname"></td>
				</tr>
				<tr>
					<td>性别:</td>
					<td>
						<input type="radio" name="gender" value="m" checked>男 
						<input type="radio" name="gender" value="f">女
					</td>
				</tr>
				<tr>
					<td>年龄:</td>
					<td><input type="number" name="age"></td>
				</tr>
				<tr>
					<td>生日:</td>
					<td><input type="date" name="birthday"></td>
				</tr>
				<tr>
					<td>专业:</td>
					<td>
						<select name="major" id="mj">
							<option value="" disabled selected hidden>-请选择专业-</option>
						</select>
					</td>
				</tr>
			</table>
		</form>
		<button id="bb" type="button" onclick="send_data()">提交信息</button>
	</div>
	
	<div id="err"></div>
	<div id="suc"></div>
</body>
</html>

3.JDBC连接数据库 (DBUtil.java)

这个类里面提供四个基本函数作为接口:连接,增删改,查询,关闭连接。其中增删改和查询均接收一个sql字符串作为参数,查询函数需返回查询结果(ResultSet类型)。

下面的代码基本是直接copy网上的:

DBUtil.java

package util;
 
import java.sql.*;
 
public class DBUtil {
	static String url = "jdbc:mysql://localhost:3306/hit"; 
	static String username = "root"; 
	static String password = "password"; 
	static Connection  conn = null;
	static ResultSet rs = null;
	static PreparedStatement ps =null;
	public static void init(){
		try {
			Class.forName("com.mysql.jdbc.Driver");
			conn = DriverManager.getConnection(url,username,password);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public static int addUpdDel(String sql){
		int i = 0;
		try {
			PreparedStatement ps =  conn.prepareStatement(sql);
			i =  ps.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		
		return i;
	}
	public static ResultSet selectSql(String sql){
		try {
			ps =  conn.prepareStatement(sql);
			rs =  ps.executeQuery(sql);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return rs;
	}
	public static void closeConn(){
		try {
			conn.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
}

4.建立实体类(Student.java, Major.java)

实体类里面定义跟数据表各字段对应的成员变量,成员函数只需要实现简单的get和set即可。(我直接丢给chatgpt写,活用生产力工具)

Student.java

package entity;

public class Major {
	private String mid;
	private String mname;
	
	public String getMid() {
	    return mid;
	}

	public void setMid(String mid) {
	    this.mid = mid;
	}

	public String getMname() {
	    return mname;
	}

	public void setMname(String mname) {
	    this.mname = mname;
	}
}

Major.java
package entity;

public class Student {
	private String sid;
	private String sname;
	private String gender;
	private String age;
	private String birthday;
	private String mid;
	
	public String getSid() {
	    return sid;
	}

	public void setSid(String sid) {
	    this.sid = sid;
	}

	public String getSname() {
	    return sname;
	}

	public void setSname(String sname) {
	    this.sname = sname;
	}

	public String getGender() {
	    return gender;
	}

	public void setGender(String gender) {
	    this.gender = gender;
	}

	public String getAge() {
	    return age;
	}

	public void setAge(String age) {
	    this.age = age;
	}

	public String getBirthday() {
	    return birthday;
	}

	public void setBirthday(String birthday) {
	    this.birthday = birthday;
	}

	public String getMid() {
	    return mid;
	}

	public void setMid(String mid) {
	    this.mid = mid;
	}
}

5.建立DAO接口类(StudentDao.java, StudentDaoImpl.java, MajorDao.java, MajorDaoImpl.java)

根据要实现的功能写函数接口。

需要实现的功能:查询学生信息列表,查找学号是否存在,新增学生信息,查询专业信息列表,查询专业id对应的专业名。

StudentDao.java

package dao;

import java.util.List;
import entity.Student;

public interface StudentDao {
	public List<Student> getStudentInfo();
	public boolean add(String sid,String sname,String gender,String age,String birthday,String mid);
	public boolean exist(String sid);
}

StudentDaoImpl.java
package dao;

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

import entity.Student;
import util.DBUtil;

public class StudentDaoImpl implements StudentDao {
	public List<Student> getStudentInfo()
	{
		List<Student> list = new ArrayList<Student>();
		try {
			DBUtil.init();
			ResultSet rs = DBUtil.selectSql("select * from student");
			while (rs.next())
			{
				Student stu = new Student();
				stu.setSid(rs.getString("sid"));
				stu.setSname(rs.getString("sname"));
				stu.setGender(rs.getString("gender"));
				stu.setAge(rs.getString("age"));
				stu.setBirthday(rs.getString("birthday"));
				stu.setMid(rs.getString("mid"));
				list.add(stu);
			}
			DBUtil.closeConn();
			return list;
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return null;
	}
	public boolean add(String sid,String sname,String gender,String age,String birthday,String mid)
	{
		boolean flag = false;
		DBUtil.init();
		String sql = "INSERT INTO student (`sid`, `sname`, `gender`, `age`, `birthday`, `mid`) VALUES ('"
				+ sid + "','" + sname + "','" + gender + "','"
				+ age + "','" + birthday + "','" + mid + "')";
		System.out.println(sql);
		int i =DBUtil.addUpdDel(sql);
		if(i>0){
			flag = true;
		}
		DBUtil.closeConn();
		return flag;
	}
	public boolean exist(String sid)
	{
		DBUtil.init();
		String sql = "select * from student where sid='"+sid+"'";
		ResultSet rs = DBUtil.selectSql(sql);
		try {
			if (rs.next()) return true;
			else return false;
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return false;
	}
}

MajorDao.java
package dao;

import java.util.List;
import entity.Major;

public interface MajorDao {
	public List<Major> getMajorInfo();
	public String getMname(String mid);
}

MajorDaoImpl.java
package dao;

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

import entity.Major;
import util.DBUtil;

public class MajorDaoImpl implements MajorDao{
	public List<Major> getMajorInfo()
	{
		List<Major> list = new ArrayList<Major>();
		try {
			DBUtil.init();
			ResultSet rs = DBUtil.selectSql("select * from major");
			while (rs.next())
			{
				Major mj = new Major();
				mj.setMid(rs.getString("mid"));
				mj.setMname(rs.getString("mname"));
				list.add(mj);
			}
			DBUtil.closeConn();
			return list;
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return null;
	}
	public String getMname(String mid)
	{
		try {
			DBUtil.init();
			String sql="select * from major where mid='"+mid+"'";
			ResultSet rs = DBUtil.selectSql(sql);
			while (rs.next())
				return rs.getString("mname");
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return null;
	}
}

# 6.编写Servlet

前面DAO类准备完成之后就可以写Servlet脚本了。

这里实验要求是只有一个Servlet,要处理多种请求,所以在html里面请求的时候带一个action参数用来辨别。到后端要先提取判断。

servlet的一般写法是扩展HttpServlet类,重写doGet和doPost方法。

涉及到字符串和json之间的倒腾来倒腾去,引入GSON包处理json数据。(外部jar包全放在WEB-INF/lib下面)

异常数据处理:直接返回去一个数字,0代表成功,1代表姓名不合法(其实就是只判断开头是不是问号,偷懒了),2代表学号有重复,html文件有对应处理。

package servlet;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import dao.StudentDao;
import dao.StudentDaoImpl;
import dao.MajorDao;
import dao.MajorDaoImpl;
import entity.Student;
import entity.Major;

public class StudentServlet extends HttpServlet {
	public void doGet(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {
			doPost(request, response);
		}
	public void doPost(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {
		String act = request.getParameter("action");
		if (act.equals("qs"))
		{
			StudentDao stu = new StudentDaoImpl();
			MajorDao mj = new MajorDaoImpl();
			List<Student> info = stu.getStudentInfo();
			Gson gson = new Gson();
			JsonArray stulist = gson.fromJson(gson.toJson(info), JsonArray.class);
			
			JsonArray stu_ = new JsonArray();
			for (int i=0; i<stulist.size();i++)
			{
				JsonObject stuu = (JsonObject) stulist.get(i);
				String midd = stuu.get("mid").getAsString();
				stuu.addProperty("mname", mj.getMname(midd));
				System.out.println(mj.getMname(midd));
				stu_.add(stuu);
			}
			
			response.setContentType("application/json");
			response.setCharacterEncoding ("UTF-8");
			
			response.getWriter().print(stu_.toString());
		}
		if (act.equals("qm"))
		{
			MajorDao mj = new MajorDaoImpl();
			List<Major> minfo = mj.getMajorInfo();
			Gson gson = new Gson();
			response.setContentType("application/json");
			response.setCharacterEncoding ("UTF-8");
			response.getWriter().print(gson.toJson(minfo));
		}
		if (act.equals("send"))
		{
			response.setContentType("text/plain");
			response.setCharacterEncoding ("UTF-8");
			String sid = request.getParameter("sid");
			String sname = request.getParameter("sname");
			String gender = request.getParameter("gender");
			String age = request.getParameter("age");
			String birthday = request.getParameter("birthday");
			String major = request.getParameter("major");
			StudentDao stu = new StudentDaoImpl();
			if (sname.charAt(0)=='?') {
				response.getWriter().print("1");
			}
			else if (stu.exist(sid))
			{
				response.getWriter().print("2");
			}
			else {
				stu.add(sid, sname, gender, age, birthday, major);
				response.getWriter().print("0");
			}
		}
	}
}

7.一些踩过的坑

  • sql插入语句字段名要用反单引号括起来,普通单引号报错,在mysql里直接输入才发现这个。
  • 谷歌的GSON和阿里的Fastjson不太一样,在网上查资料注意辨别(比如说一个json类型名叫JsonObject,一个叫JSONObject)
  • java判断字符串相等得用equals方法,不是==(一开始不知道,chatgpt告诉我的 2333333)
  • 项目跑起来好像比较慢?刚启动服务器的时候浏览器访问html页面不一定能及时返回数据库查询结果,多刷新几下才正常,不要一上来看到报错就自己先陷入暴躁了。
  • CSS的样式冲突真的很讨厌,md,善用浏览器控制台查看问题出处。

8.结语

超长的一篇……整个项目做完其实已经超ddl了,不过考察课扣几分就扣吧。做了好几个晚上来着。

从啥玩意都不懂开始,自力更生,还是挺不错的。

一条经验:做不太明白的东西先从最基本的能跑起来的helloworld开始,然后往这个微不足道但是五脏俱全的小东西里面一点一点加功能,每走一步都先完善到能顺利跑起来的程度,这样错误就比较好排查了。一开始就想着把一部分功能尽善尽美地做完其实吃力不讨好,后面报错多的话人都麻了。

最上面的html看似是一开始写好的,其实跟servlet相纠缠改了好多遍的。

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值