Web学习笔记(四):超市管理系统的搭建

本文是Web学习笔记第四篇,主要介绍了一个超市管理系统的搭建过程,包括项目前瞻、准备工作和具体功能实现。重点讲解了登录、注销、非法访问过滤以及用户管理(密码修改、用户总数、条件查询)等功能的实现,涉及Servlet、DAO和服务层的代码编写。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Web学习笔记():超市管理系统的搭建

1. 项目前瞻

​ 整合整个JavaWeb所学习的知识做一个超市管理系统小项目。该管理系统分为四个功能:

  • 登录注销

  • 用户管理

  • 订单管理

  • 供应商管理

    每个部分都需要连接数据库,实现增删改查功能。

2.项目实现

2.1 准备工作

​ 首先创建一套模板式的项目结构:

  • 创建Maven中的web-app项目模板

  • 修改Web-inf中的xml文件,导入项目依赖

  • 配置Tomcat,IDEA连接数据库

  • 在main目录下建Java、resources两个包,在java包中创建项目包结构

    • pojo数据库实体类
    • dao持久化操作数据库
    • service业务层
    • servlet
    • filter过滤器层
    • util工具类
  • 为了操作数据库,在resources包下新建properties文件,写入以下键值对:

    driver=com.mysql.jdbc.Driver
    url=jdbd:mysql://localhost:3306/smbms?userUnicode=true&characterEncoding=utf8
    username=root
    password=123456
    
  • 在pojo目录创建数据库对应的实体类,类中的每个变量对应表单的各个属性

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JwD02idK-1597284538511)(C:\Users\hasee\Desktop\XIKAIJAVA\JavaWeb\smbmgProject\1596163713199.png)]

  • 在dao中创建操作数据库的公共类

public class BaseDao {

    private static String driver;
    private static String url;
    private static String username;
    private static String password;

    static {
        // 通过类加载器读取资源
        Properties properties = new Properties();
        InputStream stream = BaseDao.class.getClassLoader().getResourceAsStream("db.properties");
        try {
            properties.load(stream);
        } catch (IOException e) {
            e.printStackTrace();
        }

        driver = properties.getProperty("driver");
        url = properties.getProperty("url");
        username = properties.getProperty("username");
        password = properties.getProperty("password");
    }

    public static Connection getConnection(){
        Connection connection = null;
        try {
            Class.forName(driver);
            connection = DriverManager.getConnection(url, username, password);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return connection;
    }

    //编写查询公共类,传入statement、resultSet是为了方便关闭
    public static ResultSet execute(Connection connection,String sql,Object[] params,ResultSet resultSet,PreparedStatement statement) throws SQLException {
        statement = connection.prepareStatement(sql);
        //开始赋值
        for (int i = 0; i < params.length; i++) {
            //statement占位符从1开始,数组从0开始
            statement.setObject(i+1,params[i]);
        }
        resultSet = statement.executeQuery();
        return resultSet;
    }

    //编些增删改公共类
    public static int execute(Connection connection,String sql,Object[] params,PreparedStatement statement) throws SQLException {
        statement = connection.prepareStatement(sql);
        //开始赋值
        for (int i = 0; i < params.length; i++) {
            //statement占位符从1开始,数组从0开始
            statement.setObject(i+1,params[i]);
        }
        int update = statement.executeUpdate();
        return update;
    }

    //关闭连接,释放资源
    public static boolean closeResource(Connection connection,ResultSet resultSet,PreparedStatement preparedStatement){
        boolean flag = false;
        if (resultSet!= null){
            try {
                resultSet.close();
                resultSet = null;
            } catch (SQLException e) {
                e.printStackTrace();
                flag = false;
            }
        }

        if (connection!= null){
            try {
                connection.close();
                connection = null;
            } catch (SQLException e) {
                e.printStackTrace();
                flag = false;
            }
        }

        if (preparedStatement!= null){
            try {
                preparedStatement.close();
                preparedStatement = null;
            } catch (SQLException e) {
                e.printStackTrace();
                flag = false;
            }
        }
        return flag;
    }

}
  • 在filter下创建字符编码过滤器,并在web.xml中注册
public class CharacterEncodingFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        servletRequest.setCharacterEncoding("UTF-8");
        servletResponse.setCharacterEncoding("UTF-8");

        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {

    }
}
  • 将前端静态资源导入webapp目录

    至此准备工作已经全部完毕,都是一些固定的流程,几乎在每个项目通用。

