友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
JSP入门教程(DOC格式)-第10部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
建表 sql 脚本放在 10…01/WEB…INF/sql/import。sql。
…用户
create table user(
id bigint; …主键
username varchar (100); …帐号
password varchar (100); …密码
reg_time datetime; …注册时间
last_login datetime …上次登录时间
);
…主题
create table thread(
id bigint; …主题
title varchar (200); …标题
content varchar (2000); …内容
create_time datetime; …发帖时间
update_time datetime; …更新时间
92 / 148
…………………………………………………………Page 93……………………………………………………………
hit integer; …点击数
user bigint …发帖用户
);
…回复
create table ment(
id bigint; …主题
content varchar (2000); …内容
create_time datetime; …发布时间
user bigint; …回复用户
thread bigint …回复的主题
);
根据数据库表建模。每张表对应三部分:domain,dao 和 servlet。domain 是简
单的 javabean 用来封装数据表中的数据,dao 中进行对数据库的业务操作,
servlet 作为控制器处理请求调用 dao 和 domain 实现业务功能。
为了便于管理,将使用到的类分成四个包,domain,dao,utils 和 web。domain;
dao; web 中分别包含 domain; dao 和 servlet 类,utils 包中是数据库连接工具
和过滤器。
这里的 domain 和 dao 都是按照理想状态编写的,将数据库表中的字段对应到
domain 类中,然后 dao 提供 CRUD 功能,不过 dao 中的有些功能并没有用到,比
如 update 和 remove。
10。3。 功能设计
整个在线留言板可分为两大功能部分:用户管理与主题回复管理。
10。3。1。 用户管理
用户管理功能包括:新用户注册,用户登录,用户注销。用户登录的时候顺便带
上一个用户在线列表。
这部分的页面主要在 security 目录下,操作代码都放在
anni。web。UserServlet。java 和对应的 anni。domain。User,anni。dao。UserDao
中。
93 / 148
…………………………………………………………Page 94……………………………………………………………
1。 新用户注册
这是 CRUD 中的 create,向用户表中添加一条新信息,我们只在前台页面
中使用 javascript 进行数据校验,要求用户输入用户名,密码,并且在
两次密码输入相同的时候才能提交。
提交的请求交由 UserServlet 的register()方法处理。
/**
* 注册新用户。
*/
public void register(HttpServletRequest
request;HttpServletResponse response) throws Exception {
String username = request。getParameter(〃username〃);
String password = request。getParameter(〃password〃);
String confirmPassword =
request。getParameter(〃confirmPassword〃);
boolean userExists = userDao。checkExists(username);
if (userExists) {
request。setAttribute(〃error〃; 〃用户名:〃 + username + 〃
已被使用了,请更换其他用户名注册。〃);
request。getRequestDispatcher(〃/security/register。jsp〃)。forward(
request; response);
} else {
User user = new User();
user。setUsername(username);
user。setPassword(password);
userDao。save(user);
response。sendRedirect (request。getContextPath() +
〃/security/registerSuccess。jsp〃);
}
}
获得用户名和密码后,先通过 userDao。checkExists()检测数据库中是否
已经有了同名的用户,如果用户名重复,就跳转到
/security/register。jsp 显示错误信息。如果用户名没有重复,则将此
用户信息添加入库,然后页面重定向到/security/registerSuccess。jsp
显示注册成功信息。
94 / 148
…………………………………………………………Page 95……………………………………………………………
保存信息之后使用 redirect 是个避免重复提交的简易方法,如果使用
forward,浏览器上的url 不会改变,用户刷新页面就会导致重复提交信
息。
2。 用户登录与注销
登录与注销的流程与之前介绍的大体相同。第 4。2 节 “例子:在线列表”
/**
* 登录。
*/
public void login(HttpServletRequest request;HttpServletResponse
response) throws Exception {
String username = request。getParameter(〃username〃);
String password = request。getParameter(〃password〃);
User user = userDao。login(username; password);
if (user != null) {
user。setLastLogin(new Date());
userDao。update(user);
HttpSession session = request。getSession();
session。setAttribute(〃user〃; user);
// 加入在线列表
session。setAttribute(〃onlineUserBindingListener〃; new
OnlineUserBindingListener(username));
response。sendRedirect(request。getContextPath() +
〃/security/loginSuccess。jsp〃);
} else {
request。setAttribute(〃error〃; 〃用户名或密码错误!〃);
request。getRequestDispatcher(〃/security/login。jsp〃)。forward(req
uest; response);
}
}
/**
* 注销。
*/
public void logout(HttpServletRequest
request;HttpServletResponse response) throws Exception {
request。getSession()。invalidate();
95 / 148
…………………………………………………………Page 96……………………………………………………………
response。sendRedirect(request。getContextPath() +
〃/security/logoutSuccess。jsp〃);
}
我们先根据请求中的用户名和密码去数据库搜索用户信息。如果能找到,
说明用户输入无误可以登录,这时更新用户最后登录时间,并将 user 保
存到 session 中,同时使用 listener 操作在线列表。
如果用户名或密码错误,则将请求转发至/security/login。jsp 页面,显
示错误信息。
3。 控制用户访问权限
与用户操作相关的还有 anni。utils。SecurityFilter,我们使用它来控制
用户的访问权限。可以参考之前的讨论:第 7。2 节 “用filter 控制用
户访问权限”。
web。xml 中对 SecurityFilter 的配置如下:
SecurityFilter
anni。utils。SecurityFilter
SecurityFilter
/*
因为filter…mapping太不灵活,我们让SecurityFilter过滤所有的请求,
在代码里判断哪些请求需要保护。
public void doFilter(ServletRequest request;
ServletResponse response;
FilterChain chain)
throws IOException; ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
String url = req。getServletPath();
String method = req。getParameter(〃method〃);
if (〃/create。jsp〃。equals(url) ||
(〃/thread。do〃。equals(url) && 〃post〃。equals(method)) ||
96 / 148
…………………………………………………………Page 97……………………………………………………………
(〃/ment。do〃。equals(url) && 〃post〃。equals(method))) {
HttpSession session = req。getSession();
if (session。getAttribute(〃user〃) == null) {
res。sendRedirect(req。getContextPath() +
〃/security/securityFailure。jsp〃);
return ;
}
}
chain。doFilter(request; response);
}
在此我们只保护三个请求:/create。jsp (进入发布新主题的页面),
/thread。do?method=post (发布新主题),/ment。do?method=post (发
布回复)。这三个操作只有在用户登录之后才能访问,如果用户还没有的
登录就会页面重定向到/security/securityFailure。jsp,显示权限不足
无法访问的提示信息。
10。3。2。 主题回复管理
主题回复管理功能包括:查看所有主题,查看某一主题的详细信息和对应回复,
发表新主题,发表回复。点击主题时还会计算点击数。
1。 查看所有主题信息
进入应用,index。jsp 会立即跳转到/forum。do?method=list,并在
list。jsp 中显示所有主题,包括主题标题,回复数,作者,点击数,最
后回复时间,最后回复人。这些信息按照“最后回复时间”进行逆序排列。
实现代码在 anni。web。ForumServlet 的 list()方法内。
/**
* 显示所有帖子。
*/
private void list(HttpServletRequest request; HttpServletResponse
response) throws Exception {
List list = forumDao。getAll();
request。setAttribute(〃list〃; list);
request。getRequestDispatcher(〃/list。jsp〃)。forward(request;
response);
}
97 / 148
…………………………………………………………Page 98……………………………………………………………
调用 anni。dao。ForumDao 的pagedQuery()方法返回我们需要的信息,这
里只用 domain 中定义的类已经无法满足我们了(显示的信息包含了三个
表的信息),为了方便起见我们直接使用了 Map 来传递数据。
public List getAll() throws Exception {
Connection conn = null;
Statement state = null;
List list = new ArrayList();
try {
conn = DbUtils。getConn();
state = conn。createStatement();
String sql = 〃select 〃 +
〃t。id; 〃 +
〃t。title; 〃 +
〃(select count(id) from ment where
thread=t。id) as reply; 〃 +
〃(select username from user where id=t。user) as
author; 〃 +
〃t。hit; 〃 +
〃(select top 1 create_time from ment where
thread=t。id order by create_time desc) as create_time; 〃 +
〃(select top 1 u。username from ment c;user u
where c。thread=t。id and c。user=u。id 〃 +
〃order by create_time desc) as user 〃 +
〃from thread t 〃 +
〃order by user desc〃 ;
ResultSet rs = state。executeQuery(sql);
while (rs。next()) {
Map map = new HashMap();
map。put(〃id〃; rs。getLong(1)); // 主键
map。put(〃title〃; rs。getString(2)); // 标题
map。put(〃reply〃; rs。getInt(3)); // 回复数
map。put(〃author〃; rs。getString(4)); // 作者
map。put(〃hit〃; rs。getInt(5)); // 点击数
map。put(〃updateDate〃; rs。getTimestamp(6)); // 最后发
言时间
map。put(〃user〃; rs。getString(7)); // 最后发言人
list。add(map);
}
} finally {
98 / 148
…………………………………………………………Page 99……………………………………………………………
DbUtils。close(null; state; conn);
}
return list;
}
或许有人会奇怪为什么不直接使用 ResultSet。这其实是一种理念问题,
如果你返回 ResultSet 到 jsp 页面,的确免去了封装成 Map 的步骤,但是
同时产生了两个问题。
第一,数据库操作对应的代码蔓延到前台页面,有违我们分层设计的初衷。
如果觉得我们这是过度设计的话,那么第二个问题则是更严重的,将
ResultSet 放到 jsp 上很难控制何时关闭数据库连接,如果发生了异常可
能来不及关闭数据连接,用不了多长时间就会耗尽资源了。
ForumDao 中,勉强拼凑出三个表连接查询的 sql,还不清楚性能是否有保
证。
2。 显示主题详细信息
点击主题标题/forum。do?method=view&id=1,会进入显示对应详细信息的
页面/view。jsp。顶部显示的是主题帖子的标题,发布时间,作者和内容。
主题内容下面列出所有的回复内容,页面底部是回复使用的表单,只有登
录之后才能使用。
ForumServlet 中的view()方法用来获得我们需要的主题信息和对应的回
复信息。
/**
* 显示帖子内容。
*/
private void view(HttpServletRequest request; HttpServletResponse
response) throws Exception {
long id = Long。parseLong(request。getParameter(〃id〃));
Map thread = forumDao。viewThread(id);
List list = forumDao。getmentsByThread(id);
request。setAttribute(〃thread〃; thread);
request。setAttribute(〃list〃; list);
request。getRequestDispatcher(〃/view。jsp〃)。forward(request;
response);
}
我们从请求中获得主题的 id,获得主题详细信息和对应的回复信息列表,
这两项都是使用 Map 传递数据传递到 view。jsp 页面中再使用 el 和 jstl
显示出来。
99 / 148
…………………………………………………………Page 100……………………………………………………………
在显示主题详细信息时,顺便讲主题的点击数加一。
public Map viewThread(long id) throws Exception {
Connection conn = null;
PreparedStatement state = null;
Map map = new HashMap();
try {
conn = DbUtils。getConn();
state = conn。prepareStatement(〃select
t。id;t。title;t。content;t。create_time;u。username 〃 +
〃from thread t;user u where t。user=u。id and t。id=?〃);
state。setLong(1; id);
ResultSet rs = state。executeQuery();
if (rs。next()) {
map。put(〃id〃; rs。getLong(1)); // 主键
map。put(〃title〃; rs。getString(2)); // 标题
map。put(〃content〃; rs。getString(3)); // 内容
map。put(〃createTime〃; rs。getTimestamp(4)); // 发布时
间
map。put(〃username〃; rs。getString(5)); // 作者名
}
// 增加点击数
state = conn。prepareStatement(〃update thread set
hit=hit+1 where id=?〃);
state。setLong(1; id);
state。executeUpdate();
} finally {
DbUtils。close(null; state; conn);
}
return map;
}
我们把这个更新操作放到查询之后,使用 update 将 hit 字段加一,也是
为了避免在异常情况下找不到对应主题时,不必出现更新异常。
3。 发布新主题和发布回复
这两项对应了 anni。web。ThreadServlet 和 anni。web。mentServlet 中
的post()方法。
100 / 148
…………………………………………………………Page 101……………………………………………………………
为了简易起见,我们仅仅在页面上使用 javascript 检验输入的数据不能
为空。
提交之后会调用对应 dao 中的 save()方法将数据保存进数据库。最后页
面重定向到/forum。do?method=list 或/forum。do?method=view&id=1。实
际上它们都是单纯的 create 操作(CRUD 中的C)。
10。3。3。 显示在线用户列表
我们使用了 HttpSessionBindingListener 来实现在线用户列表。详细介绍见
第 8。2 节 “使用HttpSessionBindingListener”。
/list。jsp 和/view。jsp 两个页面上的在线用户列表显示效果完全一样,如果有
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!