<think>我们正在开发一个基于MVC模式的在线问卷调查系统,使用JSP作为视图层,Servlet作为控制器,JavaBean作为模型层。系统需要包含问卷创建、数据收集和结果可视化功能。下面将分步骤说明实现方法,并提供关键代码示例。###1.系统架构设计-**模型层(JavaBean)**:封装业务逻辑和数据访问,包括问卷、问题、选项、回答等实体类,以及数据库操作类。-**视图层(JSP)**:负责展示用户界面,如问卷创建表单、问卷填写页面、结果展示页面。-**控制层(Servlet)**:接收用户请求,调用模型层处理业务逻辑,并选择合适的视图展示结果。###2.数据库设计我们使用MySQL数据库,创建以下表:-`survey`:问卷表(id,title,description,create_time)-`question`:问题表(id,survey_id,content,type)//type可以是单选、多选、文本等-`option`:选项表(id,question_id,content)-`answer`:回答表(id,survey_id,question_id,option_id,content,user_id)//对于文本回答,使用content字段###3.实现步骤####3.1问卷创建功能**流程**:用户填写问卷信息(标题、描述)并添加问题(每个问题有类型和选项),提交后保存到数据库。**关键代码**:-创建问卷的Servlet(`CreateSurveyServlet`):```java@WebServlet("/createSurvey")publicclassCreateSurveyServletextendsHttpServlet{protectedvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{Stringtitle=request.getParameter("title");Stringdescription=request.getParameter("description");//创建Survey对象并保存到数据库Surveysurvey=newSurvey();survey.setTitle(title);survey.setDescription(description);survey.setCreateTime(newDate());SurveyDAOsurveyDAO=newSurveyDAO();intsurveyId=surveyDAO.saveSurvey(survey);//返回生成的问卷ID//处理问题String[]questions=request.getParameterValues("question");for(inti=0;i<questions.length;i++){StringquestionContent=questions[i];Stringtype=request.getParameter("type_"+i);Questionquestion=newQuestion();question.setSurveyId(surveyId);question.setContent(questionContent);question.setType(type);QuestionDAOquestionDAO=newQuestionDAO();intquestionId=questionDAO.saveQuestion(question);//如果是选择题,处理选项if("radio".equals(type)||"checkbox".equals(type)){String[]options=request.getParameterValues("option_"+i);for(StringoptionContent:options){Optionoption=newOption();option.setQuestionId(questionId);option.setContent(optionContent);OptionDAOoptionDAO=newOptionDAO();optionDAO.saveOption(option);}}}response.sendRedirect("surveyCreated.jsp");//创建成功页面}}```-创建问卷的JSP(`createSurvey.jsp`)使用表单动态添加问题(需要JavaScript支持动态添加问题输入框和选项输入框)。####3.2数据收集功能**流程**:用户访问问卷链接(如`fillSurvey.jsp?surveyId=1`),填写问卷并提交。**关键代码**:-展示问卷的Servlet(`FillSurveyServlet`):```java@WebServlet("/fillSurvey")publicclassFillSurveyServletextendsHttpServlet{protectedvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{intsurveyId=Integer.parseInt(request.getParameter("surveyId"));SurveyDAOsurveyDAO=newSurveyDAO();Surveysurvey=surveyDAO.getSurveyById(surveyId);List<Question>questions=newQuestionDAO().getQuestionsBySurveyId(surveyId);for(Questionq:questions){if(q.getType().equals("radio")||q.getType().equals("checkbox")){q.setOptions(newOptionDAO().getOptionsByQuestionId(q.getId()));}}request.setAttribute("survey",survey);request.setAttribute("questions",questions);request.getRequestDispatcher("fillSurvey.jsp").forward(request,response);}}```-填写问卷的JSP(`fillSurvey.jsp`)使用JSTL和EL遍历问题列表,根据问题类型生成单选、多选或文本输入框。-提交回答的Servlet(`SubmitAnswerServlet`):```java@WebServlet("/submitAnswer")publicclassSubmitAnswerServletextendsHttpServlet{protectedvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{intsurveyId=Integer.parseInt(request.getParameter("surveyId"));//遍历问题for(Questionq:newQuestionDAO().getQuestionsBySurveyId(surveyId)){if("text".equals(q.getType())){StringanswerContent=request.getParameter("answer_"+q.getId());//保存文本答案saveAnswer(surveyId,q.getId(),null,answerContent);}else{//单选或多选String[]selectedOptions=request.getParameterValues("answer_"+q.getId());if(selectedOptions!=null){for(StringoptionId:selectedOptions){saveAnswer(surveyId,q.getId(),Integer.parseInt(optionId),null);}}}}response.sendRedirect("thankYou.jsp");//感谢页面}privatevoidsaveAnswer(intsurveyId,intquestionId,IntegeroptionId,Stringcontent){Answeranswer=newAnswer();answer.setSurveyId(surveyId);answer.setQuestionId(questionId);answer.setOptionId(optionId);answer.setContent(content);newAnswerDAO().saveAnswer(answer);}}```####3.3结果可视化功能**流程**:管理员查看问卷统计结果,包括每个问题的回答情况(如单选/多选的选项分布,文本回答的列表)。**关键代码**:-查看结果的Servlet(`ViewResultServlet`):```java@WebServlet("/viewResult")publicclassViewResultServletextendsHttpServlet{protectedvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{intsurveyId=Integer.parseInt(request.getParameter("surveyId"));Surveysurvey=newSurveyDAO().getSurveyById(surveyId);List<Question>questions=newQuestionDAO().getQuestionsBySurveyId(surveyId);//为每个问题获取回答统计for(Questionq:questions){if("radio".equals(q.getType())||"checkbox".equals(q.getType())){List<Option>options=newOptionDAO().getOptionsByQuestionId(q.getId());//计算每个选项的计数for(Optionop:options){intcount=newAnswerDAO().countAnswerByOptionId(op.getId());op.setCount(count);}q.setOptions(options);}else{//文本问题,获取所有回答内容List<Answer>textAnswers=newAnswerDAO().getAnswersByQuestionId(q.getId());q.setTextAnswers(textAnswers);}}request.setAttribute("survey",survey);request.setAttribute("questions",questions);request.getRequestDispatcher("viewResult.jsp").forward(request,response);}}```-结果展示的JSP(`viewResult.jsp`)根据问题类型展示不同的结果:-对于选择题,使用柱状图(如使用Chart.js)或饼图展示选项分布。-对于文本题,直接列出所有回答。###4.性能优化建议-使用连接池(如DBCP)管理数据库连接,避免频繁创建连接[^4]。-对于热门问卷,考虑使用缓存(如Redis)存储问卷信息,减少数据库压力[^4]。###5.注意事项-使用JSTL和EL简化JSP中的Java代码,使视图层更清晰[^1]。-在Servlet中处理业务逻辑,避免在JSP中编写过多逻辑代码。以上是一个基本的实现框架,具体实现时还需要考虑异常处理、安全性(如防止SQL注入、XSS攻击)和用户体验优化。§§相关问题§§1.如何优化在线问卷调查系统的数据库查询性能?2.在JSP+Servlet+JavaBean架构中,如何处理用户会话(session)以跟踪填写问卷的用户?3.如何实现问卷的权限控制(如只有登录用户才能创建问卷)?4.在结果可视化中,如何使用JavaScript图表库(如Chart.js)绘制统计图表?</think>以下是一个基于JSP+JavaBean+Servlet实现MVC模式在线问卷调查系统的完整方案,包含问卷创建、数据收集和结果可视化功能:
### 系统架构设计(MVC模式)
```mermaid
graph TD
A[视图层 JSP] -->|提交请求| B[控制器 Servlet]
B -->|调用| C[模型层 JavaBean]
C -->|数据操作| D[MySQL数据库]
D -->|返回数据| C
C -->|处理结果| B
B -->|转发结果| A
```
### 一、数据库设计(MySQL)
```sql
-- 问卷表
CREATE TABLE survey (
id INT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(100) NOT NULL,
description TEXT,
creator_id INT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 问题表
CREATE TABLE question (
id INT PRIMARY KEY AUTO_INCREMENT,
survey_id INT,
content VARCHAR(200) NOT NULL,
type ENUM('单选','多选','文本') NOT NULL,
FOREIGN KEY (survey_id) REFERENCES survey(id)
);
-- 选项表
CREATE TABLE option (
id INT PRIMARY KEY AUTO_INCREMENT,
question_id INT,
content VARCHAR(100) NOT NULL,
FOREIGN KEY (question_id) REFERENCES question(id)
);
-- 回答表
CREATE TABLE answer (
id INT PRIMARY KEY AUTO_INCREMENT,
question_id INT,
option_id INT NULL, -- 文本回答时为NULL
text_answer TEXT, -- 文本回答内容
user_ip VARCHAR(45),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (question_id) REFERENCES question(id),
FOREIGN KEY (option_id) REFERENCES option(id)
);
```
### 二、JavaBean模型层
```java
// Survey.java
public class Survey {
private int id;
private String title;
private String description;
// getters/setters
}
// Question.java
public class Question {
private int id;
private int surveyId;
private String content;
private String type; // 单选/多选/文本
private List<Option> options;
// getters/setters
}
// Option.java
public class Option {
private int id;
private int questionId;
private String content;
// getters/setters
}
// Answer.java
public class Answer {
private int id;
private int questionId;
private int optionId; // 选项ID(文本回答为0)
private String textAnswer;
// getters/setters
}
// DBUtil.java (数据库操作类)
public class DBUtil {
public Connection getConnection() throws SQLException {
return DriverManager.getConnection(
"jdbc:mysql://localhost:3306/survey_db", "user", "pass");
}
public List<Survey> getAllSurveys() {
// 实现数据库查询逻辑
}
public void saveAnswer(Answer answer) {
// 实现回答保存逻辑
}
// 其他CRUD方法...
}
```
### 三、Servlet控制器
```java
// SurveyServlet.java
@WebServlet("/survey")
public class SurveyServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String action = request.getParameter("action");
switch(action) {
case "create":
showCreateForm(request, response);
break;
case "view":
showSurvey(request, response);
break;
case "results":
showResults(request, response);
break;
}
}
private void showResults(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
int surveyId = Integer.parseInt(request.getParameter("id"));
SurveyDAO surveyDAO = new SurveyDAO();
// 获取问卷结果数据
Map<Question, Map<String, Integer>> stats = surveyDAO.getSurveyStats(surveyId);
request.setAttribute("stats", stats);
request.getRequestDispatcher("/results.jsp").forward(request, response);
}
}
// AnswerServlet.java
@WebServlet("/answer")
public class AnswerServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
int surveyId = Integer.parseInt(request.getParameter("surveyId"));
Map<String, String[]> params = request.getParameterMap();
for (String param : params.keySet()) {
if (param.startsWith("q_")) {
int questionId = Integer.parseInt(param.substring(2));
String[] values = params.get(param);
// 创建Answer对象并保存
Answer answer = new Answer();
answer.setQuestionId(questionId);
// ...设置其他属性
new AnswerDAO().saveAnswer(answer);
}
}
response.sendRedirect("thankyou.jsp");
}
}
```
### 四、JSP视图层
#### 1. 问卷创建页面 (create.jsp)
```jsp
<%@ page contentType="text/html;charset=UTF-8" %>
<form action="survey?action=save" method="post">
<h2>创建新问卷</h2>
<input type="text" name="title" placeholder="问卷标题" required>
<textarea name="description" placeholder="问卷描述"></textarea>
<div id="questions">
<!-- 动态添加问题区域 -->
</div>
<button type="button" onclick="addQuestion()">添加问题</button>
<input type="submit" value="发布问卷">
</form>
<script>
function addQuestion() {
const html = `<div class="question">
<input type="text" name="q_content" placeholder="问题内容" required>
<select name="q_type" onchange="changeType(this)">
<option value="单选">单选</option>
<option value="多选">多选</option>
<option value="文本">文本</option>
</select>
<div class="options"></div>
<button type="button" onclick="addOption(this)">添加选项</button>
</div>`;
document.getElementById('questions').innerHTML += html;
}
</script>
```
#### 2. 问卷展示页面 (view.jsp)
```jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<h2>${survey.title}</h2>
<p>${survey.description}</p>
<form action="answer" method="post">
<input type="hidden" name="surveyId" value="${survey.id}">
<c:forEach items="${questions}" var="q">
<div class="question">
<p>${q.content}</p>
<c:choose>
<c:when test="${q.type == '单选'}">
<c:forEach items="${q.options}" var="opt">
<input type="radio" name="q_${q.id}" value="${opt.id}"> ${opt.content}<br>
</c:forEach>
</c:when>
<c:when test="${q.type == '多选'}">
<c:forEach items="${q.options}" var="opt">
<input type="checkbox" name="q_${q.id}" value="${opt.id}"> ${opt.content}<br>
</c:forEach>
</c:when>
<c:otherwise>
<textarea name="q_${q.id}"></textarea>
</c:otherwise>
</c:choose>
</div>
</c:forEach>
<input type="submit" value="提交问卷">
</form>
```
#### 3. 结果可视化页面 (results.jsp)
```jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<h2>${survey.title} - 统计结果</h2>
<c:forEach items="${stats}" var="entry">
<div class="question-result">
<h3>${entry.key.content}</h3>
<canvas id="chart_${entry.key.id}" width="400" height="300"></canvas>
<script>
const ctx = document.getElementById('chart_${entry.key.id}').getContext('2d');
const chartData = {
labels: [<c:forEach items="${entry.value}" var="opt">"${opt.key}",</c:forEach>],
datasets: [{
data: [<c:forEach items="${entry.value}" var="opt">${opt.value},</c:forEach>],
backgroundColor: ['#FF6384', '#36A2EB', '#FFCE56']
}]
};
new Chart(ctx, {
type: 'pie', // 或 'bar'
data: chartData
});
</script>
</div>
</c:forEach>
```
### 五、关键技术点
1. **MVC分层实现**:
- 视图层:JSP负责UI展示
- 控制器:Servlet处理请求路由
- 模型层:JavaBean封装业务逻辑和数据
2. **数据库优化**:
- 使用连接池管理数据库连接[^4]
- 批量处理回答提交操作
```java
// 在AnswerDAO中实现批量插入
public void saveAnswers(List<Answer> answers) {
try (Connection conn = DBUtil.getConnection();
PreparedStatement stmt = conn.prepareStatement(
"INSERT INTO answer(question_id, option_id, text_answer) VALUES(?,?,?)")) {
for (Answer a : answers) {
stmt.setInt(1, a.getQuestionId());
stmt.setObject(2, a.getOptionId() > 0 ? a.getOptionId() : null);
stmt.setString(3, a.getTextAnswer());
stmt.addBatch();
}
stmt.executeBatch();
}
}
```
3. **结果可视化**:
- 使用Chart.js绘制图表
- 统计SQL示例:
```sql
-- 获取问题选项统计
SELECT o.content AS option_text, COUNT(a.id) AS count
FROM answer a
JOIN option o ON a.option_id = o.id
WHERE a.question_id = ?
GROUP BY o.id;
```
4. **安全措施**:
- 使用PreparedStatement防止SQL注入
- 对用户输入进行XSS过滤
- 限制同一IP的提交频率
### 六、部署运行
1. 环境要求:
- Tomcat 9+
- MySQL 5.7+
- JDK 8+
2. 部署步骤:
```bash
# 创建数据库
mysql> CREATE DATABASE survey_db;
# 导入SQL脚本
mysql -u root -p survey_db < schema.sql
# 部署WAR包到Tomcat
cp survey_system.war $TOMCAT_HOME/webapps/
```
### 性能优化建议
1. 对热门问卷使用Redis缓存统计结果[^4]
2. 对大型问卷采用分页加载数据
3. 使用异步处理生成可视化图表
4. 对文本回答建立全文索引