2.2 登录功能的实现

​ 登录功能的实现思路为:网页发送请求到servlet,servlet将数据发送到服务层,服务层将数据传递到持久层,持久层调用数据库查询数据,将查询结果一层层返回。编写的顺序推荐从下到上,即先编写持久层查询数据的代码,再编写服务层,最后完成servlet的编写。

2.2.1 持久层的实现

​ 持久层并不是上来直接就撸代码,我们需要先实现一个登录功能的持久层接口,确定需要传输进入的参数为数据库Connection和传入的用户名userCode:

public interface UserDao {
    public User getLoginUser(Connection connection,String userCode) throws SQLException;
}

​ 接着实现这个接口,需要调用dao层的公共类BaseDao,得到的是userCode对应的整个User对象数据,将其放入pojo实现类中:

public class UserDaoImpl implements UserDao {
    @Override
    public User getLoginUser(Connection connection, String userCode) throws SQLException {
        ResultSet rs = null;
        PreparedStatement preparedStatement = null;
        User user = null;

        if (connection != null) {
            System.out.println(userCode);
                String sql = "select * from smbms_user where userCode=?";
                Object[] params = {userCode};
                rs = BaseDao.execute(connection, sql, params, rs, preparedStatement);
                if (rs.next()){
                    user = new User();
                    user.setId(rs.getInt("id"));
                    user.setUserCode(rs.getString("userCode"));
                    System.out.println(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"));
                }
                BaseDao.closeResource(connection,rs,preparedStatement);

        }
        return user;
    }
}
2.2.2 服务层的实现

​ 通过观察服务层的代码可以看到,服务层的作用仅仅是将Servlet请求的用户名转发到持久层,接收持久层返回的user对象和判断密码是否正确,但是user对象可以直接传送给Servlet,而且判断密码也可以在Servlet层完成 ,所以个人感觉这一层有些鸡肋,也许这是因为在小项目里面所以作用不甚明显吧。代码的实现有很多种,最主要的还是需要学会这种分层的思想。

​ 以下为服务层的代码,和持久层相同,都是先创建一个接口,再通过实现接口来完成功能:

public interface UserService {
    public User login(String userCode,String userPassword) throws SQLException;
}



public class UserServiceImpl implements UserService {
    private UserDao userDao;

    public UserServiceImpl() {
        userDao = new UserDaoImpl();
    }

    //调用Dao层
    @Override
    public User login(String userCode, String userPassword){
        Connection connection =null;
        User user = null;
        try {
            connection = BaseDao.getConnection();
            user = userDao.getLoginUser(connection, userCode);

        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            BaseDao.closeResource(connection,null,null);
        }
        if (null != user) {
            if (!user.getUserPassword().equals(userPassword))
                user = null;
        }
        return user;
    }
}
2.2.3 Servlet层的实现

​ Servlet层的操作就很常规化了,接收用户名和密码后向服务层传递,再接收服务层的返回数据,登录成功则进入系统,失败则返回登录界面。

public class LoginServlet extends HttpServlet {
    @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){
            //登录成功,重定向到主页
            req.getSession().setAttribute(Constants.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);
    }
}

​ 之后将Servlet层在xml中映射即可。

2.3 注销功能的实现

​ 注销功能在Servlet中直接实现,简直不要太简单,直接上代码:

public class LogoutServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//移除Session
        req.getSession().removeAttribute(Constants.USER_SESSION);
        resp.sendRedirect(req.getContextPath()+"/login.jsp");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

​ 不要忘记在xml中配置映射。

2.4 对非法访问进行过滤

​ 非法访问是指在未登录的情况下访问网页内部,需要使用过滤器,也很简单:

public class SysFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse resp = (HttpServletResponse)servletResponse;
        HttpServletRequest req = (HttpServletRequest)servletRequest;

        User user = (User) req.getSession().getAttribute(Constants.USER_SESSION);
        if (user == null){
            resp.sendRedirect(req.getContextPath()+"/error.jsp");
        }else {
            filterChain.doFilter(servletRequest,servletResponse);
        }

    }

    @Override
    public void destroy() {

    }
}

