当前位置: 首页 > news >正文

河北省科技政策查询系统2

本系统面向河北省科技政策的浏览与管理需求,主要服务于两类用户:
普通用户/访客:可在首页查看热门政策,通过关键字、类型、年份、发布机关等多维度组合查询,并查看政策详情。
管理员:在登录之后,可对政策数据进行新增、编辑、删除等维护操作。
1.2 架构设计
系统采用经典的 MVC 三层架构,职责清晰、层次分明:
① 表现层(View)
JSP 页面(index.jsp、list.jsp、detail.jsp、login.jsp、admin.jsp、edit.jsp)
② 控制层(Controller)
Servlet(IndexServlet、PolicyListServlet、PolicyDetailServlet、LoginServlet、AdminServlet、PolicyEditServlet、PolicyDeleteServlet、LogoutServlet、EncodingFilter)
③ 业务/数据访问层(Model/DAO)
PolicyDao、UserDao + Entity(Policy、User)+ 工具类 DBUtil
1.3 模块划分与核心流程
模块 对应文件 功能说明
首页展示 IndexServlet + index.jsp 加载热门政策、类型/年份统计,提供搜索入口
政策列表与检索 PolicyListServlet + list.jsp 支持关键字 + 类型 + 年份 + 机关的分页查询
政策详情 PolicyDetailServlet + detail.jsp 展示单条政策的全部字段
管理员登录 LoginServlet + login.jsp 账号密码验证,Session 保存登录状态
后台管理 AdminServlet + admin.jsp 管理员进入列表管理界面
新增 / 编辑 PolicyEditServlet + edit.jsp 处理政策的新增与编辑提交
删除 PolicyDeleteServlet 按 ID 删除指定政策
编码过滤器 EncodingFilter 统一请求/响应字符集,防止中文乱码
数据库工具 DBUtil 封装 JDBC 连接与资源释放逻辑
1.4 数据库设计
系统以核心表 policy 存储政策数据,表字段涵盖政策名称、类型、分类、范围、文档号、发布机关、通过/发布/实施日期、领域、主题、关键词、上级政策、前后续政策、状态、正文、PDF 链接、冗余度、层级、政策关键词、新层级、年份、新关键词、二级主题以及总和等丰富信息。同时创建 user 表存储管理员账号信息。
1.5 关键设计要点
动态条件构造:在 PolicyDao 中通过 StringBuilder 动态拼接 WHERE 子句,实现可选多字段组合查询,避免 SQL 硬编码冗余。
PreparedStatement 参数化:所有 SQL 均采用占位符,既防止 SQL 注入,又提高批量执行效率。
分页查询:利用 MySQL 的 LIMIT start, size 实现列表分页,并在 Servlet 中计算 totalPage 供 JSP 渲染页码导航。
统一编码处理:通过 EncodingFilter 对所有请求与响应设置 UTF-8,解决中文参数传递乱码。
Session 鉴权:通过 Session 中的 user 对象识别管理员身份,避免越权访问后台。
二、源程序代码
2.1 实体类 —— Policy.java
src/entity/Policy.java
package entity;

import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Policy implements Serializable {
private Long id;
private String name;
private String type;
private String category;
private String range;
private String document;
private String form;
private String organ;
private Date viadata;
private Date pubdata;
private Date perdata;
private String field;
private String theme;
private String keyword;
private String superior;
private String precursor;
private String succeed;
private String state;
private String text;
private String pdf;
private String redundancy;
private String rank;
private String policykey;
private String newrank;
private String year;
private String newkey;
private String secondtheme;
private Integer allsum;

public Policy() {}

// getter / setter 省略(完整见项目源文件)...

public String formatDate(Date d) {
if (d == null) return "";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.format(d);
}
}
2.2 实体类 —— User.java
src/entity/User.java
package entity;

import java.io.Serializable;

public class User implements Serializable {
private Integer id;
private String username;
private String password;
private String role;

public User() {}

public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public String getRole() { return role; }
public void setRole(String role) { this.role = role; }
}
2.3 数据库工具类 —— DBUtil.java
src/util/DBUtil.java
package util;

import java.sql.*;

public class DBUtil {
private static final String DRIVER = "com.mysql.cj.jdbc.Driver";
private static final String URL = "jdbc:mysql://localhost:3306/febs?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false";
private static final String USER = "root";
private static final String PASSWORD = "D123";

static {
try {
Class.forName(DRIVER);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}

public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(URL, USER, PASSWORD);
}

public static void close(ResultSet rs, Statement stmt, Connection conn) {
try { if (rs != null) rs.close(); } catch (SQLException e) { e.printStackTrace(); }
try { if (stmt != null) stmt.close(); } catch (SQLException e) { e.printStackTrace(); }
try { if (conn != null) conn.close(); } catch (SQLException e) { e.printStackTrace(); }
}

public static void close(PreparedStatement pstmt, Connection conn) {
close(null, pstmt, conn);
}
}
2.4 数据访问层 —— PolicyDao.java(核心查询方法)
src/dao/PolicyDao.java
package dao;

