留言管理V1.0

原创
2016/09/02 15:20
阅读数 186

1、需求分析

1.1、用例分析:主要是分析整个系统的功能。

    基于用户管理功能的用例

    基于留言管理的用例

1.2、类图分析:在用例的基础上来分析需要存在哪些对象。

1.3、创建ER图:可以直接根据ER图生成相应的基于MySQL的SQL脚本。使用工具:MySQL WorkBench

    创建ER图:

    

    导出SQL脚本:

    

    

    登录数据库导入脚本:

    在命令提示符:mysql -u root -p

    创建数据库:create database pm_msg

    使用数据库: use pm_msg

    导入数据库:source d:\\xxx

    创建用户:GRANT ALL ON pm_msg.* TO 'pm_msg'@'localhost' IDENTIFIED BY 'pm123'

2、创建类

2.1、确定好包

    org.pm.message.vo--->放置原始对象
    org.pm.message.idao--->dao的接口
    org.pm.message.dao.impl--->dao的实现类
    org.pm.message.exception--->放置异常的类
    org.pm.message.util--->放置工具类
    org.pm.message.test--->放置测试类

2.2、创建原始类

    全部在org.pm.message.vo中创建,所有属性设置为private,并且提供相应的getter和setter方法。

2.3、创建异常处理类:MessageException

2.4、创建DAO接口:创建IUserDao完成对User对象的CRUD操作(Create,Read,Update,Delete)