​ 当然,这个也要在xml中配置映射。

2.5 修改密码功能的实现

​ 修改密码功能有两个部分需要注意:

  1. 后台修改新密码
  2. 前端通过ajax完成旧密码输入是否正确,即判断是否本人操作
2.5.1 dao层的操作

​ dao层就是将输入的新密码更新到数据库中,没啥好说的。

public int updataPwd(Connection connection, int id, String password) throws SQLException {
        PreparedStatement preparedStatement = null;
        int execute = 0;
        if (connection != null) {
            String sql = "update smbms_user set userPassword = ? where id = ?";
            Object[] params = {password, id};
            execute = BaseDao.execute(connection, sql, params, preparedStatement);
            BaseDao.closeResource(connection,null,preparedStatement);
        }
        return execute;
    }
2.5.2 Service层的操作

​ service层除了转发功能还验证了密码修改是否成功。

public Boolean updatePwd(int id, String pwd){
        Connection connection = null;
        boolean flag = false;
        int i = 0;
        try {
            connection = BaseDao.getConnection();
            i = userDao.updataPwd(connection, id, pwd);
            if (i > 0){
                flag = true;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            BaseDao.closeResource(connection,null,null);
        }
        return flag;
    }
2.5.3 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")&&method!=null){
            this.updatePwd(req,resp);
        }else if (method.equals("pwdmodify")&&method!= null){
            this.pwdModify(req,resp);
        }

    }
    public void updatePwd(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        Object attribute = req.getSession().getAttribute(Constants.USER_SESSION);
        String newpassword = req.getParameter("newpassword");

        if (attribute != null && newpassword!= null && newpassword.length()!= 0){
            UserServiceImpl service = new UserServiceImpl();
            Boolean flag = service.updatePwd(((User) attribute).getId(), newpassword);
            System.out.println(((User) attribute).getId());
            System.out.println(newpassword);
            if (flag){
                req.setAttribute("message","密码修改成功,请使用新密码登录");
                req.getSession().removeAttribute(Constants.USER_SESSION);
            }else {
                req.setAttribute("message","密码修改失败");
            }
        }else {
            req.setAttribute("message","新密码格式错误");
        }
        req.getRequestDispatcher("pwdmodify.jsp").forward(req,resp);
    }

    public void pwdModify(HttpServletRequest req, HttpServletResponse resp){
        Object attribute = req.getSession().getAttribute(Constants.USER_SESSION);
        String oldpassword = req.getParameter("oldpassword");

        //创建一个结果集传递给ajax
        HashMap<String, String> map = new HashMap<>();

        if (attribute == null){ //session过期了
            map.put("result","sessionerror");
        }else if (oldpassword == null && oldpassword.length() == 0){
            map.put("result","error");
        }else {
            if (((User)attribute).getUserPassword().equals(oldpassword)){
                map.put("result","true");
            }else {
                map.put("result","false");
            }
        }

        try {
            resp.setContentType("application/json");
            PrintWriter writer = resp.getWriter();
            writer.write(JSONArray.toJSONString(map));
            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

2.6 用户管理底层实现

​ 用户管理层可以说是目前所有功能里最难的一部分了。首先通过用户管理层界面我们可以看到:

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TLmbEV0p-1597284538530)(C:\Users\hasee\Desktop\XIKAIJAVA\JavaWeb\smbmgProject\1596340405700.png)]

​ 该部分内容由三个部分组成:

  1. 用户角色

  2. 通过条件查询到的用户和分页

  3. 用户总数

    这意味着我们需要完成三条线的设计,每条线都有自己的Service和dao去调用数据库获取数据,如图所示:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KZ6eHTqk-1597284538533)(C:\Users\hasee\Desktop\XIKAIJAVA\JavaWeb\smbmgProject\1596340558771.png)]

    该项目的重难点在dao层和Servlet层的实现。

    在dao层中,因为进行条件搜索需要参数用户名用户角色,如果使用静态sql,即每条sql只用来查询用户名或者角色的话那么就需要三个dao层进行处理;而使用动态sql,即先验证传入的参数是否为空来确定搜索的条件,通过StringBuffer的append方法进行sql语句的拼接,就可以动态的确定sql语句的搜索范围,减少代码量。

    在Servlet层中,需要同时处理三条线的数据并对前端数据的准确性进行确认。因为前端传入数据的特性,后台需要再次进行验证,之后就可以调用serviceimpl方法进行数据操作,再将数据返回给前端就可以了。

