MVC三层架构(代码整体以此分层编写)
- 整体的流程与代码编写思路:
建议是从后往前
写,便于调试与debug,先编写Dao层
,主要负责与数据库交互,编写sql语句等。然后编写Servicce层
,主要负责调用Dao层,再编写Servlet层
,其也是主要调用Service和前端的一些数据交互,比如resquet和response等。
基本架构
项目搭建准备工作
1- 4
5 创建项目包结构
6-7
package com.tong.dao;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
/**
* 操作数据库的基类--静态类
* @author Administrator
*
*/
public class BaseDao {
static{
//静态代码块,在类加载的时候执行
init();
}
private static String driver;
private static String url;
private static String user;
private static String password;
//初始化连接参数,从配置文件里获得
public static void init(){
Properties params=new Properties();
String configFile = "db.properties";
InputStream is=BaseDao.class.getClassLoader().getResourceAsStream(configFile);
try {
params.load(is);
} catch (IOException e) {
e.printStackTrace();
}
driver=params.getProperty("driver");
url=params.getProperty("url");
user=params.getProperty("user");
password=params.getProperty("password");
}
/**
* 获取数据库连接
* @return
*/
public static Connection getConnection(){
Connection connection = null;
try {
Class.forName(driver);
connection = DriverManager.getConnection(url, user, password);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return connection;
}
/**
* 查询操作
* @param connection
* @param pstm
* @param rs
* @param sql
* @param params
* @return
*/
public static ResultSet execute(Connection connection,PreparedStatement pstm,ResultSet rs, String sql,Object[] params) throws Exception{
pstm = connection.prepareStatement(sql);
//向sql中的占位符加载参数Object[] params
for(int i = 0; i < params.length; i++){
pstm.setObject(i+1, params[i]);
}
rs = pstm.executeQuery();
return rs;
}
/**
* 更新操作
* @param connection
* @param pstm
* @param sql
* @param params
* @return
* @throws Exception
*/
public static int execute(Connection connection,PreparedStatement pstm, String sql,Object[] params) throws Exception{
int updateRows = 0;
pstm = connection.prepareStatement(sql);
for(int i = 0; i < params.length; i++){
pstm.setObject(i+1, params[i]);
}
updateRows = pstm.executeUpdate();
return updateRows;
}
/**
* 释放资源
* @param connection
* @param pstm
* @param rs
* @return
*/
public static boolean closeResource(Connection connection,PreparedStatement pstm,ResultSet rs){
boolean flag = true;
if(rs != null){
try {
rs.close();
rs = null;//GC回收,如果rs.close();没关上,让其等于null,使垃圾回收器自动回收。
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
flag = false;
}
}
if(pstm != null){
try {
pstm.close();
pstm = null;//GC回收
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
flag = false;
}
}
if(connection != null){
try {
connection.close();
connection = null;//GC回收
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
flag = false;
}
}
return flag;
}
}
8 导致静态资源
- 放在webapp目录下,因为是网站资源
登录功能实现
1.编写前端页面
- login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>系统登录 - 超市订单管理系统</title>
<link type="text/css" rel="stylesheet" href="${pageContext.request.contextPath }/css/style.css" />
<script type="text/javascript">
/* if(top.location!=self.location){
top.location=self.location;
} */
</script>
</head>
<body class="login_bg">
<section class="loginBox">
<header class="loginHeader">
<h1>超市订单管理系统</h1>
</header>
<section class="loginCont">
<form class="loginForm" action="${pageContext.request.contextPath }/login.do" name="actionForm" id="actionForm" method="post" >
<div class="info">${
error }</div>
<div class="inputbox">
<label for="userCode">用户名:</label>
<input type="text" class="input-text" id="userCode" name="userCode" placeholder="请输入用户名" required/>
</div>
<div class="inputbox">
<label for="userPassword">密码:</label>
<input type="password" id="userPassword" name="userPassword" placeholder="请输入密码" required/>
</div>
<div class="subBtn">
<input type="submit" value="登录"/>
<input type="reset" value="重置"/>
</div>
</form>
</section>
</section>
</body>
</html>
2.设置首页
<!--设置欢迎界面-->
<welcome-file-list>
<welcome-file>login.jsp</welcome-file>
</welcome-file-list>
3.编写Dao层用户登录的接口
- Dao层(数据持久层)负责操作数据库,业务(比如用户登录,比对账号密码)有业务层负责。
public User getLoginUser(Connection connection, String userCode)throws Exception;
4.编写Dao接口实现类
public User getLoginUser(Connection connection, String userCode) throws Exception {
// TODO Auto-generated method stub
PreparedStatement pstm = null;
ResultSet rs = null;
User user = null;
if(null != connection){
String sql = "select * from smbms_user where userCode=?";
Object[] params = {
userCode};
rs = BaseDao.execute(connection, pstm, rs, sql, params);
if(rs.next()){
user = new User();
user.setId(rs.getInt("id"));
user.setUserCode(rs.getString("userCode"));
user.setUserName(rs.getString("userName"));
user.setUserPassword(rs.getString("userPassword"));
user.setGender(rs.getInt("gender"));
user.setBirthday(rs.getDate("birthday"));
user.setPhone(rs.getString("phone"));
user.setAddress(rs.getString("address"));
user.setUserRole(rs.getInt("userRole"));
user.setCreatedBy(rs.getInt("createdBy"));
user.setCreationDate(rs.getTimestamp("creationDate"));
user.setModifyBy(rs.getInt("modifyBy"));
user.setModifyDate(rs.getTimestamp("modifyDate"));
}
//connection设成null不让其关闭,是因为后面业务层还需要数据库连接。
BaseDao.closeResource(null, pstm, rs);
}
return user;
}
5.业务层接口
- 业务层都会调用Dao层,来获取业务所需的数据。
public interface UserService {
//用户登录
public User login(String userCode, String userPassword);
}
6.业务层实现类
- 因为业务层要调用Dao层(来获取数据库的数据),调用Dao层,就需要给它传参数,则connection此时传给Dao层,所以connection对象在业务层创建。
- 业务层存在事务,失败了会回滚。,所以connection对象在业务层创建。
public class UserServiceImpl implements UserService{
private UserDao userDao;
public UserServiceImpl(){
userDao = new UserDaoImpl();
}
public User login(String userCode, String userPassword) {
// TODO Auto-generated method stub
Connection connection = null;
User user = null;
try {
connection = BaseDao.getConnection();
user = userDao.getLoginUser(connection, userCode);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
BaseDao.closeResource(connection, null, null);
}
//匹配密码
if(null != user){
if(!user.getUserPassword().equals(userPassword))
user = null;
}
return user;
}
}
7.编写servlet
- servlet是控制层,用来调用业务层
- 控制层的作用:接受用户的请求交给业务层去做,这里用户的请求是登录(输入用户名和密码请求登录),业务层要做的是在数据库中匹配输入的用户名和密码。
public class LoginServlet extends HttpServlet {
//servlet:控制层,接收用户的请求,调用业务层代码。
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//从前端获取用户名和密码(接收用户的请求)
String userCode = req.getParameter("userCode");
String userPassword = req.getParameter("userPassword");
//接收请求后需处理业务,业务是:和数据库中的数据进行对比,所以需调用业务层
UserServiceImpl userService = new UserServiceImpl();
User user = userService.login(userCode, userPassword);//这里已经把登录的人给查出来了
if(user != null){
//查有此人,可以登录
//将用户的信息放入Session中
req.getSession().setAttribute(Constant.USER_SESSION , user);
//跳转主页(跳转到另一个页面,地址变了,所以用重定向)
resp.sendRedirect("jsp/frame.jsp");
}else{
//查无此人,无法登录
//转发会登录页面,顺带提示它,用户名或密码错误。((跳转到本页面,只是在本页面加了些信息(用户名或密码错误),地址没变,所以用请求转发))
req.setAttribute("error" , "用户名或密码错误");//请求可以携带数据
req.getRequestDispatcher("login.jsp").forward(req , resp);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
8.注册servlet
<!--servlet-->
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>com.tong.servlet.user.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/login.do</url-pattern>
</servlet-mapping>
整体流程
前端一启动,执行login.jsp(设置成了首页),直接到web.xml中设置的login.do(servlet映射),调用对应的控制器
servlet(LoginServlet),servlet中会调用业务(UserServiceImpl),然后业务会调用想用的Dao(UserDaoImpl)来获取数据。
登录功能优化
注销功能:
思路:移除session,返回登录界面;
- LogoutServlet
public class LogoutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.getSession().removeAttribute(Constant.USER_SESSION);
resp.sendRedirect(req.getContextPath() + "/login.jsp");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
注册
<!--注销-->
<servlet>
<servlet-name>LogoutServlet</servlet-name>
<servlet-class>com.tong.servlet.user.LogoutServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LogoutServlet</servlet-name>
<url-pattern>/jsp/logout.do</url-pattern>
</servlet-mapping>
登陆拦截优化
为什么要登陆拦截优化
- 正常登陆后,将登录界面的网址复制下来,在注销后的登录界面,黏贴复制的网址,在没填用户名和密码的前提下,进入了系统。所以需要登录拦截
拦截判断的条件是
session中有无user这个属性,因为在用户注销,或还没登录
的情况下,session中没有user这个属性,如果没有,说明不是正常登录,进行拦截;只有当正常登录时,会创建session中user这个属性,此时可以正常登录。
注销后,黏贴网址,依然能登录进来,需进行拦截。
具体步骤
编写一个过滤器,并注册
public class SysFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
//过滤器,从session中获取用户
User user = (User) request.getSession().getAttribute(Constant.USER_SESSION);
if(user == null){
//session已经被移除,或者用户注销,或还没登录
response.sendRedirect("error.jsp");
}else{
chain.doFilter(req , resp);
}
}
public void destroy() {
}
}
<!--用户登录过滤器-->
<filter>
<filter-name>SysFilter</filter-name>
<filter-class>com.tong.filter.SysFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SysFilter</filter-name>
<url-pattern>/jsp/*</url-pattern>
</filter-mapping>
密码修改
密码修改,需要和数据库打交道,所以还需要走dao层、service层、servlet层这一条线。
- Dao层:根据用户ID修改用户密码(update语句);
- service层:接收传过来的密码和调用Dao获取后台的密码,作对比。
- servlet层:把框里输入的新旧密码拿到,交给业务层。
1.导入前端素材
2.写项目,建议从下向上写
3.编写Dao层修改当前用户密码接口
public interface UserDao
里
//修改当前用户密码
//增删改返回的都是int类型,查找返回对应的实体类;
public int updatePwd(Connection connection, int id, String pwd)throws Exception;
4.编写Dao接口实现类
public class UserDaoImpl implements UserDao
里
public int updatePwd(Connection connection, int id, String pwd)
throws Exception {
// TODO Auto-generated method stub
int flag = 0;
PreparedStatement pstm = null;
if(connection != null){
String sql = "update smbms_user set userPassword= ? where id = ?";
Object[] params = {
pwd,id};
flag = BaseDao.execute(connection, pstm, sql, params);
BaseDao.closeResource(null, pstm, null);
}
return flag;
}
5.业务层接口
public interface UserService
里
//根据userId修改密码
public boolean updatePwd(int id, String pwd);
6.业务层接口实现类
- 因为业务层要调用Dao层(来获取数据库的数据),调用Dao层,就需要给它传参数,则connection此时传给Dao层,所以connection对象在业务层创建。
- 业务层存在事务,失败了会回滚。,所以connection对象在业务层创建。
- 因为业务层需要调用Dao层,即,这里调用userDao中的方法,这就涉及userDao的创建,有以下两种方法:
①在每个方法方法里new一个:UserDao userDao = new UserDaoImpl();
②【推荐】利用私有化 加 构造函数引入Dao:
public class UserServiceImpl implements UserService {
//引入Dao,以后spring会代替这一步
private UserDao userDao;
public UserServiceImpl(){
userDao = new UserDaoImpl();
}
。。。。(各个函数)
}
public class UserServiceImpl implements UserService
里
public boolean updatePwd(int id, String pwd) {
boolean flag = false;//使用标志位,判断密码是否修改成功。
Connection connection = null;
try{
connection = BaseDao.getConnection();
if(userDao.updatePwd(connection,id,pwd) > 0)
flag = true;
}catch (Exception e) {
e.printStackTrace();
}finally{
BaseDao.closeResource(connection, null, null);
}
return flag;
}
7.servlet记得实现复用需要提取出方法
//实现servlet复用
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getParameter("method");
if(method.equals("savepwd")){
this.updatePwd(req , resp);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
public void updatePwd(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//从session里拿Uer实体类
Object o = req.getSession().getAttribute(Constant.USER_SESSION);
String newPassword = req.getParameter("newPassword");
boolean flag = false;
if(o != null && newPassword != null && newPassword.length() != 0){
//Uer实体类和新密码newPassword都拿到了,开始调用业务层
UserServiceImpl userService = new UserServiceImpl();
flag = userService.updatePwd(((User) o).getId(), newPassword);
if(flag){
req.setAttribute("message" , "修改密码成功,请退出,使用新密码登录");
//密码修改成功,当前session。因为密码变了,session的内容自然也变了,所以需要移除,又因为密码变了需要重新登录,所以session会重新创建
req.getSession().removeAttribute(Constant.USER_SESSION);
}else {
req.setAttribute("message" , "密码修改失败");
}
}else{
req.setAttribute("message" , "新密码有问题");
}
req.getRequestDispatcher("pwdmodify.jsp").forward(req , resp);
}
}
优化密码修改使用Ajax
1.阿里巴巴的fastjson
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.61</version>
</dependency>
2.后台代码修改
public class UserServlet extends HttpServlet
里
public void pwdModify(HttpServletRequest req, HttpServletResponse resp) throws IOException {
//从Session中拿ID
Object o = req.getSession().getAttribute(Constant.USER_SESSION);
String oldpassword = req.getParameter("oldpassword");
//万能的Map : 结果集
Map<String, String> resultMap = new HashMap<String, String>();
if(null == o ){
//session过期、失效
resultMap.put("result", "sessionerror");
}else if(StringUtils.isNullOrEmpty(oldpassword)){
//旧密码输入为空
resultMap.put("result", "error");
}else{
String sessionPwd = ((User)o).getUserPassword();
if(oldpassword.equals(sessionPwd)){
resultMap.put("result", "true");
}else{
//旧密码输入不正确
resultMap.put("result", "false");
}
}
resp.setContentType("application/json");
PrintWriter outPrintWriter = resp.getWriter();
outPrintWriter.write(JSONArray.toJSONString(resultMap));
outPrintWriter.flush();
outPrintWriter.close();
}
用户管理实现(查询)
思路
导入静态资源
1.导入分页的工具类
public class PageSupport {
//当前页码-来自于用户输入
private int currentPageNo = 1;
//总数量(表)
private int totalCount = 0;
//页面容量
private int pageSize = 0;
//总页数-totalCount/pageSize(+1)
private int totalPageCount = 1;
public int getCurrentPageNo() {
return currentPageNo;
}
public void setCurrentPageNo(int currentPageNo) {
if(currentPageNo > 0){
this.currentPageNo = currentPageNo;
}
}
public int getTotalCount() {
return totalCount;
}
public void setTotalCount(int totalCount) {
if(totalCount > 0){
this.totalCount = totalCount;
//设置总页数
this.setTotalPageCountByRs();
}
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
if(pageSize > 0){
this.pageSize = pageSize;
}
}
public int getTotalPageCount() {
return totalPageCount;
}
public void setTotalPageCount(int totalPageCount) {
this.totalPageCount = totalPageCount;
}
public void setTotalPageCountByRs(){
if(this.totalCount % this.pageSize == 0){
this.totalPageCount = this.totalCount / this.pageSize;
}else if(this.totalCount % this.pageSize > 0){
this.totalPageCount = this.totalCount / this.pageSize + 1;
}else{
this.totalPageCount = 0;
}
}
}
2.导入用户列表页面
- userlist.jsp
- rollpage.jsp
1.获取用户数量
1.UerDao接口
//通过条件查询-用户表记录数
public int getUserCount(Connection connection, String userName, int userRole)throws Exception;
2.UerDaoImpl【使用动态SQL】
//根据用户名或用户角色查询用户总数
public int getUserCount(Connection connection, String userName, int userRole) throws Exception {
// TODO Auto-generated method stub
PreparedStatement pstm = null;
ResultSet rs = null;
int count = 0;
if(connection != null){
//用StringBuffer是因为userName和userRole可能有也可能没有(也就是sql语句不确定,可变-->动态sql),利用StringBuffer的可变性进行随时添加
StringBuffer sql = new StringBuffer();
sql.append("select count(1) as count from smbms_user u,smbms_role r where u.userRole = r.id");
//用ArrayList也是因为应对userName和userRole有没有的情况,随时添加。
// 如果直接用object[]={userName,userRole}的话,userName和userRole不一定谁有谁没有,这样在dao层的执行语句中给sql语句的占位符(?)复制时,可能参数对不上,使用ArrayList随时添加可以解决这个问题。
List<Object> list = new ArrayList<Object>();
if(!StringUtils.isNullOrEmpty(userName)){
sql.append(" and u.userName like ?");
list.add("%"+userName+"%");
}
if(userRole > 0){
sql.append(" and u.userRole = ?");
list.add(userRole);
}
//把list转换成数组。
//经过判断,userName和userRole谁存在谁不存在已经确定,list中保存了存在的参数,此时可以转换成数组Object[] params,供BaseDao.execute()调用。
Object[] params = list.toArray();
System.out.println("sql ----> " + sql.toString());
rs = BaseDao.execute(connection, pstm, rs, sql.toString(), params);
if(rs.next()){
count = rs.getInt("count");//从结果集中获取最终的数量。
}
BaseDao.closeResource(null, pstm, rs);
}
return count;
}
3.UerService接口
//根据条件查询用户表记录数
public int getUserCount(String queryUserName, int queryUserRole);
4.UeServiceImpl
public int getUserCount(String queryUserName, int queryUserRole) {
// TODO Auto-generated method stub
Connection connection = null;
int count = 0;
System.out.println("queryUserName ---- > " + queryUserName);
System.out.println("queryUserRole ---- > " + queryUserRole);
try {
connection = BaseDao.getConnection();
count = userDao.getUserCount(connection, queryUserName,queryUserRole);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
BaseDao.closeResource(connection, null, null);
}
return count;
}
2.获取用户列表
public List<User> getUserList()
,对这个查询用户列表函数,再走一遍上面的流程(如下)
UerDao接口、UerDaoImpl、UerService接口、UeServiceImpl
3.获取角色列表
为了结构清晰,将角色相关的代码单独放在一个包中,和pojo类对应,编写对应的dao和service
4.用户显示的servlet【这个servlet较为复杂】
至此服务处理完了,编写最后的servlet,这个servlet程序主要包括以下5部分。
//重点、难点
private void query(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//查询用户列表
//从前端获取数据
String queryUserName = request.getParameter("queryname");
String temp = request.getParameter("queryUserRole");
String pageIndex = request.getParameter("pageIndex");
int queryUserRole = 0;
UserService userService = new UserServiceImpl();
List<User> userList = null;
//设置页面容量。
// 第一次走这个请求,一定是第一页,页面大小固定
int pageSize = Constant.pageSize;
//当前页码
int currentPageNo = 1;
/**
* http://localhost:8090/SMBMS/userlist.do
* ----queryUserName --NULL
* http://localhost:8090/SMBMS/userlist.do?queryname=
* --queryUserName ---""
*/
System.out.println("queryUserName servlet--------"+queryUserName);
System.out.println("queryUserRole servlet--------"+queryUserRole);
System.out.println("query pageIndex--------- > " + pageIndex);
//判断这些请求是否要处理
if(queryUserName == null){
queryUserName = "";
}
if(temp != null && !temp.equals("")){
queryUserRole = Integer.parseInt(temp);
}
if(pageIndex != null){
try{
currentPageNo = Integer.valueOf(pageIndex);
}catch(NumberFormatException e){
response.sendRedirect("error.jsp");
}
}
//为了实现分页,需要计算出当前页面和总页面,页面大小...
//总数量(表)
int totalCount = userService.getUserCount(queryUserName,queryUserRole);
//总页数
PageSupport pages=new PageSupport();
pages.setCurrentPageNo(currentPageNo);
pages.setPageSize(pageSize);
pages.setTotalCount(totalCount);
int totalPageCount = pages.getTotalPageCount();
//控制首页和尾页
if(currentPageNo < 1){
currentPageNo = 1;
}else if(currentPageNo > totalPageCount){
currentPageNo = totalPageCount;
}
//获取用户列表展示
userList = userService.getUserList(queryUserName,queryUserRole,currentPageNo, pageSize);
request.setAttribute("userList", userList);
List<Role> roleList = null;
RoleService roleService = new RoleServiceImpl();
roleList = roleService.getRoleList();
request.setAttribute("roleList", roleList);
request.setAttribute("queryUserName", queryUserName);
request.setAttribute("queryUserRole", queryUserRole);
request.setAttribute("totalPageCount", totalPageCount);
request.setAttribute("totalCount", totalCount);
request.setAttribute("currentPageNo", currentPageNo);
request.getRequestDispatcher("userlist.jsp").forward(request, response);
}
用户管理实现(增删改)
- 一切的增删改都需要处理
事务
,下面是一个增加的使用事务的例子:
public boolean add(User user) {
// TODO Auto-generated method stub
boolean flag = false;
Connection connection = null;
try {
connection = BaseDao.getConnection();
connection.setAutoCommit(false);//开启JDBC事务管理
int updateRows = userDao.add(connection,user);
connection.commit();
if(updateRows > 0){
flag = true;
System.out.println("add success!");
}else{
System.out.println("add failed!");
}
} catch (Exception e) {
e.printStackTrace();
try {
System.out.println("rollback==================");
//如果执行出错就回滚
connection.rollback();
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}finally{
//在service层进行connection连接的关闭
BaseDao.closeResource(connection, null, null);
}
return flag;
}
供应商管理、订单管理
供应商管理、订单管理,和用户管理一样,也分查询和增删改两部分。实施方式也大体类似
转载:https://blog.csdn.net/qq_42647047/article/details/116503457
查看评论