import entity.Policy;
import util.DBUtil;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class PolicyDao {

public int countByConditions(String keyword, String type, String year, String organ) throws SQLException {
StringBuilder sql = new StringBuilder("SELECT COUNT(*) FROM policy WHERE 1=1");
List params = new ArrayList<>();
buildCondition(sql, params, keyword, type, year, organ);
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = DBUtil.getConnection();
pstmt = conn.prepareStatement(sql.toString());
setParams(pstmt, params);
rs = pstmt.executeQuery();
if (rs.next()) return rs.getInt(1);
} finally {
DBUtil.close(rs, pstmt, conn);
}
return 0;
}

public List findByConditions(String keyword, String type, String year, String organ, int pageNo, int pageSize) throws SQLException {
StringBuilder sql = new StringBuilder("SELECT * FROM policy WHERE 1=1");
List params = new ArrayList<>();
buildCondition(sql, params, keyword, type, year, organ);
sql.append(" ORDER BY id DESC LIMIT ?,?");
params.add((pageNo - 1) * pageSize);
params.add(pageSize);
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
List list = new ArrayList<>();
try {
conn = DBUtil.getConnection();
pstmt = conn.prepareStatement(sql.toString());
setParams(pstmt, params);
rs = pstmt.executeQuery();
while (rs.next()) list.add(mapRow(rs));
} finally {
DBUtil.close(rs, pstmt, conn);
}
return list;
}

public Policy findById(Long id) throws SQLException {
String sql = "SELECT * FROM policy WHERE id=?";
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = DBUtil.getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setLong(1, id);
rs = pstmt.executeQuery();
if (rs.next()) return mapRow(rs);
} finally {
DBUtil.close(rs, pstmt, conn);
}
return null;
}

// ======== 新增 / 更新 / 删除 方法(完整见源文件)========
public boolean save(Policy policy) throws SQLException { ... }
public boolean update(Policy policy) throws SQLException { ... }
public boolean delete(Long id) throws SQLException { ... }

// ======== 工具方法 ========
private void buildCondition(StringBuilder sql, List params,
String keyword, String type, String year, String organ) {
if (keyword != null && !keyword.trim().isEmpty()) {
sql.append(" AND (name LIKE ? OR keyword LIKE ? OR policykey LIKE ?)");
String kw = "%" + keyword.trim() + "%";
params.add(kw); params.add(kw); params.add(kw);
}
if (type != null && !type.trim().isEmpty()) {
sql.append(" AND type=?");
params.add(type);
}
if (year != null && !year.trim().isEmpty()) {
sql.append(" AND year=?");
params.add(year);
}
if (organ != null && !organ.trim().isEmpty()) {
sql.append(" AND organ LIKE ?");
params.add("%" + organ.trim() + "%");
}
}

private void setParams(PreparedStatement pstmt, List params) throws SQLException {
for (int i = 0; i < params.size(); i++) {
Object p = params.get(i);
if (p instanceof Integer) {
pstmt.setInt(i + 1, (Integer) p);
} else {
pstmt.setString(i + 1, String.valueOf(p));
}
}
}

private Policy mapRow(ResultSet rs) throws SQLException {
Policy p = new Policy();
p.setId(rs.getLong("id"));
p.setName(rs.getString("name"));
p.setType(rs.getString("type"));
p.setCategory(rs.getString("category"));
// ... 其余字段映射完整见源文件 ...
return p;
}
}

2.5 控制层 —— PolicyListServlet.java

src/servlet/PolicyListServlet.java

package servlet;

import dao.PolicyDao;
import entity.Policy;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;

@WebServlet("/policyList")
public class PolicyListServlet extends HttpServlet {

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
process(req, resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
process(req, resp);
}

private void process(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");

String keyword = req.getParameter("keyword");
String type = req.getParameter("type");
String year = req.getParameter("year");
String organ = req.getParameter("organ");

int pageNo = 1;
int pageSize = 10;
try {
String p = req.getParameter("pageNo");
if (p != null && !p.trim().isEmpty()) pageNo = Integer.parseInt(p.trim());
} catch (NumberFormatException e) {
pageNo = 1;
}
if (pageNo < 1) pageNo = 1;

PolicyDao dao = new PolicyDao();
try {
int totalCount = dao.countByConditions(keyword, type, year, organ);
int totalPage = (totalCount + pageSize - 1) / pageSize;
if (pageNo > totalPage && totalPage > 0) pageNo = totalPage;

List policies = dao.findByConditions(keyword, type, year, organ, pageNo, pageSize);
req.setAttribute("policies", policies);
req.setAttribute("keyword", keyword);
req.setAttribute("type", type);
req.setAttribute("year", year);
req.setAttribute("organ", organ);
req.setAttribute("pageNo", pageNo);
req.setAttribute("totalCount", totalCount);
req.setAttribute("totalPage", totalPage);
} catch (SQLException e) {
e.printStackTrace();
req.setAttribute("error", "查询失败:" + e.getMessage());
}
req.getRequestDispatcher("/list.jsp").forward(req, resp);
}
}