2.6.1 用户总数线

​ 1.从dao层写起,sql语句使用了连表查询和 count(1) ,这是专门用于查询结果数量的语句,效率远远高于 count(*) 。同时利用了StringBuffer和List,是为了针对该功能的搜索查询,即可以根据搜索的用户名或角色显示相应结果相应的数量。

public int getUserCount(Connection connection, String username, int userRole) throws SQLException {
        //查询所有相关的用户
        ResultSet resultSet = null;
        PreparedStatement preparedStatement = null;
        int count = 0;

        if (connection != null){
            StringBuffer sql = new StringBuffer();
            ArrayList<Object> list = new ArrayList<>();
            sql.append("select count(1) as count from smbms_user u,smbms_role r where u.userRole = r.id");

            //查询名字对应的用户
            if (!StringUtils.isNullOrEmpty(username)){
                sql.append(" and u.username like ?");
                list.add("%"+username+"%");
            }

            if (userRole > 0){
                sql.append(" and u.userRole like ?");
                list.add(userRole);
            }

            Object[] params = list.toArray();
            resultSet = BaseDao.execute(connection,String.valueOf(sql),params,resultSet,preparedStatement);
            if (resultSet.next()){
                count = resultSet.getInt("count");
            }
            BaseDao.closeResource(null,resultSet,preparedStatement);
        }
        return count;

    }

​ 2.service层方面进行数据的转发

public int getUserCount(String username, int userRole) {
        Connection connection = null;
        int count = 0;
        try {
            connection = BaseDao.getConnection();
            count = userDao.getUserCount(connection,username,userRole);
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            BaseDao.closeResource(connection,null,null);
        }
        return count;
    }
2.6.2 条件查询结果和分页线

​ 1. 条件查询的dao层使用连表查询,同时将结果进行分页,通过传入的页数参数返回相应页数的结果。

 public List<User> getUserList(Connection connection, String username, int userRole, int currentPage, int pageSize) throws SQLException {
        ResultSet rs = null;
        PreparedStatement preparedStatement = null;
        ArrayList<User> userList = new ArrayList<>();
        int count = 0;

        if (connection != null){
            StringBuffer sql = new StringBuffer();
            ArrayList<Object> list = new ArrayList<>();
            sql.append("select u.*,r.roleName as userRoleName from smbms_user u,smbms_role r where u.userRole = r.id");

            //查询名字对应的用户
            if (!StringUtils.isNullOrEmpty(username)){
                sql.append(" and u.username like ?");
                list.add("%"+username+"%");
            }

            if (userRole > 0){
                sql.append(" and u.userRole like ?");
                list.add(userRole);
            }
            //将查询结果分页
            sql.append(" order by creationDate DESC limit ?,?");
            currentPage = (currentPage-1)*pageSize;
            list.add(currentPage);
            list.add(pageSize);

            Object[] params = list.toArray();
            rs = BaseDao.execute(connection,String.valueOf(sql),params,rs,preparedStatement);
            while (rs.next()){
                User _user = new User();
                _user.setId(rs.getInt("id"));
                _user.setUserCode(rs.getString("userCode"));
                _user.setUserName(rs.getString("userName"));
                _user.setGender(rs.getInt("gender"));
                _user.setBirthday(rs.getDate("birthday"));
                _user.setPhone(rs.getString("phone"));
                _user.setUserRole(rs.getInt("userRole"));
                _user.setUserRoleName(rs.getString("userRoleName"));
                userList.add(_user);
            }
            BaseDao.closeResource(null,rs,preparedStatement);
        }
        return userList;
    }

​ 2.service层方面进行数据的转发