2.5、创建DAO实现类

    1、导入数据库的驱动:将数据库驱动拷贝到lib目录下

    2、创建DBUtil来统一创建和关闭Connection、Preparestatement、ResultSet

	public static Connection getConnention() {
		//访问数据库的用户名和密码
		String username = "pm";
		String password = "pm123";
		//固定写法,localhost:3306表示数据库的地址和端口是3306
		//数据库名称,?后可加参数进行字符编码等相关设置
		String url = "jdbc:mysql://localhost:3306/pm_msg?useSSL=false";
		Connection con = null;
		try {
			//加载驱动类(特别注意)
			Class.forName("com.mysql.jdbc.Driver");
			con = DriverManager.getConnection(url, username, password);
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return con;
	}

    3、创建实现类

    通过配置文件来设置数据库的用户名和密码:

    首先添加一个properties的配置文件,在该文件中将用户名和密码手动配置进去,properties文件是通过键值对来进行对应的。

username=pm
password=pm123
url=jdbc:mysql://localhost:3306/pm_msg?useSSL=false

    读取properties文件

    1、创建Properties对象

Properties prop = new Properties();

    2、通过该对象的load()读入文件

prop.load(TestProp.class.getClassLoader().getResourceAsStream("jdbc.properties"));

    3、使用该对象的getProperty()方法可以根据key获取value

String username = prop.getProperty("username");

    由于Properties对象仅仅只用初始化一次就可以,所以使用单例模式完成操作。

	private static Properties jdbcProp;
	/*
	 * 使用单例模式来读取Properties文件
	 */
	public static Properties getJdbcProp() {
		try {
			//如果对象不存在才创建,否则就直接返回,此时返回的对象永远都是同一个对象
			if(jdbcProp==null) {
				jdbcProp = new Properties();
				jdbcProp.load(PropertiesUtil.class.getClassLoader().getResourceAsStream("jdbc.properties"));
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return jdbcProp;
	}

    编写DAO的实现类

    load(int id)

    1、创建Connection
    2、创建SQL
    3、创建PreparedStatement
    4、设置ps的值
    5、根据ps获取rs
    6、循环该rs依次获取相应的对象

	@Override
	public Message load(int id) {
		Message msg = null;
		Connection con = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			//1、创建Connection
			con = DBUtil.getConnention();
			//2、编写sql
			String sql = "select * from t_msg where id=?";
			//3、创建ps
			ps = con.prepareStatement(sql);
			//4、使用ps设置sql中?
			ps.setInt(1, id);
			//5、根据ps获取rs,执行查询
			rs = ps.executeQuery();
			while(rs.next()) {
				msg = new Message();
				msg.setContent(rs.getString("content"));
				msg.setId(rs.getInt("id"));
				//不能使用getDate(),getDate()仅仅只是获取年月日而不能获取时间
				//getTime()仅仅只能获取时间,需要使用getTimestamp()
				msg.setPostDate(new Date(rs.getTimestamp("post_date").getTime()));
				msg.setTitle(rs.getString("title"));
				msg.setUserId(rs.getInt("user_id"));
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			DBUtil.close(rs);
			DBUtil.close(ps);
			DBUtil.close(con);
		}
		return msg;
	}

    update

	@Override
	public void update(Message msg) {
		Connection con = null;
		PreparedStatement ps = null;
		try {
			con = DBUtil.getConnention();
			String sql = "update t_msg set title=?,content=? where id=?";
			ps = con.prepareStatement(sql);
			ps.setString(1, msg.getTitle());
			ps.setString(2, msg.getContent());
			ps.setInt(3, msg.getId());
			//执行修改
			ps.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			DBUtil.close(ps);
			DBUtil.close(con);
		}
	}

3、创建页面

    3.1、创建原则

    主要分为三大部分:顶部、底部和内容部分。

    顶部和底部都通过include包含到内容部分中。顶部和底部文件都放置到inc文件夹中。

    不需要登录就可以访问的页面直接放置到根目录,需要登录才能进行的操作全部放到/user/xxx文件夹中。

    管理人员所有能访问的功能全部放到/admin/xxx文件夹中。

    以上规范目录结构叫做:约定优于配置。

    约定相应的文件名:

    和用户相关的都使用user_开头:user_registerInput.jsp表示用户的注册页面,user_delete.jsp。

    所有需要进行输入的页面都是由xxxInput来表示:user_registerInput.jsp,msg_addInput.jsp,comment_addInput.jsp。

    和留言相关的都是由msg开头。

    header.jsp

<%@page import="org.pm.msg.model.User"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%
	User u = (User)session.getAttribute("loginUser");
%>
<div style="text-align: right;border-bottom: 1px solid #000;">
<%
	if(u!=null) {
%>
	欢迎[<%=u.getNickname() %>]使用系统!
	<a href="<%=request.getContextPath()%>/admin/user/list.jsp ">用户管理</a>
	&nbsp;<a href="<%=request.getContextPath()%>/admin/user/updateSelfInput.jsp">修改个人信息</a>
	&nbsp;<a href="<%=request.getContextPath()%>/logout.jsp">退出系统</a>
<%
	}
%>
	&nbsp;<a href="<%=request.getContextPath()%>/loginInput.jsp">用户登录</a>
	&nbsp;<a href="<%=request.getContextPath()%>/msg/list.jsp">留言管理</a>
</div>

    需要注意的是:不能有html的标签。

    bottom.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<hr/>
<h3 style="text-align:center">CopyRight 2016-2020</h3>

    error.jsp:通过error.jsp统一显示相应的错误信息。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>错误信息</title>
</head>
<body>
	<%
		//异常信息
		Exception e = (Exception)request.getAttribute("exception");
		//返回的连接地址
		String url = (String)request.getAttribute("url");
		//返回的连接说明
		String urlIntro = (String)request.getAttribute("urlIntro");
	%>
</body>
</html>

    e,url,urlIntro都应该是在处理页面通过服务器端跳转传递过来的。

    3.2、创建过滤器

    字符编码过滤器

public class CharFilter implements Filter {
	
	private String encoding;

	@Override
	public void destroy() {
		
	}

	@Override
	public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
			throws IOException, ServletException {
		req.setCharacterEncoding(encoding);
		chain.doFilter(req, resp);
	}

	@Override
	public void init(FilterConfig cfg) throws ServletException {
		encoding = cfg.getInitParameter("encoding");
		if(encoding==null||"".equals(encoding.trim())) {
			encoding = "UTF-8";
		}
	}

}

    web.xml配置:

  <filter>
    <filter-name>CharEncoding</filter-name>
    <filter-class>org.pm.msg.filter.CharFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>CharEncoding</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  <filter>

    3.3、用户管理部分

    用户添加:用户添加需要两个页面,添加的输入页面和添加的处理页面。

    添加的输入页面中需要相应的form表单(user_registerInput.jsp)。

	<!-- 提交的处理页面是user_register.jsp -->
	<form action="add.jsp" method="post">
		<table align="center" border="1" width="500">
			<tr>
				<td>用户名称:</td>
				<td>
					<input type="text" name="username" value="<%=request.getParameter("username") %>"/>
					<%=ValidateUtil.showError(request, "username") %>
				</td>
			</tr>
			<tr>
				<td>用户密码:</td><td><input type="password" name="password"/>
					<%=ValidateUtil.showError(request, "password") %>
				</td>
			</tr>
			<tr>
				<td>用户昵称:</td><td><input type="text" name="nickname" value="<%=request.getParameter("nickname") %>"/>
					<%=ValidateUtil.showError(request, "nickname") %>
				</td>
			</tr>
			<tr>
				<td colspan="2"><input type="submit" value="添加用户"/></td>
			</tr>
		</table>
	</form>

    在添加的处理页面中对提交的数据进行控制(user_register.jsp)。

<%
	//获取表单的数据
	String username = request.getParameter("username");
	String password = request.getParameter("password");
	String nickname = request.getParameter("nickname");
	boolean validate = ValidateUtil.validateNull(request, new String[]{"username","password","nickname"});
	if(!validate) {
	%>
		<jsp:forward page="addInput.jsp"/>
	<% 
	}
	User user = new User();
	user.setNickname(nickname);
	user.setPassword(password);
	user.setUsername(username);
	user.setStatus(0);
	user.setType(0);
	IUserDao userDao = DAOFactory.getUserDao();
	try {
		//调用DAO完成对用户的存储
		userDao.add(user);
		response.sendRedirect("list.jsp");
		return;
	} catch(MsgException e) {
		//发现出错之后将相应的错误对象,和在错误页面中要处理的连接通过
		//request作用域传递过去。并且通过服务器端跳转到错误页面。
		request.setAttribute("exception", e);
		request.setAttribute("url", request.getContextPath()+"/user_registerInput.jsp");
		request.setAttribute("urlIntro", "返回用户注册页面");
%>
	<jsp:forward page="/inc/error.jsp"></jsp:forward>

  

    用户登录:时序图

       

    需要页面:user_loginInput.jsp,只用增加相应的登录表单即可:user_login.jsp。

	<%
		try {
			//1、获取用户名和密码
			String username = request.getParameter("username");
			String password = request.getParameter("password");
			//2、创建UserDao
			IUserDao ud = DAOFactory.getUserDao();
			//3、调用UserDao的login()方法
			User u = ud.login(username, password);
			//5、验证成功,让用户对象存储到session中
			session.setAttribute("loginUser", u);
			//6、跳转到list.jsp(客户端)
			response.sendRedirect(request.getContextPath()+"/admin/user/list.jsp");
		} catch(MsgException e) {
			//4、捕获异常
			request.setAttribute("exception", e);
			request.setAttribute("url", request.getContextPath()+"/user_loginInput.jsp");
			request.setAttribute("urlIntro", "返回登录页面");
	%>
		<jsp:include page="/inc/error.jsp"></jsp:include>
		<h2 style="color: red">发生错误:<%=e.getMessage() %></h2>
	<% 	
		}
	%>

    登录检测

    页面控制

    1、控制header.jsp,根据用户的类型显示相应的菜单

<%
	//从session中获取用户
	User u = (User)session.getAttribute("loginUser");
%>
<div style="text-align: right;border-bottom: 1px solid #000;">
<%
	//判断如果用户为空说明没有登录,当登录成功之后会向session中保存User的值,此时User对象就不为空了
	if(u!=null) {
%>
	<!-- 登录成功显示的信息 -->
	欢迎[<%=u.getNickname() %>]使用系统!
<% 
	if(u!=null&&u.getType()==1) {
%>
	<!-- 如果是管理人员要显示的信息 -->
	<a href="<%=request.getContextPath()%>/admin/user/list.jsp ">用户管理</a>
<% 
	}
%>
	&nbsp;<a href="<%=request.getContextPath()%>/admin/user/updateSelfInput.jsp">修改个人信息</a>
	&nbsp;<a href="<%=request.getContextPath()%>/logout.jsp">退出系统</a>
<%
	}
%>
	<!-- 没有登录显示的信息 -->
	&nbsp;<a href="<%=request.getContextPath()%>/loginInput.jsp">用户登录</a>
	&nbsp;<a href="<%=request.getContextPath()%>/msg/list.jsp">留言管理</a>
</div>

    过滤器控制

    通过过滤器检测用户的登录情况:如果用户没有登录直接跳转到登录页面。

    1、创建相应的LoginFilter来检查登录

	@Override
	public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
			throws IOException, ServletException {
		//为了更好的操作request和response,此处将其转化为HttpServletRequest和HttpServletResponse
		HttpServletRequest request = (HttpServletRequest)req;
		HttpServletResponse response = (HttpServletResponse)resp;
		//获取session中的数据,request.getSession()可以获取session对象
		User user = (User)request.getSession().getAttribute("loginUser");
		if(user==null) {
			//如果用户没有登录直接跳转
			response.sendRedirect(request.getContextPath()+"/user_loginInput.jsp");
			return;
		}
		chain.doFilter(request, response);
	}

    2、发布filter

  <filter>
  	<filter-name>LoginFilter</filter-name>
  	<filter-class>org.pm.msg.filter.LoginFilter</filter-class>
  </filter>
  <!-- 注意:不仅仅需要对于user文件夹,对admin文件夹也要控制 -->
  <filter-mapping>
  	<filter-name>LoginFilter</filter-name>
  	<url-pattern>/user/*</url-pattern>
  </filter-mapping>
  <filter-mapping>
  	<filter-name>LoginFilter</filter-name>
  	<url-pattern>/admin/*</url-pattern>
  </filter-mapping>

    如果用户不是系统管理员而访问了admin中的信息直接跳转到error.jsp页面。

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		HttpServletRequest req = (HttpServletRequest)request;
		HttpServletResponse resp = (HttpServletResponse)response;
		HttpSession session = req.getSession();
		User u = (User)session.getAttribute("loginUser");
		if(u.getType()!=1) {
                        //如果用户不是超级管理员直接跳转
			resp.sendRedirect(req.getContextPath()+"/loginInput.jsp");
			return;
		}else{
			chain.doFilter(request, response);
		}
	}

        filter配置:

  <filter>
    <filter-name>AdminCheckFilter</filter-name>
    <filter-class>org.pm.msg.filter.AdminCheckFilter</filter-class>
  </filter>
  <!-- 只用过滤管理功能 -->
  <filter-mapping>
    <filter-name>AdminCheckFilter</filter-name>
    <url-pattern>/admin/*</url-pattern>
  </filter-mapping>

    过滤器的时序图:

    退出系统

    1、需要在哪个目录中存储?

    由于退出系统应该是在登录之后才能使用,所以需要放置到/user/logout.jsp中使用。

    2、操作步骤:

    session.invalidate();//使session失效,跳转到msg_list.jsp。

    用户列表:用户列表只能管理人员才能使用。用户列表的文件名/admin/user_list.jsp。

    时序图:

    

    基于分页的用户列表

    1、在页面需要获取分页数量和一共多少条记录,就必须新建一个分页对象来存储这些信息。

    

    具体DAO代码:

	@Override
	public Pager<User> list(String condition) {
		//pageOffset表示从第几个记录开始读取数据
		int pageOffset = SystemContext.getPageOffset();
		//pageSize表示每页显示多少条记录
		int pageSize = SystemContext.getPageSize();
		Connection con = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		List<User> users = new ArrayList<User>();
		User u = null;
		Pager<User> pages = new Pager<User>();
		try {
			con = DBUtil.getConnention();
			String sql = "select * from t_user";
			//获取总记录数
			String sqlCount = "select count(*) from t_user";
			if(condition!=null||!"".equals(condition)) {
				sql+=" where username like '%"+condition+"%' or nickname like '%"+condition+"%'";
				sqlCount+=" where username like '%"+condition+"%' or nickname like '%"+condition+"%'";
			}
			sql+=" limit ?,?";
			ps = con.prepareStatement(sql);
			ps.setInt(1, pageOffset);
			ps.setInt(2, pageSize);
			rs = ps.executeQuery();
			while(rs.next()) {
				u = new User();
				u.setId(rs.getInt("id"));
				u.setNickname(rs.getString("nickname"));
				u.setPassword(rs.getString("password"));
				u.setUsername(rs.getString("username"));
				u.setStatus(rs.getInt("status"));
				u.setType(rs.getInt("type"));
				users.add(u);
			}
			DBUtil.close(rs);
			DBUtil.close(ps);
			ps = con.prepareStatement(sqlCount);
			rs = ps.executeQuery();
			int totalRecord = 0;
			while(rs.next()) {
				totalRecord = rs.getInt(1);
			}
                        //根据总记录数来获取分页数
			int totalPage = (totalRecord-1)/pageSize+1;
			pages.setPageOffset(pageOffset);
			pages.setPageSize(pageSize);
			pages.setTotalPage(totalPage);
			pages.setTotalRecord(totalRecord);
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			DBUtil.close(rs);
			DBUtil.close(ps);
			DBUtil.close(con);
		}
		pages.setDatas(users);
		return pages;
	}

    具体的页面代码:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib prefix="pg" uri="http://jsptags.com/tags/navigation/pager" %>
<%
	int items = Integer.parseInt(request.getParameter("items"));
	String params = request.getParameter("params");
	//System.out.println(params);
	if(params==null) params="";
	String[] ps = params.split(",");
%>
<pg:pager maxPageItems="15" items="<%=items %>" export="curPage=pageNumber">
<%
	for(String p:ps) {
%>
	<pg:param name="<%=p %>"/>
<%
	}
%>
	<pg:last>
		共<%=items %>条记录,共<%=pageNumber %>页,当前第<%=curPage %>页。
	</pg:last>
	<pg:first>
		<a href="<%=pageUrl%>">首页</a>
	</pg:first>
	<pg:prev>
		<a href="<%=pageUrl%>">上一页</a>
	</pg:prev>
	<pg:pages>
	<%
		if(curPage==pageNumber) {
	%>
		[<%=pageNumber %>]
	<% 
		} else {
	%>
		<a href="<%=pageUrl%>"><%=pageNumber %></a>
	<%
		}
	%>
	</pg:pages>
	<pg:next>
		<a href="<%=pageUrl %>">下一页</a>
	</pg:next>
	<pg:last>
		<a href="<%=pageUrl%>">尾页</a>
	</pg:last>
</pg:pager>

    用户删除、停用

    用户删除时序图:

    

    /admin/user_delete.jsp

<%@include file="/inc/adminCheck.jsp" %>
<%
	try {
		//获取传入的id
		int id = Integer.parseInt(request.getParameter("id"));
		IUserDao userDao = DAOFactory.getUserDao();
		//删除用户
		userDao.delete(id);
		//返回用户列表页面
		response.sendRedirect("list.jsp");
	} catch(MsgException e) {
%>
	<h2 style="color:red">发生错误:<%=e.getMessage() %></h2>
<%
	}
%>

    用户停用时序图:

  

	<%
		//获取传入的id
		int id = Integer.parseInt(request.getParameter("id"));
		IUserDao ud = DAOFactory.getUserDao();
		//获取用户
		User u = ud.load(id);
		//判断用户当前的状态,如果为停用就设置为启用,否则相反
		if(u.getStatus()==0) {
			u.setStatus(1);
		} else {
			u.setStatus(0);
		}
		//调用dao的update()方法
		ud.update(u);
		//返回用户列表页面
		response.sendRedirect("list.jsp");
	%>

    用户信息和密码修改

    1、修改密码时序图:

    1.1、在updatePwdInput.jsp中获取当前用户信息

<%@include file="/inc/adminCheck.jsp" %>
<%
	int id = Integer.parseInt(request.getParameter("id"));
	IUserDao userDao = DAOFactory.getUserDao();
	//获取要修改密码的用户
	User u = userDao.load(id);
%>
</head>
<body>
<!--  <%=application.getRealPath("/") %> -->
	<jsp:include page="inc.jsp">
		<jsp:param value="更新" name="op"/>
	</jsp:include>
	<form action="update.jsp" method="post">
	<!-- 通过隐藏域来传递要修改用户的id -->
	<input type="hidden" name="id" value="<%=u.getId()%>"/>
		<table align="center" border="1" width="500">
			<tr>
				<td>用户名称:</td>
				<td>
					<%=u.getUsername() %>
				</td>
			</tr>
			<tr>
				<td>用户密码:</td><td><input type="password" name="password" value="<%=u.getPassword() %>"/>
					<%=ValidateUtil.showError(request, "password") %>
				</td>
			</tr>
			<tr>
				<td>用户昵称:</td><td><input type="text" name="nickname" value="<%=u.getNickname() %>"/>
					<%=ValidateUtil.showError(request, "nickname") %>
				</td>
			</tr>
			<tr>
				<td colspan="2"><input type="submit" value="更新用户"/>
					<input type="reset" value="重置信息"/>
				</td>
			</tr>
		</table>
	</form>
</body>
</html>

    1.2、在updatePwd.jsp页面中获取要修改的id和密码数据
    1.3、检查两次输入的密码是否正确
    1.4、通过DAO获取User对象,并且判断user对象的原始密码是否输入正确,如果不正确抛出异常,返回到修改密码页面。
    1.5、修改密码,返回到留言列表页面

<%@include file="/inc/adminCheck.jsp" %>
<%
	//获取id和相应的密码信息
	int id = Integer.parseInt(request.getParameter("id"));
	String password = request.getParameter("password");
	String nickname = request.getParameter("nickname");
	boolean validate = ValidateUtil.validateNull(request, new String[]{"password","nickname"});
	if(!validate) {
	%>
		<jsp:forward page="updateInput.jsp"/>
	<% 
	}
	IUserDao userDao = DAOFactory.getUserDao();
	User user = userDao.load(id);
	user.setNickname(nickname);
	user.setPassword(password);
	try {
		userDao.update(user);
		response.sendRedirect("list.jsp");
		return;
	} catch(MsgException e) {
%>
	<h2 style="color: red">发生错误:<%=e.getMessage() %></h2>
<%
	}
%>

    2、修改用户信息时序图:

    

    2.1、user_updateInput.jsp
   需要在session中获取用户的基本信息,在表单中设定相应的信息。

3.4、特殊知识部分

    ThreadLocal对象的使用

    ThreadLocal可以在整个线程中有效,所以可以将一些表示层的变量存储到ThreadLocal中,此时可以在
    DAO层获取ThreadLocal中的数据,这样可以省略一些DAO层所需要的公共参数。

    1、ThreadLocal的定义:定义一个类叫做SystemContext,在这个类中定义一系列的static的ThreadLocal
    变量。并且提供相应的get和set和remove方法。

public class SystemContext {
	//需要传递的线程对象
	private static ThreadLocal<Integer> pageSize = new ThreadLocal<Integer>();
	private static ThreadLocal<Integer> pageIndex = new ThreadLocal<Integer>();
	private static ThreadLocal<Integer> pageOffset = new ThreadLocal<Integer>();
	
	//特别注意这种写法
	public static void setPageOffset(int _pageOffset) {
		pageOffset.set(_pageOffset);
	}
	//特别注意这种写法
	public static int getPageOffset() {
		return pageOffset.get();
	}
	//特别注意这种写法
	public static void removePageOffset() {
		pageOffset.remove();
	}

    2、创建相应的filter在表示层传递信息时将这些数据写入到ThreadLocal中。

		try {
			int pageOffset = 0;
			int pageSize = 15;
			try {
				pageOffset = Integer.parseInt(req.getParameter("pager.offset"));
			} catch (NumberFormatException e) {
			}
			//写入需要传递的数据信息
			SystemContext.setPageOffset(pageOffset);
			SystemContext.setPageSize(pageSize);
			chain.doFilter(req, resp);
		} finally {
			//这个请求运行结束之后,需要删除ThreadLocal中的值
			SystemContext.removePageOffset();;
			SystemContext.removePageSize();
		}

    3、在DAO中获取相应的ThreadLocal对象进行处理。

		//pageOffset表示从第几个记录开始读取数据
		int pageOffset = SystemContext.getPageOffset();
		//pageSize表示每页显示多少条记录
		int pageSize = SystemContext.getPageSize();

    使用Pager-taglib实现分页:使用Pager-taglib可以帮助我们快速开发分页处理。

    1、将相应jar包拷贝到lib目录(pager-taglib.jar)。

    2、在jsp页面引入相应的分页标签库。

<%@taglib prefix="pg" uri="http://jsptags.com/tags/navigation/pager" %>

    3、使用分页标签来处理分页:所有的分页信息都要放置在<pg:pager>中,这个标签中有一些非常重要的
    属性可以设置,items:表示有多少条记录。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib prefix="pg" uri="http://jsptags.com/tags/navigation/pager" %>
<%
	int items = Integer.parseInt(request.getParameter("items"));
	String params = request.getParameter("params");
	//System.out.println(params);
	if(params==null) params="";
	String[] ps = params.split(",");
%>
<!-- items:总记录数,maxPageItems:每页显示多少条,curPage=pageNumber:
	将导出的参数pageNumber重新命名为curPage,用来表示当前页 -->
<pg:pager maxPageItems="15" items="<%=items %>" export="curPage=pageNumber">
<%
	for(String p:ps) {
%>
	<pg:param name="<%=p %>"/>
<%
	}
%>
	<pg:last>
		共<%=items %>条记录,共<%=pageNumber %>页,当前第<%=curPage %>页。
	</pg:last>
	<!-- pg:first表示首页,会默认导出pageUrl -->
	<pg:first>
		<a href="<%=pageUrl%>">首页</a>
	</pg:first>
	<pg:prev>
		<a href="<%=pageUrl%>">上一页</a>
	</pg:prev>
	<pg:pages>
	<%
		//curPage是在pg:pager中导出的,pageNumber表示页码,curPage是pg:pager
		//中的pageNumber表示的是当前页
		if(curPage==pageNumber) {
	%>
		[<%=pageNumber %>]
	<% 
		} else {
	%>
		<a href="<%=pageUrl%>"><%=pageNumber %></a>
	<%
		}
	%>
	</pg:pages>
	<pg:next>
		<a href="<%=pageUrl %>">下一页</a>
	</pg:next>
	<pg:last>
		<a href="<%=pageUrl%>">尾页</a>
	</pg:last>
</pg:pager>

    具体运用:

		<!-- 传递多个参数可以用,分隔 -->
		<jsp:include page="/inc/pager.jsp">
			<!-- value:总记录数 -->
			<jsp:param value="<%=pages.getTotalRecord() %>" name="items"/>
			<jsp:param value="condition,status,type" name="params"/>
		</jsp:include>

    pager.jsp页面

<%
	//获取需要分页页面所传递过来的参数
	int items = Integer.parseInt(request.getParameter("items"));
	String params = request.getParameter("params");
	//System.out.println(params);
	if(params==null) params="";
	String[] ps = params.split(",");
%>

    使用Tomcat数据源来获取Connection

    1、在Tomcat的server.xml中的context中加入如下信息:

      <Context docBase="msg02" path="/msg01" reloadable="true" source="org.eclipse.jst.jee.server:msg02">
      	<!-- name="jdbc/msg":表示数据源的名称,msg为自定义名称 -->
      	<Resource name="jdbc/msg"
            auth="Container"
            type="javax.sql.DataSource"
            <!-- 连接数据库的用户名 -->
            username="pm"
            <!-- 连接数据库的密码 -->
            password="pm123"
            <!-- 数据库驱动的名称,不同数据库名称不一样
            	这里显示的是MySQL的数据库驱动 -->
            driverClassName="com.mysql.jdbc.Driver"
            <!-- 数据库的连接字符串 -->
            url="jdbc:mysql://localhost:3306/pm_msg?useSSL=false"
            maxTotal="20"
            maxIdle="20"/>
      </Context>

    2、读取Tomcat的JDBC数据源

		//数据库连接池方式
		Connection con = null;
		try {
			Context initCtx = new InitialContext();
			Context envCtx = (Context) initCtx.lookup("java:comp/env");
			//jdbc/msg:数据源的名称
			DataSource ds = (DataSource)
			  envCtx.lookup("jdbc/msg");
			con = ds.getConnection();
		} catch (NamingException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return con;

    3、注意事项:由于Tomcat的数据源是要依附于Tomcat才能使用的,如果没有使用Tomcat启动就无法创
    建相应的Context对象,也就无法获取Connection,所以什么时候才能使用这种方式呢?

    在开发过程中使用JDBC数据源,因为要做相应的单元测试,在项目开发完毕之后再使用Tomcat的数据
    源。特别注意,也可以使用一些开源的如DBCP等数据源来完成这个操作。

    工厂模式的使用

    1、使用的原因:整个程序中创建DAO都是使用UserDao dao = new UserDao();来完成创建的,如此就
    导致dao对象完全依赖于实现类了,将来如果希望修改实现类,所有的dao的调用都需要进行修改。

    2、简单工厂

public class DAOFactory {
	/**
	 * 通过DAOFactory来创建dao对象,此时当需要修改相应的dao实现时,
	 * 只用修改该段代码就完成了所有的修改
	 * @return
	 */
	public static IUserDao getUserDao() {
		return new UserDao();
	}

    3、OCP-->开放关闭原则:指的是我们对修改是关闭的,对添加是开放的。

    事务处理:事务指的是将所有的对数据库的读写操作保持一定的原子性,要所有操作执行完之后才进行数
    据库的更新操作,如果在运行中出现异常,需要回滚。

    1、对于MySQL而言,需要INNODB的表类型才能支持数据库的事务操作。

    2、在JDBC中数据会默认提交,需要设置自动提交为false。

     con.setAutoCommit(false);

    3、在所有的更新操作完成之后使用con.commit()来提交事务。

		try {
			con = DBUtil.getConnention();
			//开启事务,需要执行con.commit()之后才会提交事务
			con.setAutoCommit(false);
			//删除评论
			String sql = "delete from t_comment where msg_id=?";
			ps = con.prepareStatement(sql);
			ps.setInt(1, id);
			ps.executeUpdate();
			/*int a = 10;
			if(a==10) {
				throw new MsgException("aaaaaaaa");
			}*/
			sql = "delete from t_msg where id=?";
			DBUtil.close(ps);
			ps = con.prepareStatement(sql);
			ps.setInt(1, id);
			ps.executeUpdate();
			//提交事务
			con.commit();
		} catch (SQLException e) {
			e.printStackTrace();
			try {
				//回滚事务
				con.rollback();
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
		} finally {
			DBUtil.close(ps);
			DBUtil.close(con);
		}

    JUnit简介:JUnit表示单元测试

    1、创建:

   

    2、使用单元测试类:
    @Before-->表示每一个单元测试之前都会执行
    @BeforeClass-->初始化类时会执行一次(必须是static)
    @Test--->表示可以独立运行的单元测试类

	private IUserDao userDao;
	//执行任意的测试单元时都会执行before中的内容
	//@BeforeClass 指的是只会执行一次
	@Before
	public void init() {
		userDao = DAOFactory.getUserDao();
	}
	
	@Test
	public void testAdd() {
		User user = new User();
		user.setNickname("垃圾");
		user.setPassword("123");
		user.setType(0);
		user.setUsername("lj");
		userDao.add(user);
	}

3.5、mybatis框架(单独一篇介绍)

3.6、留言管理部分

    留言添加:在user文件中

    留言列表

    

    显示留言信息

    

    修改留言

    1、通过msg_show.jsp页面进入修改界面

    2、只能修改自己所发布的文章(如果不是自己所发布的文章,不显示编辑按钮)

<%
  if(ValidateUtil.checkAdminOrSelf(session, msg.getUserId())) {
%>
<a href="<%=request.getContextPath()%>/admin/msg/updateInput.jsp?id=<%=msg.getId()%>">更新</a>
&nbsp;&nbsp;<a href="<%=request.getContextPath()%>/admin/msg/delete.jsp?id=<%=msg.getId()%>">删除</a>
<% 
  }
%>

    留言管理

    

    留言管理功能是为注册用户提供,特点:

    1、在该页面会显示自己的留言信息和自己的评论信息

    2、如果是超级管理员会显示全部的信息

    3、在该页面中进行相应的留言的删除操作

    4、删除留言时会自动将评论完全删除

    留言删除

    

    3.7、xheditor:HTML编辑器

    如何引入xheditor:

    1、下载xheditor-1.2.2.zip

    2、解压后将xheditor文件夹拷贝到根目录

    3、引入xheditor的js文件

<script type="text/javascript" src="<%=request.getContextPath() %>/xheditor/jquery/jquery-1.4.4.min.js"></script>
<script type="text/javascript" src="<%=request.getContextPath() %>/xheditor/xheditor-1.2.2.min.js"></script>
<script type="text/javascript" src="<%=request.getContextPath() %>/xheditor/xheditor_lang/zh-cn.js"></script>

    4、为相应的textarea增加一个id

<td><textarea rows="10" cols="100" id="content" name="content"></textarea></td>

    5、导入xheditor

<script type="text/javascript">
	$(function(){
		$("#content").xheditor({
			tools:'simple',
		});
	});
</script>

    xheditor的配置:content:表示textarea的id,skin:表示皮肤,tools:表示工具栏的显示样式,emots:
        表示表情包(特别注意写法,写错的话无法显示编辑器)。

<script type="text/javascript">
	$(function(){
		$("#content").xheditor({
			tools:'full',
			skin:'o2007blue',
			emots:{msn:{name:'MSN',count:40,width:22,height:22,line:8}}
		});
	});
</script>

    3.8、评论管理

    1、评论添加

    

    2、评论列表:显示,评论内容、评论时间、评论人员

    

    3、评论管理

    

4、该版本的问题

    1、在大量的jsp页面中嵌入了太多的Java脚本,如此开发后台的程序人员和开发前台的程序人员无法很好
    的交流,所以应该将Java的代码和jsp的标签代码进行更好的分类处理。

    2、在调用DAO的时候都是需要使用new xxxDao()来创建DAO,虽然已经使用了相应的工厂模式,并且
    程序的代码也得到一定程度的优化,但是依然还是要依赖于实现类。

    3、没有实现文件的上传操作。

    4、没有实现相应的文章的搜索操作(该操作应该是可以独立完成的)。

以上问题将会在留言板版本2中解决。

展开阅读全文
打赏
0
1 收藏
分享
加载中
更多评论
打赏
0 评论
1 收藏
0
分享
OSCHINA
登录后可查看更多优质内容
返回顶部
顶部