2.6 登录控制 —— LoginServlet.java

src/servlet/LoginServlet.java

package servlet;

import dao.UserDao;
import entity.User;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.SQLException;

@WebServlet("/login")
public class LoginServlet extends HttpServlet {

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
if (req.getSession().getAttribute("user") != null) {
resp.sendRedirect(req.getContextPath() + "/admin");
return;
}
req.getRequestDispatcher("/login.jsp").forward(req, resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
String username = req.getParameter("username");
String password = req.getParameter("password");
if (username == null || password == null || username.trim().isEmpty() || password.trim().isEmpty()) {
req.setAttribute("error", "请输入用户名和密码");
req.getRequestDispatcher("/login.jsp").forward(req, resp);
return;
}
UserDao dao = new UserDao();
try {
User user = dao.login(username.trim(), password.trim());
if (user != null) {
req.getSession().setAttribute("user", user);
resp.sendRedirect(req.getContextPath() + "/admin");
} else {
req.setAttribute("error", "用户名或密码错误");
req.getRequestDispatcher("/login.jsp").forward(req, resp);
}
} catch (SQLException e) {
e.printStackTrace();
req.setAttribute("error", "登录失败:" + e.getMessage());
req.getRequestDispatcher("/login.jsp").forward(req, resp);
}
}
}

2.7 编码过滤器 —— EncodingFilter.java

src/servlet/EncodingFilter.java

package servlet;

import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebFilter("/*")
public class EncodingFilter implements Filter {

@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
chain.doFilter(request, response);
}
}

2.8 前端 JSP 示意(list.jsp 列表页)

web/list.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

政策列表 - 河北省科技政策查询系统

政策查询

关键字:类型:年份:



<c:forEach var="p" items="${policies}">








</c:forEach>
ID政策名称类型年份发布机关操作
${p.id} ${p.name} ${p.type} ${p.year} ${p.organ} 查看详情

共 ${totalCount} 条,当前第 ${pageNo} / ${totalPage} 页

项目亮点

① 清晰的 MVC 三层架构:View(JSP)、Controller(Servlet)、Model(DAO+Entity)职责隔离,易维护、易扩展。

② 灵活的多条件组合查询:通过 StringBuilder 动态拼接 SQL,加上 PreparedStatement 参数化查询,既避免了 SQL 注入,又让代码逻辑清晰。

③ 完善的分页与导航设计:在 DAO 层同时提供 countByConditions 与 findByConditions 方法,Servlet 计算 totalPage,JSP 层直接渲染分页链接。

④ 全站 UTF-8 编码处理:通过 EncodingFilter 统一拦截设置字符集,从源头避免中文乱码问题。

⑤ 基于 Session 的登录机制:登录成功后将 user 对象存入 Session,后台管理页以此鉴权,支持退出登录。

4.2 遇到的问题与解决方法

问题 1:MySQL 8.x 的驱动与时区问题

解决:使用
com.mysql.cj.jdbc.Driver(旧版为 com.mysql.jdbc.Driver),并在 URL 上加入 serverTimezone=Asia/Shanghai 参数,避免 Invalid Connection 属性异常。

问题 2:JSP 提交表单中文参数乱码

解决:在每个 Servlet 中显式调用
request.setCharacterEncoding("UTF-8"),并引入 EncodingFilter 统一拦截,同时确保 JSP 页面与 HTML meta 均声明为 UTF-8。

问题 3:LIMIT 子句参数不能直接拼接

解决:将 LIMIT 的起始位置与条数也作为 PreparedStatement 的
? 占位符参数传入,避免字符串拼接导致 SQL 注入风险。

问题 4:日期字段的存储与展示

解决:数据库使用 DATE 类型,Java 实体使用 java.util.Date;在插入时通过
new java.sql.Date(date.getTime()) 转换,展示时在 Policy 内封装 formatDate(Date) 方法统一输出 yyyy-MM-dd。

问题 5:Servlet 3.0 注解与 web.xml 的共存

解决:项目采用
@WebServlet 与 @WebFilter 注解方式注册,减少 web.xml 配置冗余;Tomcat 部署时需确保 metadata-complete 未设为 true,以支持注解扫描。

