多层结构应用系统示例
目标功能
场景
用户登录; 用户注册; 用户上传照片.
架构描述
展示层
-
视图负责用户交互, 向用户展示模型的信息, 接收用户输入.
- 用户登录: index.html
- 用户注册: newUser.jsp
- 用户上传照片: userDetail.jsp
-
模型代表与用户交互过程中的数据. 用户输入的数据和处理后向用户展示的数据.
- 用户: User
-
控制器负责接收用户输入, 进行数据格式转换后更新(/创建)模型; 把模型交给服务层处理; 把服务层处理完成后的模型交给向用户展示处理结果的视图.
- 用户登录: LoginServlet.java
- 用户注册: RegisterServlet.java
- 更新用户详情: UpdateUserDetailServlet.java
- 上传照片: UploadPicServlet.java
服务层
- 业务服务组织由多种领域对象参与的业务流程, 是服务层的入口.
- 用户服务: UserService
- 检查展示层传来数据的合法性
- 检查/设置对象的活动状态/进度状态
- 设置传递回展示层的对象必要的字段 - 一些展示层不需要的字段可能被屏蔽
- 组织业务流程, 调用领域服务 - 领域对象的一些仅用于展示层的字段可能被丢弃
- 用户服务: UserService
- 领域服务负责完成基本的业务活动.
- 身份认证工具: AuthenticationTool
- 用户管理: UserManagement
- 日志管理: LogTool
- 领域对象代表出现在业务活动/流程中的对象.
- 用户: User
- 用户操作日志: UserOperationLog
持久层
-
DAO负责数据库增删改查, 在对象(的实例)和数据库的表(的行)之间转换.
- User的增删改查: UserDao.java
- 日志记录: UserOperationLogDao.java
-
数据库负责数据的持久保存.
- 用户信息表: APPUSER
- 用户操作日志表: LOGIN_LOG
技术要点
在各层之间传递user对象, 并在各层的方法中改变user对象的状态.
-
网页提交到servlet, 把user对象传递到服务层处理后, 转到新的网页.
-
服务层在业务处理过程中调用数据访问层读写数据库.
查询日志表的SQL语句示例
SELECT * FROM ROOT.LOGIN_LOG where cast(time as varchar(128)) > '2020-03-01'
特别注意事项:
在项目的build目录中创建文件夹pic, 以存储上传照片.
用户登录场景
展示层
视图
用户登录网页index.html
内容如下
<!DOCTYPE html>
<html>
<head>
<title>Portal</title>
<meta charset="GB18030">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<form action="login" method="post">
用户名<input type="text" name="user_name" required>
口令<input type="password" name="password" value="" />
<input type="submit" value="提交">
</form>
<p>点击提交按钮,如果输入框是空的,浏览器会提示错误信息。</p>
</body>
</html>
点击"提交"按钮, 把form中的数据提交到相对当前目录的login. 此url对应的控制器是LoginServlet[^urlPatterns = {"/login"}].
控制器
LoginServlet.java
内容如下
package controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import model.User;
@WebServlet(name = "LoginServlet", urlPatterns = {"/login"})
public class LoginServlet extends HttpServlet {
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String userName = request.getParameter("user_name");
String password = request.getParameter("password");
User user = new User(userName, password);
service.UserService.login(user);
request.getSession().setAttribute("user", user);
request.setAttribute("user", user);
if (user.getIsLogin() == true) {
request.getRequestDispatcher("/WEB-INF/view/userDetail.jsp").forward(request, response);
} else {
request.getRequestDispatcher("/WEB-INF/view/newUser.jsp").forward(request, response);
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
public String getServletInfo() {
return "Short description";
}
}
其关键处理过程在方法processRequest()中. 处理过程如下
- 读取网页传来的参数, user_name和password. 封装在对象user中传递给服务层
- 服务层
service.UserService.login(user);
处理完user对象后,- 把user放置在会话session中保存
- 并且把user设置为request的属性
- 根据user是否登录成功, 转到交给不同视图
- 成功转到用户详情页面
"/WEB-INF/view/userDetail.jsp"
- user是为注册用户或者口令不正确,转到新用户注册页面
/WEB-INF/view/newUser.jsp
- 成功转到用户详情页面
模型
User.java内容如下
package model;
public class User {
private String name;
private String password;
private boolean isLogin;
private String pic;
public User() {
this("", "");
}
public User(String name, String password) {
this.name = name;
this.password = password;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean getIsLogin() {
return isLogin;
}
public void setIsLogin(boolean isLogin) {
this.isLogin = isLogin;
}
public String getPic() {
return pic;
}
public void setPic(String pic) {
this.pic = pic;
}
}
服务层
业务服务 - 服务聚合
组合业务活动的流程的UserService.java内容如下
package service;
import model.User;
public class UserService {
public static void login(User user) {
if (UserManagement.isBadUserFormat(user)) {
return;
}
boolean isAuthenticationPassed = AuthenticationTool.checkUser(user.getName(), user.getPassword());
if (isAuthenticationPassed) {
UserManagement.recoverUser(user);
LogTool.appendLog(user.getName(), "login", "OK");
} else {
LogTool.appendLog(user.getName(), "login", "Failed");
}
user.setIsLogin(isAuthenticationPassed);
}
public static void register(User user) {
if (UserManagement.isBadUserFormat(user)) {
return;
}
boolean isAdded = UserManagement.addUser(user);
if (isAdded == true) {
LogTool.appendLog(user.getName(), "registor", "OK");
user.setIsLogin(true);
} else {
user.setIsLogin(false);
LogTool.appendLog(user.getName(), "registor", "Failed");
}
}
public static void updateUserDetail(User user) {
if (UserManagement.isBadUserFormat(user)) {
return;
}
if (user.getIsLogin() == false) {
LogTool.appendLog(user.getName(), "update", "Failed", "not login");
return;
}
boolean isUpdated = UserManagement.updateUser(user);
LogTool.appendLog(user.getName(), "update", String.valueOf(isUpdated));
}
}
在用户登录场景只调用了service.UserService.login(user);
处理过程如下
- 检查user状态是否正常.
UserManagement.isBadUserFormat(user)
- 如果user==null, user.username==null或者为空串""或者全部是空白, 则返回false. 以保证user.username是正常的文字.
- 由
AuthenticationTool.checkUser()
检查用户名和口令是否正确. - 如果正确, 则读取user的各个字段, 填充到user对象中
UserManagement.recoverUser(user)
- 记录操作日志
LogTool.appendLog(user.getName(), "login", "OK");
- 记录操作日志
- 否则, 记录操作日志
LogTool.appendLog(user.getName(), "login", "Failed");
- 设置user的登录状态为执行
AuthenticationTool.checkUser()
的结果
领域服务
实现基本业务活动的服务/工具
用户认证服务AuthenticationTool.java
内容如下
package service;
import dao.UserDao;
import model.User;
public class AuthenticationTool {
static boolean checkUser(String userID, String password) {
User user = UserDao.selectUser(userID);
if (user == null) {
return false;
} else {
return user.getPassword().equals(password);
}
}
}
Authentication.check功能是
- 调用持久层的
UserDao.selectUser(userID)
查找指定用户的信息- 如果没有找到, 则认证失败
- 如果找到, 那么如果口令一致则成功, 口令不一致则失败
用户管理服务UserManagement.java
内容如下
package service;
import dao.UserDao;
import model.User;
public class UserManagement {
static boolean removeUser(User user) {
return UserDao.deleteUser(user);
}
static boolean update(User user) {
return UserDao.updateUser(user) != null;
}
static User findUser(String username) {
User user = UserDao.selectUser(username);
return user;
}
static void recoverUser(User user) {
User user1 = UserDao.selectUser(user.getName());
if(user1 == null) return;
user.setPassword(user1.getPassword());
user.setPic(user.getPic());
}
static boolean addUser(User user) {
boolean isUserFinded = findUser(user.getName()) != null;
if (isUserFinded) {
LogTool.appendLog(user.getName(), "register", "Failed", "username is existed");
return false;
}
Boolean isAdded = UserDao.insertUser(user);
return isAdded;
}
static boolean isBadUserFormat(User user) {
if (user == null) {
return true;
}
if (user.getName() == null) {
return true;
}
if (user.getName().trim().length() == 0) {
return true;
}
return false;
}
}
UserManagement.recoverUser(user)
调用UserDao.selectUser()
方法. 功能是
- 调用持久层的
UserDao.selectUser(user)
查找指定用户的信息, 形成user对象.- 如果没有找到, 则直接返回. 不对user的属性填充
- 填充user的pic和password字段的数据.
持久层
DAO
负责访问数据库的读写User对象的UserDao.java
,内容如下
package dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import model.User;
public class UserDao {
public static User selectUser(String usernname) {
String sql = "SELECT * from appuser where username = ?";
try {
Connection connectDb = DerbyConnection.connectDb();
PreparedStatement p = connectDb.prepareStatement(sql);
p.setString(1, usernname);
ResultSet rs = p.executeQuery();
if (rs.next()) {
User user = new User(rs.getString("username"), rs.getString("password"));
user.setPic(rs.getString("pic"));
return user;
} else {
return null;
}
} catch (Exception e) {
System.out.println(e);
return null;
}
}
public static User updateUser(User user) {
String sql = "UPDATE appuser SET pic = ?, password = ? WHERE username = ?";
try {
Connection connectDb = DerbyConnection.connectDb();
PreparedStatement p = connectDb.prepareStatement(sql);
p.setString(1, user.getPic());
p.setString(2, user.getPassword());
p.setString(3, user.getName());
p.execute();
connectDb.commit();
} catch (Exception e) {
System.out.println(e);
user = null;
}
return user;
}
public static boolean insertUser(User user) {
String sql = "INSERT INTO appuser (username, password) VALUES (?, ?)";
try {
Connection connectDb = DerbyConnection.connectDb();
PreparedStatement p = connectDb.prepareStatement(sql);
p.setString(1, user.getName());
p.setString(2, user.getPassword());
p.execute();
connectDb.commit();
} catch (Exception e) {
System.out.println(e);
return false;
}
return true;
}
public static boolean deleteUser(User user) {
String sql = "DELETE FROM appuser where username = ?";
try {
Connection connectDb = DerbyConnection.connectDb();
PreparedStatement p = connectDb.prepareStatement(sql);
p.setString(1, user.getName());
p.execute();
connectDb.commit();
return true;
} catch (Exception e) {
System.out.println(e);
return false;
}
}
}
在此次调用中, 仅有UserDao.selectUser(userID)
selectUser(String usernname)方法的执行过程如下
- 定义查询SQL语句
- 调用DerbyConnection.connectDb()获得到数据库的连接
- 创建访问数据库的sql语句对象, 并设置sql语句中参数.
- 执行查询得到结果集
- 如果得到的结果集中有数据行
- 根据数据行的内容创建一个User对象, 作为返回值
- 否则返回空
- 如果得到的结果集中有数据行
负责建立数据库连接的DerbyConnection.java内容如下
package dao;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DerbyConnection {
private static Connection connection;
public static Connection connectDb() {
if (connection != null) {
return connection;
}
try {
Class.forName("org.apache.derby.jdbc.ClientDriver");
connection = DriverManager.getConnection(
"jdbc:derby://localhost:1527/OOPDB",
"root",
"123456");
return connection;
} catch (Exception e) {
connection = null;
return null;
}
}
public static void close(){
try {
connection.close();
} catch (SQLException ex) {
}
connection = null;
}
}
其核心功能为
- 加载jdbc驱动程序
Class.forName("org.apache.derby.jdbc.ClientDriver");
- 创建到数据库的连接
DriverManager.getConnection( "jdbc:derby://localhost:1527/OOPDB", "root", "123456");
- 其中
jdbc:derby://localhost:1527/OOPDB
是数据库的url,- 用jdbc访问derby数据库
- 数据库在本机的1527端口运行
- 数据库名称为OOPDB
- root是用户名
- 123456是口令
- 其中
另一个DAO对象是记录用户登录日志的UserOperationLogDao.java
内容如下
package dao;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import model.UserOperationLog;
public class UserOperationLogDao {
public static List<UserOperationLog> selectLog(String usernname) {
List<UserOperationLog> logs = new ArrayList<>();
String sql = "SELECT * from login_log where username = ?";
try {
PreparedStatement p = DerbyConnection.connectDb().prepareStatement(sql);
p.setString(1, usernname);
ResultSet rs = p.executeQuery();
while (rs.next()) {
UserOperationLog log = new UserOperationLog(rs.getString("logID"), rs.getString("username"), rs.getTimestamp("time").toLocalDateTime(), rs.getString("action"));
logs.add(log);
}
} catch (Exception e) {
System.out.println(e);
return null;
}
return logs;
}
public static UserOperationLog insertLog(UserOperationLog log) {
String sql = "INSERT INTO login_log (logID, username, time, action) VALUES (?, ?, ?, ?)";
try {
PreparedStatement p = DerbyConnection.connectDb().prepareStatement(sql);
p.setString(1, log.getLogID());
p.setString(2, log.getUsername());
p.setTimestamp(3, Timestamp.valueOf(log.getTime()));
p.setString(4, log.getAction());
p.execute();
} catch (Exception e) {
System.out.println(e);
log = null;
}
return log;
}
}
方法insertLog(LoginLog log)
中用使用SQL的插入语句. 并且表 login_log的字段time是timestamp类型. 对应的日志对象LoginLog中的time是LocalDateTime类型. 程序中进行了数据格式的转换.
UserOperationLog.java内容如下
package model;
import java.time.LocalDateTime;
import java.util.UUID;
public class UserOperationLog {
private String logID;
private String username;
private LocalDateTime time;
private String action;
public UserOperationLog() {
this("", "");
}
public UserOperationLog(String username, String action) {
this.username = username;
this.action = action;
this.logID = UUID.randomUUID().toString();
this.time = LocalDateTime.now();
}
public UserOperationLog(String logID, String username, LocalDateTime time, String action) {
this.logID = logID;
this.username = username;
this.time = time;
this.action = action;
}
public String getLogID() {
return logID;
}
public void setLogID(String logID) {
this.logID = logID;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
public LocalDateTime getTime() {
return time;
}
public void setTime(LocalDateTime time) {
this.time = time;
}
}
其中logID对应数据库中的主键采用UUID.randomUUID().toString();
方式生成, 只是演示UUID的生成方法, 对于日志并没有实质的用途, 除了在分布式生成日志行时, 能够"保证"主键不重复.
数据库
用户信息表: APPUSER
- username varchar[50] PK
- password varchar[50]
- pic varchar[200]
用户操作日志表: LOGIN_LOG
- logid varchar[50] PK
- username varchar[50]
- action varchar[200]
- time timestamp
用户注册场景
展示层
视图
用户名口令检查不通过的情况, 转到/WEB-INF/view/newUser.jsp
内容如下
<%@page import="model.User"%>
<%@page contentType="text/html" pageEncoding="GB18030"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=GB18030">
<title>JSP Page</title>
</head>
<body>
<% //java代码写在这里
User user = (User) request.getAttribute("user");
%>
<h1>
Hello <%=user.getName()%>
</h1>
<h1>
口令检查结果为 <%=user.getIsLogin()%>
</h1>
<h1>换一个用户名, 注册新用户</h1>
<form action="register" method="post">
用户名<input type="text" name="user_name" required>
口令<input type="password" name="password" value="" />
<input type="submit" value="提交">
</form>
<p>点击提交按钮,如果输入框是空的,浏览器会提示错误信息。</p>
</body>
</html>
接收了从LoginServlet传递来的request中的属性user,
- User user = (User) request.getAttribute(“user”);
- 在页面中显示用户名
<%=user.getName()%>
和口令检查结果<%=user.getIsLogin()%>
输入新的用户名和口令后, 点击"提交"按钮后, 到同目录下的register对应的RegisterServlet[^urlPatterns = {"/register"}]执行.
控制器
控制器RegisterServlet.java内容如下
package controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import model.User;
@WebServlet(name = "RegisterServlet", urlPatterns = {"/register"})
public class RegisterServlet extends HttpServlet {
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String userName = request.getParameter("user_name");
String password = request.getParameter("password");
User user = new User(userName, password);
service.UserService.register(user);
request.getSession().setAttribute("user", user);
request.setAttribute("user", user);
if (user.getIsLogin() == true) {
request.getRequestDispatcher("/WEB-INF/view/userDetail.jsp").forward(request, response);
} else {
request.getRequestDispatcher("/WEB-INF/view/newUser.jsp").forward(request, response);
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
public String getServletInfo() {
return "Short description";
}
}
其关键处理过程在方法processRequest()中. 处理过程如下
- 读取网页传来的参数, user_name和password. 封装在对象user中传递给服务层
- 服务层
service.UserService.register(user);
处理完user对象后,- 把user放置在会话session中保存
- 并且把user设置为request的属性
- 根据user是否处于登录成功状态, 转到交给不同视图
- 成功转到用户详情页面
"/WEB-INF/view/userDetail.jsp"
- user是为注册用户或者口令不正确,转回到新用户注册页面
/WEB-INF/view/newUser.jsp
- 成功转到用户详情页面
模型
User.java
服务层
业务服务 - 服务聚合
service.UserService.register(user);
对应的方法内容如下
public static void register(User user) {
if (UserManagement.isBadUserFormat(user)) {
return;
}
boolean isAdded = UserManagement.addUser(user);
if (isAdded == true) {
LogTool.appendLog(user.getName(), "registor", "OK");
user.setIsLogin(true);
} else {
user.setIsLogin(false);
LogTool.appendLog(user.getName(), "registor", "Failed");
}
}
其处理过程如下
- 检查user格式是否正常
- 如果user==null, user.username==null或者为空串""或者全部是空白, 则返回false. 以保证user.username是正常的文字.
- 由
UserManagement.addUser()
增加新用户.- 如果增加新用户成功
- 记录操作日志
LogTool.appendLog(user.getName(), "registor", "OK");
- 调用用户登录方法, 更新用户状态
- 记录操作日志
- 否则, 设置user为未登录状态
- 记录操作日志
LogTool.appendLog(user.getName(), "registor", "Failed");
- 记录操作日志
- 如果增加新用户成功
领域服务
UserManagement.java
文件中的addUser()方法内容如下
static boolean addUser(User user) {
boolean isUserFinded = findUser(user.getName()) != null;
if (isUserFinded) {
LogTool.appendLog(user.getName(), "register", "Failed", "username is existed");
return false;
}
Boolean isAdded = UserDao.insertUser(user);
return isAdded;
}
其处理过程如下
- 根据用户名查找用户是否已经存在
- 如果找到, 则记录日志后直接返回false
- 调用
UserDao.insertUser()
增加用户, 并返回增加结果是否成功.
持久层
DAO
UserDao的insertUser()方法如下
public static boolean insertUser(User user) {
String sql = "INSERT INTO appuser (username, password) VALUES (?, ?)";
try {
Connection connectDb = DerbyConnection.connectDb();
PreparedStatement p = connectDb.prepareStatement(sql);
p.setString(1, user.getName());
p.setString(2, user.getPassword());
p.execute();
connectDb.commit();
} catch (Exception e) {
System.out.println(e);
return false;
}
return true;
}
执行过程如下
- 定义SQL插入语句
- 建立到数据库的连接
- 创建执行SQL语句的对象, 并设置需要的参数name和password
- 执行并提交
数据库
同上
用户上传照片场景
<%--
Document : addUserDetail
Created on : 2020-2-29, 10:45:51
Author : subo1
--%>
<%@page import="model.User"%>
<%@page contentType="text/html" pageEncoding="GB18030"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=GB18030">
<title>JSP Page</title>
<script language=javascript>
function process(action) {
document.userDetailForm.action = action;
document.userDetailForm.submit();
}
</script>
</head>
<body>
<% //java代码写在这里
User user = (User) request.getAttribute("user");
%>
<h1>维护用户信息, 上传照片</h1>
<form name="userDetailForm" method="POST" enctype="multipart/form-data">
<table border="1">
<thead>
<tr>
<th>用户名</th>
<th>口令</th>
<th>照片</th>
<th>照片上传</th>
</tr>
</thead>
<tbody>
<tr>
<td><%=user.getName()%></td>
<td><%=user.getPassword()%></td>
<td><img src="<%=user.getPic()%>" alt="upload a jpg"></td>
<td>
<input type="file" name="<%=user.getName()%>.jpg"/>
<input type="button" value="上传" onclick="process('uploadpic')"/>
</td>
</tr>
<tr>
<td> </td>
<td> </td>
<td> </td>
<td><input type="button"value="提交更改" οnclick="process('updateuser')" /></td>
</tr>
</tbody>
</table>
</form>
</body>
</html>
其中的JavaScript代码
<script language=javascript>
function process(action) {
document.userDetailForm.action = action;
document.userDetailForm.submit();
}
</script>
用于尽心form提交. 提交action的目标url是
- uploadpic, 当点击按钮"上传"时
<input type="button" value="上传" onclick="process('uploadpic')"/>
- updateuser, 当点击按钮"提交更改"时
<input type="button"value="提交更改" onclick="process('updateuser')" />
在表格中显示图片<img src="<%=user.getPic()%>" alt="upload a jpg">
约定,
- 图片保存在网站运行上下文目录中的文件夹pic
- 在用NetBeans管理运行JavaWeb项目时, 每次重新部署会删除pic目录. 只能手工重建.
- user的pic字段用于存储图片的相对路径和文件名, 约定为jpg
<input type="file" name="<%=user.getName()%>.jpg"/>
- 图片文件名为每个用户名.jpg, 目录为pic. 例如pic/00.jpg
控制器
控制器UploadPicServlet.java内容如下
package controller;
import java.io.IOException;
import java.util.Collection;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import model.User;
@WebServlet(name = "UploadPicServlet", urlPatterns = {"/uploadpic"})
@MultipartConfig(maxFileSize = 1000_000_000, maxRequestSize = 2000_000_000)
public class UploadPicServlet extends HttpServlet {
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
User user = (User) request.getSession().getAttribute("user");
Collection<Part> parts = request.getParts();
for (Part part : parts) {
if (part.getSubmittedFileName() != null) {
String realPath = this.getServletContext().getRealPath("/pic/");
part.write(realPath + part.getName());
user.setPic("pic/" + part.getName());
}
}
request.setAttribute("user", user);
request.getRequestDispatcher("/WEB-INF/view/userDetail.jsp").forward(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
public String getServletInfo() {
return "Short description";
}
}
其关键方法processRequest()的执行过程如下:
- 从session中读取user对象
- 对于上传的文件, 保存在当前运行上下文目录中的/pic/目录中, 文件名用网页端指定的名称.
- 为request设置user属性, 传递(又回)到视图网页
/WEB-INF/view/userDetail.jsp
控制器UpdateUserDetailServlet内容如下
package controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import model.User;
@WebServlet(name = "UpdateUserDetailServlet", urlPatterns = {"/updateuser"})
public class UpdateUserDetailServlet extends HttpServlet {
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
User user = (User) request.getSession().getAttribute("user");
service.UserService.updateUserDetail(user);
request.getSession().setAttribute("user", user);
request.setAttribute("user", user);
if (user.getIsLogin() == true) {
request.getRequestDispatcher("/WEB-INF/view/userDetail.jsp").forward(request, response);
} else {
request.getRequestDispatcher("/WEB-INF/view/newUser.jsp").forward(request, response);
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
public String getServletInfo() {
return "Short description";
}
}
其关键方法processRequest()的执行过程如下:
- 从session中读取user对象
- 更新用户的信息, 调用
service.UserService.updateUserDetail(user)
- 为request设置user属性,
- 如果用户处于登录状态, 传递到视图网页
/WEB-INF/view/userDetail.jsp
- 否则转到注册新用户视图
/WEB-INF/view/newUser.jsp
- 如果用户处于登录状态, 传递到视图网页
模型
同上
服务层
业务服务 - 服务聚合
service.UserService.updateUserDetail(user);
对应的方法内容如下
public static void updateUserDetail(User user) {
if (UserManagement.isBadUserFormat(user)) {
return;
}
if (user.getIsLogin() == false) {
LogTool.appendLog(user.getName(), "update", "Failed", "not login");
return;
}
boolean isUpdated = UserManagement.updateUser(user);
LogTool.appendLog(user.getName(), "update", String.valueOf(isUpdated));
}
其处理过程如下
- 检查user格式是否正常
- 如果user==null, user.username==null或者为空串""或者全部是空白, 则返回false. 以保证user.username是正常的文字.
- 检查用户是否登录, 如果没有登录
- 记录操作日志
LogTool.appendLog(user.getName(), "update", "Failed", "not login");
- 记录操作日志
- 由
UserManagement.updateUser()
更新用户. - 记录操作日志
LogTool.appendLog(user.getName(), "update", String.valueOf(isUpdated));
领域服务
UserManagement.java
文件中的updateUser()方法内容如下
static boolean updateUser(User user) {
return UserDao.updateUser(user) != null;
}
持久层
DAO
UserDao的updateUser()方法如下
public static User updateUser(User user) {
String sql = "UPDATE appuser SET pic = ?, password = ? WHERE username = ?";
try {
Connection connectDb = DerbyConnection.connectDb();
PreparedStatement p = connectDb.prepareStatement(sql);
p.setString(1, user.getPic());
p.setString(2, user.getPassword());
p.setString(3, user.getName());
p.execute();
connectDb.commit();
} catch (Exception e) {
System.out.println(e);
user = null;
}
return user;
}
执行过程如下
- 定义SQL更新语句
- 建立到数据库的连接
- 创建执行SQL语句的对象, 并设置需要的参数pic, password和name
- 执行并提交
数据库
同上
运行结果
登录页面
输入数据库中存在的用户名口令, 显示用户详情, 等待照片上传.
点击"选择文件"按钮, 选择jpg图片, 然后点击"上传"按钮.
上传照片后, 点击提交更改. 数据库中appuser表的记录username=00的行的PIC被添加了图片文件名pic/00.jpg
.
查看日志表