public List<User> getUserList(String queryUserName, int queryUserrRole, int currentPage, int pageSize) {
        List<User> list = null;
        Connection connection = null;
        try {
            connection = BaseDao.getConnection();
            list = userDao.getUserList(connection,queryUserName,queryUserrRole,currentPage,pageSize);
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            BaseDao.closeResource(connection,null,null);
        }
        return list;
    }
2.6.3 角色查询

​ 因为角色属于Role而非User,故需要重新在dao包和service包下创建role类。

dao层代码:

public List<Role> getRoleList(Connection connection) {
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        ArrayList<Role> roles = null;

        if (connection != null) {
            try {
                roles = new ArrayList<>();
                Object[] params = {};
                String sql = "select * from smbms_role";
                resultSet = BaseDao.execute(connection, sql, params, resultSet, preparedStatement);
                while (resultSet.next()){
                    Role _role = new Role();
                    _role.setRoleName(resultSet.getString("roleName"));
                    _role.setId(resultSet.getInt("id"));
                    _role.setRoleCode(resultSet.getString("roleCode"));
                    roles.add(_role);
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }finally {
                BaseDao.closeResource(null,resultSet,preparedStatement);
            }
        }
        return roles;
    }

service层代码:

public class RoleServcieImpl implements RoleService {
    private RoleDao roleDao;

    public RoleServcieImpl() {
        roleDao = new RoleDaoImpl();
    }

    @Override
    public List<Role> getRoleList() {
        Connection connection = null;
        List<Role> roleList = null;

        connection = BaseDao.getConnection();
        roleList = roleDao.getRoleList(connection);
        BaseDao.closeResource(connection,null,null);
        return roleList;
    }
}
2.6.4 Servlet层

​ servlet层的功能是将三条线汇总,处理三条线的功能。建议先直接调用service层的方法,然后根据参数来补充数据。需要注意的是要对前端传输进来的数据进行校验。

public void query(HttpServletRequest req, HttpServletResponse resp){
        //从前端获取数据
        String queryname = req.getParameter("queryname");
        //因为下拉框的特性是未选择的话传入的值为null,因此需要赋初值0,把前端接收的变量用temp代替
        String temp = req.getParameter("queryUserRole");
        int queryUserRole = 0;
        //页面也是同理
        String pageIndex = req.getParameter("pageIndex");
        int currentPageNo = 1;

        //定义页面显示的数据大小
        int pageSize = 5;

        UserServiceImpl userService = new UserServiceImpl();

        if (queryname == null){
            queryname = "";
        }
        //确认前端传输的值不是null或空才将结果赋值给queryUserRole
        //否则结果为默认值0
        if (temp != null && !temp.equals("")){
            queryUserRole = Integer.parseInt(temp);
        }
        //确认前端传输的值不是null或空才将结果赋值给currentPageNo
        //否则结果为默认值1
        if (pageIndex != null){
            currentPageNo = Integer.parseInt(pageIndex);
        }

        //获取用户总数
        int total = userService.getUserCount(queryname, queryUserRole);
        List<User> userList = null;

        //总页数支持
        PageSupport pageSupport = new PageSupport();
        pageSupport.setCurrentPageNo(currentPageNo);
        pageSupport.setPageSize(pageSize);
        pageSupport.setTotalCount(total);

        //设置总页数
        int totalPageCount = ((int)(total/pageSize))+1;
        //控制首页和尾页
        if (currentPageNo < 1){
            currentPageNo = 1;
        }else if (currentPageNo > totalPageCount){
            currentPageNo = totalPageCount;
        }

        //获取用户列表展示
        userList = userService.getUserList(queryname, queryUserRole, currentPageNo, pageSize);
        req.setAttribute("userList",userList);

        //获取角色列表
        RoleServcieImpl roleServcie = new RoleServcieImpl();
        List<Role> roleList = roleServcie.getRoleList();
        req.setAttribute("roleList",roleList);
        req.setAttribute("totalCount",total);
        req.setAttribute("currentPageNo",currentPageNo);
        req.setAttribute("totalPageCount",totalPageCount);
        req.setAttribute("queryUserName",queryname);
        req.setAttribute("queryUserRole",queryUserRole);

        try {
            req.getRequestDispatcher("userlist.jsp").forward(req,resp);
        } catch (ServletException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值