# tickets
**Repository Path**: JavaObjects/tickets
## Basic Information
- **Project Name**: tickets
- **Description**: Java EE(Filter + HttpSession + servlet + JDBC连接池 + 单例模式) + Jsp 购票网
- **Primary Language**: Java
- **License**: MulanPSL-2.0
- **Default Branch**: master
- **Homepage**: https://github.com/javaobjects/tickets
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 1
- **Created**: 2019-08-29
- **Last Updated**: 2024-05-21
## Categories & Tags
**Categories**: Uncategorized
**Tags**: HttpSession, cookie, Servlet, Ajax, jsp
## README

# 购票网
#### 项目说明
+ 此项目为JavaWeb项目,后端纯Java未使用框架,前端为Jsp,实现了前后端数据通信,前后端基本的增删改查功能。
#### 开发环境
+ Windows
#### 配置环境
| 程序 | 版本 | 说明 |
|--------------|-----------|--------------------------|
| Jdk | 1.8.0 161 | Java 开发工具包 |
| Mysql | 5.5.27 | 关系型数据库 |
| 或者 Oracle | 11.2.0.1.0 | 关系型数据库 |
| Apache-Tomcat | 9.0.71 | Java 服务器 |
#### 开发工具
| 工具 | 版本 | 说明 |
|--------------------------|---------------|-------------------------|
| Eclipse IDE | 4.11.0(2022.12)| 后端开发IDE |
| Vscode IDE | 1.34.0| 前端开发IDE |
| Git | 2.24.1 | 代码托管平台 |
| Google Chrome | 75.0.3770.100 | 浏览器、前端调试工具 |
| Navicat | 12 | 数据库连接工具 |
| PL/SQL | 11.2.0.1.0 | 数据库连接工具 |
| Postman | 7.1.0 | 接口测试工具 |
| VMware Workstation Pro | 14.1.3 | 虚拟机(未用到或许你会用到) |
| PowerDesigner | 15 | 数据库设计工具(未用到或许你会用到) |
| SQLyog | 12.0.3 | 数据库连接工具 (未用到或许你会用到) |
| Visio | 2013 | 时序图、流程图等绘制工具(未用到或许你会用到) |
| ProcessOn | —— | 架构图等绘制工具(未用到或许你会用到) |
| XMind ZEN | 9.2.0 | 思维导图绘制工具(未用到或许你会用到) |
| RedisDesktop | 0.9.3.817 | redis客户端连接工具(未用到或许你会用到) |
#### 编码规范
- 规范方式:严格遵守阿里编码规约。
- 命名统一:简介最大程度上达到了见名知意。
- 分包明确:层级分明可快速定位到代码位置。
- 注释完整:描述性高大量减少了开发人员的代码阅读工作量。
- 工具规范:使用统一jar包避免出现内容冲突。
- 代码整洁:可读性、维护性高。
#### 包结构
```
+- tickets
+- Img --md文件所需的图片
+- sql --sql语句包
+- txt --部分知识点总结
+- src
+- main
| +- java
| | +- net
| | +- tencent
| | | +- tickets
| | | +- dao -- 数据访问层,与底层 MySQL 进行数据交互
| | | +- entity --实体
| | | +- filter --过滤器,控制前端页面访问
| | | +- service --dao与servlet解耦合
| | | +- servlet -- 主要是处理各种 Http 请求,各类基本参数校验,或者不复用的业务简单处理,返回 JSON 数据等
| | | | +- admin
| | | | +- comm
| | | | +- login
| | | | +- user
| | | +- util -- 业务相关工具
| +- webapp
| | +- admin -- 管理员前端页面
| | +- css -- 样式
| | +- images -- 图片
| | +- js -- 前端页面行为逻辑控制
| | +- META-INF
| | +- context.xml -- 数据库的配置
| | +- photos -- 用户上传头像客户端存储位置
| | +- user -- 用户前端界面
| | +- WEB-INF
+- lib -- 存放此项目所用的各种jar包
+- web.xml -- 配置默认显示的前端页面和过滤器的配置
```
#### 项目所导入的Jar包
1. jstl-1.2.jar (JSP标准标签库)
2. [jxl.jar](https://bbs.csdn.net/topics/90494976)(Java操作Excel或创建Excel)
3. ojdbc6.jar Oracle数据驱动
3. mysql-connector-java-5.1.39-bin.jar mysql数据驱动
3. Java生成Json传输给前端的接口驱动
```
commons-beanutils-1.7.0.jar
commons-collections-3.1.jar
commons-lang-2.5.jar
commons-logging-1.1.1.jar
ezmorph-1.0.3.jar
json-lib-2.1-jdk15.jar
```
6. 上传照片功能需要的jar包
```
commons-fileupload-1.3.1.jar
commons-io-2.4.jar
```
#### 搭建运行本项目步骤
1. 确保已配置好jdk1.8的环境且与Eclipse开发工具相匹配
2. 确保已安装好Tomcat配置环境且和Eclipse开发工具配置完成
3. 导入了以应的jar包
```
右键项目 -->Build Path --> Configure Build Path --> Java Build Path
--> Libraries(Class path) --> Add JARs.. --> 选择要导入的包可多选 --> Apply and close
```

4. 将tickets_all.sql导入自己的navicat执行并生成对应的mysql数据库数据 前提是要配置好自己的mysql数据库跟navicat可视化工具
5. 配置自己mysql数据库在META-INF下的context.xml
```xml
```
6. 配置默认显示页面以及拦截器 WEB-INF下的web.xml 当然拦截器需要自己完成对应的功能,这里只是配置
```xml
tickets
f1
net.tencent.tickets.filter.AccessFilter
f1
/*
index.html
login.jsp
index.htm
default.html
default.jsp
default.htm
```
7. 在Eclipse里导入此项目-->右键此项目-->Run As-->Run On Server-->在弹出的对话框选择自己的Tomcat-->Next-->Finish-->启动成功后你的默认浏览器会自动弹出一个名为 http://localhost:8080/tickets/ 的页面表明启动成功




8. txt文件夹有部分知识点总结
#### 项目所用技术点
1. 访问控制过滤器Filter的使用
2. HttpSession的使用
3. servlet传递数据至Jsp
4. JDBC连接池的使用
5. Jsp EL表达式
6. Jsp核心标签库的运用
7. ajax输出xml或Json使用
8. Md5加密技术
9. 图形验证码技术
10. Cookie的使用
11. 图片上传
12. 服务端表格分页
13. 导出一个xls Excel表格
14. 单例模式的使用
```
// 单例模式实现步骤:
// 1.构造器私有
// 2.提供私有的静态的当前类类型的变量
// 3.提供一个公共的静态方法,返回刚才定义的变量,如果这个变量为null,那么给他赋值
```
+ 核心代码示例代码 Service
```Java
package net.tencent.tickets.service;
import java.util.List;
import net.tencent.tickets.dao.CityDao;
import net.tencent.tickets.entity.City;
public class CityService {
//属性依赖cityDao
private CityDao cityDao = CityDao.getInstance();
/**
*
* Title: getCityByProvinceNum
*
* Description:
* 获取指定省份的城市信息的业务方法
*
* @param provinceNum
* @return
*/
public List getCityByProvinceNum(String provinceNum) {
return cityDao.queryCityByProvinceNum(provinceNum);
}
/**
* Title: queryCityByCityNum
*
* Description:
*
* Copyright: Copyright (c) 2017
* Company: www.baidudu.com
* @param cityNum
* @return
* @author xianxian
* @date 2023年3月2日下午6:08:03
* @version 1.0
*/
public City queryCityByCityNum(String cityNum) {
return cityDao.queryCityByCityNum(cityNum);
}
private CityService(){}
private static CityService cityService;
public static CityService getInstance() {
if (cityService == null) {
cityService = new CityService();
}
return cityService;
}
}
```
+ dao
```Java
package net.tencent.tickets.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import net.tencent.tickets.entity.City;
import net.tencent.tickets.entity.Province;
import net.tencent.tickets.service.ProvinceService;
import net.tencent.tickets.servlet.other.ProvinceServlet;
import net.tencent.tickets.util.DBUtils_pool;
public class CityDao {
/** 根据省份编号查询所有城市的sql语句 **/
private static final String QUERY_CITY_BY_PROVINCENUM =
"SELECT CITY_ID,CITY_NUM,CITY_NAME,CITY_FATHER from tickets_city where CITY_FATHER=?";
/** 根据城市编号查询城市所有 **/
private static final String QUERY_CITY_BY_CITYNUM =
"SELECT CITY_ID,CITY_NUM,CITY_NAME,CITY_FATHER from tickets_city where CITY_NUM=?";
/**
* Title: queryCityByCityNum
*
* Description:
*
* Copyright: Copyright (c) 2017
* Company: www.baidudu.com
* @param cityNum
* @return
* @author xianxian
* @date 2023年3月2日下午6:08:47
* @version 1.0
*/
public City queryCityByCityNum(String cityNum) {
City city = new City();
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
conn = DBUtils_pool.getConnection();
stmt = conn.prepareStatement(QUERY_CITY_BY_CITYNUM);
stmt.setString(1, cityNum);
rs = stmt.executeQuery();
while (rs.next()) {
city.setCityNum(rs.getString("CITY_NUM"));
city.setId(rs.getInt("CITY_ID"));
city.setCityName(rs.getString("CITY_NAME"));
ProvinceService provinceService = ProvinceService.getInstance();
Province province = provinceService.queryProvinceByProvinceNum(rs.getString("CITY_FATHER"));
city.setProvince(province);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
DBUtils_pool.release(conn, stmt, rs);
}
return city;
}
/**
* (non-Javadoc)
* Title: queryCityByProvinceNum
*
* Description:获取指定省份的所有城市信息
*
* Copyright: Copyright (c) 2017
* Company: www.baidudu.com
* @param provinceNum
* @return
* @see net.tencent.tickets.dao.ifac.CityDaoIfac#queryCityByProvinceNum(java.lang.String)
* @author xianxian
* @date 2023年2月25日下午9:10:45
* @version 1.0
*/
public List queryCityByProvinceNum(String provinceNum) {
List cities = new ArrayList<>();
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
conn = DBUtils_pool.getConnection();
stmt = conn.prepareStatement(QUERY_CITY_BY_PROVINCENUM);
stmt.setString(1, provinceNum);
rs = stmt.executeQuery();
while (rs.next()) {
City c = new City();
c.setCityNum(rs.getString("CITY_NUM"));
c.setId(rs.getInt("CITY_ID"));
c.setCityName(rs.getString("CITY_NAME"));
c.setProvince(new Province(null,rs.getString("CITY_FATHER"),null));
cities.add(c);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
DBUtils_pool.release(conn, stmt, rs);
}
return cities;
}
// 单例模式实现步骤:
// 1.构造器私有
// 2.提供私有的静态的当前类类型的变量
// 3.提供一个公共的静态方法,返回刚才定义的变量,如果这个变量为null,那么给他赋值
private CityDao() {};
private static CityDao cityDao;
public static CityDao getInstance() {
if(cityDao == null) {
cityDao = new CityDao();
}
return cityDao;
}
}
```
+ 调用
```Java
CityService cityService = CityService.getInstance();
City city = cityService.queryCityByCityNum(cityNum);
```
#### 本项目的思维导向图草图

#### 总体架构






#### 数据库设计
##### tickets_user
|列名|数据类型|可否为空|说明|
| -- | -- | -- | -- |
| USER_ID | number(11) | not null | id(Parimary主键) |
| USER_NAME | varchar2(30) | not null | 用户名(Unique唯一) |
| USER_PASSWORD | varchar2(50) | not null | 密码 |
| USER_RULE | varchar2(2) | not null | 权限(1 管理员 2 普通用户) |
| USER_REAL_NAME | varchar2(50) | not null | 真实姓名 |
| USER_SEX | char(1) | not null | 性别(1 男 2 女) |
| USER_CITY_ID | number(11) | not null | 城市信息id值(Foreign外键tickets_city)FK_TICKETS_CITY_ID |
| USER_CERTTYPE_ID | number(11) | not null | 证件类型(1二代身份证2港澳通行证3台湾通行证4护照)(Foreign外键tickets_certtype)FK_TICKETS_CERTTYPE_ID |
| USER_CERT | varchar2(50) | not null | 证件号码 |
| USER_BIRTHDAY | date | not null | 生日 |
| USER_USERTYPE_ID | number(11) | not null | 旅客类型(1成人2儿童3学生4残疾军人、伤残人民警察)((Foreign外键tickets_usertype))FK_TICKETS_USERTYPE_ID |
| USER_CONTENT | varchar2(3000) | null | 备注信息 |
| USER_STATUS | char(1) | not null | 用户状态(0 无效 1 有效) |
| USER_LOGIN_IP | varchar2(50) | not null | 登陆IP |
| USER_IMAGE_PATH | varchar2(200) | not null | 用户头像路径 |
##### tickets_usertype
|列名|数据类型|可否为空|说明|
| -- | -- | -- | -- |
| USERTYPE_ID | number(11) | not null | id (主键) |
| USERTYPE_CONTENT | varchar2(40) | not null | 旅客类型(1成人2儿童3学生4残疾军人、伤残人民警察) |
##### tickets_province
|列名|数据类型|可否为空|说明|
| -- | -- | -- | -- |
| PROVINCE_ID | number(11) | not null | id (主键) |
| PROVINCE_NUM | varchar2(50) | not null | 省份编号 |
| PROVINCE_NAME | varchar2(40) | not null | 省份名称 |
##### tickets_city
|列名|数据类型|可否为空|说明|
| -- | -- | -- | -- |
| CITY_ID | number(11) | not null | id (主键) |
| CITY_NUM | varchar2(50) | not null | 城市编号 |
| CITY_NAME | varchar2(50) | not null | 城市名称 |
| CITY_FATHER | varchar2(6) | not null | 省份标识(Foreign外键tickets_province)FK_TICKETS_PROVINCE_NUM |
##### tickets_certtype
|列名|数据类型|可否为空|说明|
| -- | -- | -- | -- |
| CERTTYPE_ID | number(11) | not null | id (主键) |
| CERTTYPE_CONTENT | varchar2(20) | not null | 证件类型(1二代身份证2港澳通行证3台湾通行证4护照)|
#### 本项目部分业务功能及其实现简介
1. 验证码功能

+ Java 利用26个英文字母以及十个数字随机组合成四位验证码
+ 且字体颜色随机
+ 干扰线随机
+ 产生的验证码存入Session,并以图片的形式传给前端显示
+ 登录验证之时服务端取session里的验证码的值进行验证
2. 自动登录功能

+ 前端将是否自动登录的标识传给Java后台
+ Java根据标识将用户名与MD5加密后的密码存入前端的cookie中
+ 前端每次登录之时判断浏览器中是否有存入cookie若有则取到用户名与密码直接发到到后台
+ Java后台根据前端传入的用户名与密码实现自动登录的功能(有cookie且用户名与密码验证成功则跳过验证码实现自动登录)
3. 管理员用户管理查询展示列表功能

+ 当前端点击查询按钮时Java后台根据前端传入的数据进行查询并返回给前端
+ 查询结果存入session
```java
PageUtil pageUtil = new PageUtil(users, Integer.parseInt(pageCount), 1);
request.setAttribute("userList", pageUtil.getUsers_page());// 把查询结果users传给userlist.jsp页面
request.setAttribute("pagesum", pageUtil.getPagesum());// 总页数
request.setAttribute("pageNumber", pageUtil.getPageNumber());// 页码
request.getRequestDispatcher("/admin/userlist.jsp").forward(request, response);
```
+ 前端根据查询结果返回的数据通过jsp页面的如下代码进行展示数据
```jsp
|
${u.userName} |
${u.userSex==49?"男":"女"} |
${u.certType.content} |
${u.userCert} |
${u.userType.content} |
编辑
|
```
4. 查询结果全选功能

+ 前端功能实现
```javascript
let objMethod = {
selectAllNullorReserve: function (order) {
let checkboxArray = $('input[type="checkbox"]');
switch (order) {
case "全选":
for (let i = 0; i < checkboxArray.length; i++) {
if (!checkboxArray.eq(i).attr("checked")) {
checkboxArray.eq(i).attr("checked", true);
}
}
break;
case "全不选":
for (let i = 0; i < checkboxArray.length; i++) {
if (checkboxArray.eq(i).attr("checked")) {
checkboxArray.eq(i).attr("checked", false);
}
}
break;
case "反选":
for (let i = 0; i < checkboxArray.length; i++) {
if (checkboxArray.eq(i).attr("checked")) {
checkboxArray.eq(i).attr("checked", false);
} else {
checkboxArray.eq(i).attr("checked", true);
}
}
break;
default:
break;
}
}
}
```
5. 分页展示功能

+ Java后端分页 由于点击查询时所得到的结果都存入了session中,只需要将页码数值传入后端根据下标来截取对应的数值给前端就行了
+ 前端展示即可
```jsp
|
```
```javascript
// 分页改变时
$("#select_pageCount").change(function () {
$("#form_queryUser").submit();
})
```
```jsp