4.3 不足与改进方向

• 密码明文存储:当前 user 表的 password 字段直接存储明文,生产环境应改为 BCrypt / MD5 加盐存储。

• 缺少前台 / 后台权限细化:目前仅简单判断 Session 是否有 user,未来可加入 role 字段区分普通用户与管理员,并在 Filter 中进行权限拦截。

• JDBC 直接连接,未使用连接池:后续可引入 Druid / HikariCP 连接池以提高数据库访问性能。

• 前端样式较基础:可引入 Bootstrap 框架,或使用 Vue + ElementUI 改造为前后端分离架构。

• 缺少日志框架:可引入 SLF4J + Logback,替换现有 e.printStackTrace() 的粗放异常处理。

• 缺少前端参数校验:可以在 JSP 页面加入 JavaScript 非空校验,减轻服务端压力并提升用户体验。

4.4 个人收获

本项目是一次较完整的 Java Web 实战练习。通过从需求分析 → 架构设计 → 数据库建模 → 代码实现 → 部署运行的全流程实践,
不仅巩固了
Servlet、JSP、JDBC、JSTL 等核心知识点,也加深了对 MVC 架构思想、SQL 参数化查询、分页算法、
编码规范、异常处理
等工程实践的理解。特别是在多条件查询与 UTF-8 编码问题上,经历了"出现问题 → 定位原因 → 设计修复 → 验证回归"
的完整闭环,这为后续更复杂的项目开发打下了坚实基础。

http://www.gsyq.cn/news/1531175.html

相关文章:

  • R3nzSkin终极指南:如何5分钟实现英雄联盟安全换肤
  • MPC8533E eTSEC硬件级网络监控与MAC过滤实战详解
  • 深度解析:macOS设备驱动开发与内核扩展实战指南
  • 2026洋浦贸易公司财税托管指南,退税申报全盘做账老牌合规代账服务商推荐 - 信息热点
  • 从AI新手到专家:如何通过awesome-gpts找到最适合你的智能助手
  • 大气层整合包系统:Switch破解的终极完整解决方案与使用教程
  • 【推荐】油猴插件脚本,网盘不限速+文库免费下载
  • 世界模型如何用薛定谔方程建模不确定性
  • 自动驾驶出租车(RoboTaxi)深度解析:技术、场景与未来之战
  • 5个创作灵感:用AI背景移除技术彻底改变你的视频表达方式
  • 【新手教程】 OpenClaw 2.7.9 一键部署 Windows 自动化 AI 搭建(包含安装包)
  • 别再只查错误码了!用Python+asyncua库模拟OPC UA服务器,主动触发并理解10个关键故障
  • 20260615 - 简单树上问题(直径重心dfn) 总结
  • 自动驾驶货运网络:重塑物流的“钢铁驼队”
  • 2026最新自习室回本周期 3个关键因素直接影响你回本快慢
  • JavaMail连接163邮箱报错‘Unsafe Login‘?手把手教你配置IMAP ID信息搞定它
  • 告别Office订阅烦恼:Ohook让你永久解锁完整功能的3个步骤
  • 光伏支架紧固件抗风防腐选型分析_2026 上海紧固件展
  • 除了清北,中科院自动化所还偏爱哪些学校的保研生?一份近三年的生源地图
  • 际连集团:印尼公司注册代办一站式服务
  • VRCT:打破VRChat语言壁垒的实时翻译与语音转文字解决方案
  • 避坑指南:Qt C++项目成功集成Python后,如何解决‘slots冲突’和打包发布的路径问题?
  • 2026呼和浩特卫生间免砸砖防水、楼顶漏水、外墙渗水、地下室阳光房渗漏;专业防水公司为您排忧解难,线上质保,售后无忧。房屋漏水不再愁,24小时一站式快速维修。 - 企业资讯
  • 2026广州常年法律顾问律所TOP4横向测评|湾区企业商事法务托管全解:常态化法律咨询、合同纠纷前置化解、企业合规治理、劳资风险防控、商事架构优化、专项法务驻场、长期法律顾问合作 - 信息热点
  • 保姆级教程:用Spark 3.4.1 + Kafka 3.0.0实现实时WordCount(Direct方式避坑指南)
  • 告别语言障碍:MouseTooltipTranslator鼠标悬停翻译工具完全指南
  • 面向学生的多款英语单词学习软件实测运行结果有哪些差异?
  • 无锡绿鸽环保正规吗?资质案例与服务流程全维度拆解 - 信息热点
  • ESP32-S3-WROOM-1U-N16:大容量Flash加持,这款外置天线模组专为复杂固件而生
  • 抖音无水印批量下载终极指南:3分钟快速上手,轻松获取纯净视频