# Java Web 共享充电宝后台管理系统,Jsp+Servlet开发,前端bootstrap **Repository Path**: pjjg/JspServlet ## Basic Information - **Project Name**: Java Web 共享充电宝后台管理系统,Jsp+Servlet开发,前端bootstrap - **Description**: Java Web 共享充电宝后台管理系统,Jsp+Servlet开发,前端bootstrap - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 2 - **Created**: 2024-08-19 - **Last Updated**: 2024-08-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 一、确定项目(2022年4月23日) 因为在大一上学期学习c语言的期末考核的时候做了一个**共享充电宝管理系统**,但是那个时候只是学习了简单的c语言的基本操作,并还没开始学习数据库、Java等等,所以只是简单的进行内存数组操作达到管理的目的,然后也只是在cmd中通过print打印的方式去实现图形界面,在几个学期学习了**前端**(html和css+js)和**后端**(java+sqlserver)后,可以开始学习写javaweb了,也就是完整的一个**后台管理系统**。不忘初心,所以我确定我写的项目是**共享充电宝管理系统**。继续大一上学期第一次用c语言实现的管理系统的加强版。 附上几张大一上学期用c语言写的系统图 **ps:这个文档是项目的开发日志,同时也是将是我的学习笔记,因为在写这个项目的时候我刚刚在b站学完mybaits,然后正在学spring,所以涉及到一些相关问题我也会当成学习笔记去写在这个文档中,当是练习也同时是复习** ![img](https://img.bones2674.com/Picgo/202205202327335.jpg) ![img](https://img.bones2674.com/Picgo/202205202327337.jpg) # 二、项目开发环境 服务器:Tomcat 9.0.62 开发工具:Idea 2020 2.3 数据库:mysql 5.6.50 # 三、确定系统实现功能 需求分析 用户:登录、注册、注销、更改用户信息、租用充电宝、归还充电宝、查询租用充电宝信息 管理员:登录、增加充电宝信息、更改充电宝信息、删除充电宝信息、查询充电宝信息 ps: 这张图是我大一上学期期末考核的菜单图 ![img](https://img.bones2674.com/Picgo/202205202327338.jpg) # 四、搭建项目环境 ### 4.1创建maven项目 导入相关依赖包 ```xml javax.servlet javax.servlet-api 3.1.0 provided javax.servlet.jsp javax.servlet.jsp-api 2.3.1 provided javax.servlet jstl 1.2 taglibs standard 1.1.2 org.clojure java.jdbc 0.7.11 mysql mysql-connector-java 5.1.47 com.alibaba fastjson 1.2.62 ``` 通过maven仓库导入相关的依赖包 # 五、登录功能实现 ## 5.1创建登录页 创建login.jsp登录页后在wbe.xml中将login.jsp设置为欢迎页面 ```xml login.jsp ``` ## 5.2编写登录页面 我比较懒就直接在码云上直接找了一个开源的smbs项目然后拿了前端页面,然后进行了重构 ![image-20220504194449413](https://img.bones2674.com/Picgo/202205202327339.png) ## 5.3实现jdbc数据库连接 ### 5.3.1创建dao层和实体类 并在dao层中创建jdbc的接口和实现类 ![image-20220428001813414](https://img.bones2674.com/Picgo/202205202327340.png) ![](https://img.bones2674.com/Picgo/202205202327341.png) 创建用户实体类并构造set和get方法,用来存放从数据库中取出的用户数据 ### 5.3.2创建jdbc驱动连接 这里我用的数据库是我服务器远程的mysql,因为电脑磁盘不够了就没装本地的mysql,然后配置好数据库参数后 然后在idea中先用idea自带的数据库连接工具连接数据库![image-20220429183736751](https://img.bones2674.com/Picgo/202205202327342.png) 测试连接成功后,我们在resources下创建一个properties文件,然后通过类加载器将properties文件转换为流,再通过类加载器load流读取数据库配置 ![image-20220429183847021](https://img.bones2674.com/Picgo/202205202327343.png) ### 5.3.3在Dao层创建连接数据库的基础类 在Dao层创建一个叫DataBaseDao的基础类,在这个类里我们写上连接数据库的方法 **这里用通过类加载器方式把流文件读取出来** ```java public class DataBaseDao { private static String driver; private static String url; private static String user; private static String password; static{ Properties params=new Properties(); String configFile = "database.properties"; InputStream is= DataBaseDao.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"); } } ``` 然后再写一个静态获取getconnection的方法 ```java 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; } ``` ### 5.3.4进行单元测试 都写完后我们进行一下单元测试,因为我在maven仓库里导入了一个junit的依赖包 ```xml junit junit 4.12 test ``` 这个依赖可以实现单元测试 ![image-20220429190522568](https://img.bones2674.com/Picgo/202205202327344.png) 进行单元测试后发现打印出得connection为null; 并且捕获了一个SQL异常:**Access denied for user ''@'171.109.153.86' (using password: YES)** ![image-20220429191340308](https://img.bones2674.com/Picgo/202205202327345.png) 百度查询解决方法后依然没办法解决,最后再观察代码后, 发现我的database.properties文件中的uesername并没有被引用 ![image-20220429191324162](https://img.bones2674.com/Picgo/202205202327346.png) 查找连接数据库的代码后发现,从配置文件中取出数据的时候,名字写错了 ```java driver=params.getProperty("driver"); url=params.getProperty("url"); user=params.getProperty("user");//这里写错了变量名,应该写为“username” password=params.getProperty("password"); ``` 更改后问题解决,成功连接数据库 ![image-20220429191531120](https://img.bones2674.com/Picgo/202205202327347.png) ## 5.4数据库设计 这里只实现登录的话,先创建一个表Admin,然后加入字段Id,UserName,PassWord,Email基础登录信息 ![image-20220429193013637](https://img.bones2674.com/Picgo/202205202327348.png) ## 5.5实现密码验证 在com.ouyang的包下创建一个servlet层 在再servlet层中创建一个User包,用来处理User方面的请求 ![image-20220430001003727](https://img.bones2674.com/Picgo/202205202327349.png) 创建好LoginServlet后我们在web.xml中先注册一下这个Servlet ```xml LoginServlet com.ouyang.servlet.User.LoginServlet LoginServlet /login.do ``` 前端Form表单提交的action就是login.do跳转到LoginServlet处理请求 注册好Servlet后,开始写Servlet层的东西,这里Servlet层只负责视图跳转 还需要创建Service层进行业务逻辑判断,Service层又需要Dao层从数据库中拿数据库User信息 跟Dao层一样在Service层下创建一个UserService的接口和实现类,UserServiceImpl实现UserService接口 这里需要在DataBaseDao中再写一个静态的万能查询方法,方便以后Service层直接调用 ```java public static ResultSet executers(Connection connection,PreparedStatement pstm,ResultSet rs, String sql,Object[] params) throws Exception{ pstm = connection.prepareStatement(sql); for(int i = 0; i < params.length; i++){ pstm.setObject(i+1, params[i]); } rs = pstm.executeQuery(); return rs; } ``` 然后再写一个更新的万能方法 ```java 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; } ``` 然后我们就可以在Service层直接调用查询了,这两个万能方法只需要传入connection,preparedstatement,sql,和注参 再写一个关闭连接的方法 ```java public static boolean closeResource(Connection connection,PreparedStatement pstm,ResultSet rs){ boolean flag = true; if(rs != null){ try { rs.close(); rs = null; } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); flag = false; } } if(pstm != null){ try { pstm.close(); pstm = null; } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); flag = false; } } if(connection != null){ try { connection.close(); connection = null; } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); flag = false; } } return flag; } ``` 然后就可以写UserDao层的getuser方法了 ```java public User getloginuser ( Connection connection, String UserName ) throws Exception { PreparedStatement pstm = null; ResultSet rs = null; User user = null; if (connection != null) { String sql = "select * from sdbproject where UserName=?"; Object[] params = {UserName}; rs = DataBaseDao.executers(connection, pstm, rs, sql, params); if (rs.next()) { user = new User(); user.setUsername(rs.getString("UserName")); user.setUserpassword(rs.getString("UserPassword")); user.setEmail(rs.getString("Email")); } DataBaseDao.closeResource(null, pstm, rs); } return user; } ``` 然后我们再去Service层调用取出用户 ```java public User login ( String UserName, String UserPassword ) { Connection connection = null; User user = null; try { connection = DataBaseDao.getConnection(); user = userDao.getloginuser(connection,UserName); } catch (Exception e) { e.printStackTrace(); }finally { DataBaseDao.closeResource(connection,null,null); } return user; } ``` ```java String UserName = req.getParameter("UserName"); String UserPassword = req.getParameter("UserPassword"); UserService userService = new UserServiceImpl(); User user = userService.login(UserName, UserPassword); if (user!=null) { req.getSession().setAttribute("usersession",user); resp.sendRedirect(req.getContextPath()+"/admin/index.jsp"); } ``` ![image-20220501144425322](https://img.bones2674.com/Picgo/202205202327350.png) 登录功能成功实现 ## **流程:** > - 1、前端页面发起请求 > - 2、LoginServlet处理请求调用UserService层 > - 3、UserService层调用UserDao层取出用户信息进行密码判断 > - 4、将判断结果return至LoginServlet层成功则重定向到admin/index.jsp,失败则转发回login.js > ## 5.6添加过滤器,防止未登录用户访问admin/index.jsp ```java HttpServletRequest rq = (HttpServletRequest)servletRequest; HttpServletResponse rp = (HttpServletResponse)servletResponse; User userSession = (User)rq.getSession().getAttribute("userSession"); if(null == userSession){ rp.sendRedirect(rq.getContextPath()+"/error.jsp"); }else{ filterChain.doFilter(servletRequest, servletResponse); } ``` ![image-20220501144425322](https://img.bones2674.com/Picgo/202205202327350.png) # 六、注销功能实现 > ### 实现原理 > > 我们在点击退出按钮后,跳转到loginout.do进行处理,然后移除session,再重定向到登录页面 ![image-20220504201612548](https://img.bones2674.com/Picgo/202205202327351.png) 这里我为了方便省去每次创建一个Servlet都要去web.xml文件里注册,我就直接使用WebServlet注解来写了 测试后成功 # 七、密码更改功能实现 跟登录成功一样,在接口中创建方法然后实现类实现这个方法 为了后面sql拼接的方便性,后面我就直接在Dao层编写sql语句不再使用之前写的万能查询语句进行查询了 > # Dao层 ```java public boolean changepwd ( String UserName, String pwd ) { Connection connection = DataBaseDao.getConnection(); PreparedStatement statement = null; String sql = "update cdbproject set PassWord = ? where UserName = ?"; try { statement = connection.prepareStatement(sql); statement.setString(1,pwd); statement.setString(2,UserName); statement.executeUpdate(); } catch (SQLException throwables) { throwables.printStackTrace(); }finally { DataBaseDao.closeResource(connection,statement,null); } return true; } } ``` > # Service层 ```java public boolean changepwd ( String UserName, String pwd ) { if(userDao.changepwd(UserName,pwd)){ return true; } return false; } } ``` > # Servlet层 ```java public void changepwd (HttpServletRequest req, HttpServletResponse resp ) throws ServletException, IOException { User user = (User) req.getSession().getAttribute("session"); String newpassword = req.getParameter("newpassword"); if(user!=null&&newpassword!=null){ UserService userService = new UserServiceImpl(); boolean flag = userService.changepwd(user.getUsername(), newpassword); if(flag){ req.setAttribute("message","修改密码成功,请退出并使用新密码重新登录!"); req.getSession().removeAttribute("usersession"); }else { req.setAttribute("message", "修改密码失败!"); } } req.getRequestDispatcher("pwdmodify.jsp").forward(req, resp); } ``` 这里为了Servlet的复用,不可能以后一个功能都写一个Servlet,我们创建一个方法,并提交一个method的值判断执行哪个方法 这里会层前端传入一个method的参数,在后端对method进行判断就知道需要执行哪一个具体的请求了 ```java String method = req.getParameter("method"); if (method.equals("changepwd")) { this.changepwd(req, resp); } ``` ```java public void changepwd (HttpServletRequest req, HttpServletResponse resp ) throws ServletException, IOException { Object user =req.getSession().getAttribute("usersession"); String newpassword = req.getParameter("newpassword"); if(user!=null&&newpassword!=null){ UserService userService = new UserServiceImpl(); boolean flag = userService.changepwd(((User)user).getUsername(), newpassword); if(flag){ req.setAttribute("message","修改密码成功,请退出并使用新密码重新登录!"); // req.getSession().removeAttribute("usersession"); }else { req.setAttribute("message", "修改密码失败!"); } }else { req.setAttribute("message", "修改密码失败!"); } System.out.println(user); System.out.println(newpassword); req.getRequestDispatcher("/admin/pwdmodify.jsp").forward(req, resp); } ``` 这里前端会传到后端一个method,然后对method进行判断执行哪个方法 ![image-20220504215348499](https://img.bones2674.com/Picgo/202205202327352.png) 测试成功 # 八、用户列表展示实现 首先跟之前一样,我们先写Dao层,再写Service层,最后写Servlet层 > # Dao层 ```java public List getuserlist ( ) { Connection connection = DataBaseDao.getConnection(); PreparedStatement statement = null; String sql = "select * from User"; ResultSet rs = null; List list = new ArrayList<>(); try { statement = connection.prepareStatement(sql); rs = statement.executeQuery(); while (rs.next()){ User user = new User(); user.setUserid(rs.getInt(1)); user.setUsername(rs.getString(2)); user.setUserpassword(rs.getString(3)); user.setEmail(rs.getString(4)); list.add(user); } } catch (SQLException throwables) { throwables.printStackTrace(); }finally { DataBaseDao.closeResource(connection,statement,rs); } return list; } ``` Service层调用Dao层会返回一个含有user的列表 > # Service层 ```java public List getuserlist ( ) { return userDao.getuserlist(); } ``` Service层只需要返回一个user列表即可,并不需要做多余的业务逻辑判断 > # Servlet层 ```java public void getuserlist ( HttpServletRequest req, HttpServletResponse resp ) throws ServletException, IOException { req.setAttribute("list", userService.getuserlist()); req.getRequestDispatcher("/admin/userlist,jsp").forward(req,resp); } ``` ![image-20220514152156009](https://img.bones2674.com/Picgo/202205202327353.png) 进行测试后报500错误,然后我在servlet层加了一个打印 ![image-20220514152229525](https://img.bones2674.com/Picgo/202205202327354.png) 可以成功打印出我数据库里的三个用户 百度后,得知: **页面用el表示时,应该将第一个字母改为小写** **为什么el表示时,第一个字母不能写大写?** - 首先我们需要了解的El表达式的运行机制 - el表达式在你获取对象时,自动把你的属性名转换成字符串 - 并把首字母大写, 进行拼接("get"+Xxx) - 然后通过反射的方式获取到get方法,返回属性值。 - 实际上就是调用了一遍JavaBean里面的xxx属性的get函数。 之后我将所有的变量第一个字母改为小写后,成功打印出用户列表 ![image-20220514152739222](https://img.bones2674.com/Picgo/202205202327355.png) 这里就一一对应我数据库的用户 ![image-20220514153132028](https://img.bones2674.com/Picgo/202205202327356.png) # 九、搜索功能实现 我们先在servlet层新定义一个新method:search ![image-20220516133254228](https://img.bones2674.com/Picgo/202205202327357.png) 然后我们就可以在search方法中调用了 前端页面中会在select中选择要搜索的字段名,然后后面的输入框中输入值,一共是传到后端两个值,这里我们就在servlet层中接收这两个参数,他们各有一个name为key,一个name为value ```java String key = req.getParameter("key"); String value = req.getParameter("value"); ``` > # Dao层 ```java public List search (String key,String value) { Connection connection = DataBaseDao.getConnection(); PreparedStatement statement = null; String sql = "select * from User where"+key+"like '%"+value+"%'"; ResultSet rs = null; List list = new ArrayList<>(); try { statement = connection.prepareStatement(sql); rs = statement.executeQuery(); while (rs.next()){ User user = new User(); user.setUserid(rs.getInt(1)); user.setUsername(rs.getString(2)); user.setUserpassword(rs.getString(3)); user.setEmail(rs.getString(4)); list.add(user); } } catch (SQLException throwables) { throwables.printStackTrace(); }finally { DataBaseDao.closeResource(connection,statement,rs); } return list; } ``` > # Service层 ```java public List search ( String key, String value ) { return userDao.search(key,value); } ``` > # Servlet层 因为这里进行查询后要重新刷新页面,那我们就让他重新转发到userlist页面进行刷新,然后把查询出来的用户展示出来 ```java public void search(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String key = req.getParameter("key"); String value = req.getParameter("value"); req.setAttribute("list",userService.search(key,value)); req.getRequestDispatcher("/admin/userlist.jsp").forward(req,resp); } ``` ![image-20220516140039993](https://img.bones2674.com/Picgo/202205202327358.png) 然后报了一个SQL错误 这里我在Dao层里打印value的值,发现打印出来是三个问号 ![image-20220516140323859](https://img.bones2674.com/Picgo/202205202327359.png) 然后我又在Servlet层打印value的值,发现也是三个问号 原因可能是没有设置字符编码过滤 于是我又写了一个过滤器用来过滤字符编码问题 ```java public class CharacterEncoding implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { // TODO Auto-generated method stub } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // TODO Auto-generated method stub request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); chain.doFilter(request, response); } @Override public void destroy() { // TODO Auto-generated method stub } } ``` 并且在web.xml中配置过滤器 ```xml CharacterEncoding com.ouyang.filter.CharacterEncoding CharacterEncoding /* ``` ![image-20220516141024142](https://img.bones2674.com/Picgo/202205202327360.png) 字符中文乱码问题解决了,但是查询还是报错,那肯定是sql语句的问题了 ![image-20220516142215967](https://img.bones2674.com/Picgo/202205202327361.png) 打印sql后发现了问题所在,where后面没加空格,加上空格之后成功查询出结果 ![image-20220516142303968](https://img.bones2674.com/Picgo/202205202327362.png) # 十、添加用户功能实现 **添加用户的原理:** - 实际上在前端有一个隐藏的form表单,当我点击添加用户信息按钮之后,通过js的调用即可使隐藏的form表单显示出来 ![image-20220517172543190](https://img.bones2674.com/Picgo/202205202327363.png) ```html
``` 前端也搞定之后,就可以写后端页面了,根据前端页面的form表单请求地址可以看到,添加表单是请求**/user.do?mehtod=add** 所以我们依然在UserServlet上加一个add方法 ```java String UserName = req.getParameter("Username"); String Password = req.getParameter("Password"); String Email = req.getParameter("Email"); ``` 这里从前端接收三个参数,然后调用Service层的方法,这里我并没有对前端传过来的参数进行判空,因为前端已经对参数进行判空了,如果为空则传不到后端 > # Dao层 > > ```java > public Integer add ( String Username, String Password, String Email ) { > Connection connection = DataBaseDao.getConnection(); > PreparedStatement statement = null; > String sql = "insert into User(UserName,PassWord,Email) values(?,?,?)"; > Integer rs = null; > try { > statement = connection.prepareStatement(sql); > statement.setString(1,Username); > statement.setString(2,Password); > statement.setString(3,Email); > rs = statement.executeUpdate(); > } catch (SQLException throwables) { > throwables.printStackTrace(); > }finally { > DataBaseDao.closeResource(connection,statement,null); > } > return rs; > } > ``` > # Service层 > > ```java > public Integer add ( String Username, String Password, String Email ) { > return userDao.add(Username,Password,Email); > } > ``` > # Servlet层 > > ```java > public void add(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException{ > String UserName = req.getParameter("Username"); > String Password = req.getParameter("Password"); > String Email = req.getParameter("Email"); > userService.add(UserName, Password, Email); > resp.sendRedirect("/user.do?method=getuserlist"); > } > ``` 然后就报404错误,因为我添加用户完后,需要重定向对网页进行一个更新列表展示,然后就报了404错误,重定向的路径有问题 **一、使用绝对路径**  **response.sendRedirect(“绝对路径的URL地址”);**    **如http://www.baidu.com,直接跳转到该地址(注意,http头不可省略)。**    **也可以使用本地服务器绝对路径地址http://localhost:8080/myProject/123.mp3。**    **这个是最简单的,可以访问到任何你可以访问的HTTP资源。** **二、使用相对路径(这里以本地服务器地址为例)**    **1、路径前面没有斜线“/”**    **如response.sendRedirect(example/index.html);**    **容器会根据原先请求的URL建立一个完整的路径,如原先路径是http://localhost:8080/myProject/ap p/index.html , 那么会直 接访问到http://localhost:8080/myProject/app/example/index.html**    **2、路径前面有斜线“/”**    **如response.sendRedirect(/example/index.html);**    **容器会根据原先请求的URL,相对于Web应用本身,建立一个完整的路径,如原先路径是http://localh ost:8080/myProject/app/index.html , 那么会直接访问到http://localhost:8080/example/inde x.html**** 这里我用重定向的时候前面加了**/**,导致我重定向到web本身的完整路径 ![image-20220517182920835](https://img.bones2674.com/Picgo/202205202327364.png) 成功跳转! # 十一、修改用户信息功能实现 跟添加用户信息一样,点击修改按钮之后,有一个隐藏的修改框会展示出来,并调用bootstrap的数据回填机制,将表单中的数据回填到表格中进行修改 ![image-20220523090012658](https://img.bones2674.com/Picgo/202205230900912.png) id因为在数据库中是主键自增长所以设定为无法修改,只能修改用户名、密码、电子邮箱等基础信息、前端页面搞定开始写后端 这次我们先从Servlet层开始写 ```java public void updata ( HttpServletRequest req, HttpServletResponse resp ) throws IOException { Integer id = Integer.valueOf(req.getParameter("id")); String UserName = req.getParameter("username"); String PassWord = req.getParameter("password"); String Email = req.getParameter("eamil"); userService.updata(id,UserName,PassWord,Email); resp.sendRedirect("user.do?method=getuserlist"); } ``` 一样是先从前端接受传参然后调用Service层的业务逻辑 ```java public void updata ( Integer id, String Username, String Password, String Email ) { Integer rs = userDao.updata(id,Username,Password,Email); if(rs!=1)throw new RuntimeException("修改用户信息失败"); } ``` Service层写好后开始写Dao层 ```java public Integer updata ( Integer id, String Username, String Password, String Email ) { Connection connection = DataBaseDao.getConnection(); PreparedStatement statement = null; String sql = "update User set UserName = ?,PassWord = ?,Email = ? where id = ?"; Integer rs = null; try { statement = connection.prepareStatement(sql); statement.setString(1,Username); statement.setString(2,Password); statement.setString(3,Email); } catch (SQLException throwables) { throwables.printStackTrace(); }finally { DataBaseDao.closeResource(connection,statement,null); } return rs; } ``` 全部写完后我们测试一下 ![image-20220523092033741](https://img.bones2674.com/Picgo/202205230920903.png) 这里报了一个500错误,检查过后发现,我的Dao层并没有执行sql语句 犯了一个小小的错误 补上之后测试又报了一个500错误,这次控制台报错**”No value specified for parameter 4“** ![image-20220523092446587](https://img.bones2674.com/Picgo/202205230924855.png) 第四个占位符有问题,这里我没有把id传进去,又是一个小错误 测试后成功 ![image-20220523093254595](https://img.bones2674.com/Picgo/202205230932734.png) # 十二、删除用户信息功能实现 删除框也是一个隐藏的表单,点击删除按钮之后表单可显示,流程一样,我就直接写代码了 原理是根据id来删除,因为用户的id是唯一的 > ## Dao层 ```java public Integer del ( String id ) { Connection connection = DataBaseDao.getConnection(); PreparedStatement statement = null; String sql = "delete from User where Id = "+id; Integer rs = null; try { statement = connection.prepareStatement(sql); rs = statement.executeUpdate(); } catch (SQLException throwables) { throwables.printStackTrace(); }finally { DataBaseDao.closeResource(connection,statement,null); } return rs; } ``` > ## Service层 ```java public void del ( String id ) { Integer rs = userDao.del(id); if(rs==-1)throw new RuntimeException("删除用户信息失败"); } ``` > ## Servlet层 ```JAVA public void del(HttpServletRequest req, HttpServletResponse resp) throws IOException { String id = req.getParameter("id"); userService.del(id); resp.sendRedirect("user.do?method=getuserlist"); } ``` 测试之后成功可以删除用户信息 ![image-20220523094830518](https://img.bones2674.com/Picgo/202205230948599.png) **至此,用户功能全部实现** # 十三、订单管理功能 订单信息的操作无非跟用户操作差不多,只不过在涉及到数据库查询时需要用到多表查询 ## 13.1创建订单实体类和数据库设计 在pojo包下创建一个映射数据库的实体类 ![image-20220528130551305](https://img.bones2674.com/Picgo/202205281305451.png) 数据表也设计好了![image-20220528123616706](https://img.bones2674.com/Picgo/202205281236815.png) 然后我们就可以开始写业务代码了,先创建odersServlet类,方法和UserServlet一样用mehod传参判断是哪个请求实现Servlet的复用性。 ## 13.2、订单信息列表展示 > Servlet层 ```java public void getoderlist(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setAttribute("list",odersService.getlist()); req.getRequestDispatcher("/admin/cdblist.jsp").forward(req, resp); } ``` > Service层 ```java public Oders getlist () { return odersDao.getlist(); } ``` > Dao层 ```java public List getuserlist ( ) { Connection connection = DataBaseDao.getConnection(); PreparedStatement statement = null; String sql = "select * from User"; ResultSet rs = null; List list = new ArrayList<>(); try { statement = connection.prepareStatement(sql); rs = statement.executeQuery(); while (rs.next()){ User user = new User(); user.setUserid(rs.getInt(1)); user.setUsername(rs.getString(2)); user.setUserpassword(rs.getString(3)); user.setEmail(rs.getString(4)); list.add(user); } } catch (SQLException throwables) { throwables.printStackTrace(); }finally { DataBaseDao.closeResource(connection,statement,rs); } return list; } ``` 启动tomcat进行测试 ![image-20220528130920175](https://img.bones2674.com/Picgo/202205281309242.png) 果然不出所料报500错误了,然后我们开始排查报错原因,从报错原因可以看出是Servlet层doGet方法的问题 ![image-20220528131047774](https://img.bones2674.com/Picgo/202205281310953.png) 果然,我们在doGet方法里应该改成调用doPost方法,更改后继续测试 ![image-20220528131509620](https://img.bones2674.com/Picgo/202205281315692.png) 可以成功查出订单信息列表 ## 13.3订单信息添加功能实现 我们把前端页面的数据和字段都相对应的更改一下,因为前端页面是从用户列表页面重构过来的 > ## Servlet层 ```java public void addoders(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String OrderNumber = req.getParameter("OrderNumber"); String cdbName = req.getParameter("cdbName"); String RentPeople = req.getParameter("RentPeople"); odersService.addoders(OrderNumber,cdbName,RentPeople); resp.sendRedirect("oders.do?method=list"); } ``` > ## Service层 ```java public void addoders ( String OrderNumber, String cdbName, String RentPeople ) { Integer rs = odersDao.addoders(OrderNumber, cdbName, RentPeople); if(rs!=1)throw new RuntimeException("添加订单信息失败"); } ``` > ## Dao层 ```java public Integer addoders ( String OrderNumber, String cdbName, String RentPeople ) { Connection connection = DataBaseDao.getConnection(); PreparedStatement statement = null; String sql = "insert into oders(OrderNumber,cdbName,RentPeople) values(?,?,?)"; Integer rs = null; try { statement = connection.prepareStatement(sql); statement.setString(1,OrderNumber); statement.setString(2,cdbName); statement.setString(3,RentPeople); rs = statement.executeUpdate(); } catch (SQLException throwables) { throwables.printStackTrace(); }finally { DataBaseDao.closeResource(connection,statement,null); } return rs; } ``` 写完业务逻辑代码之后进行测试 ![image-20220528212246932](https://img.bones2674.com/Picgo/202205282122024.png) 测试成功 ## 13.4订单信息删除功能实现 > ## Servlet层 ```java public void del(HttpServletRequest req, HttpServletResponse resp) throws IOException { Integer id = Integer.valueOf(req.getParameter("id")); odersService.deloders(id); resp.sendRedirect("oders.do?method=list"); } ``` 从前端传入参数mehod为del后,执行del方法 根据前端传参”id“到数据库以id进行删除 > ## Service层 ```java public void deloders ( Integer id ) { Integer rs = odersDao.deloders(id); if(rs!=1) throw new RuntimeException("删除订单信息失败"); } ``` > ## Dao层 ```java public Integer deloders ( Integer id ) { Connection connection = DataBaseDao.getConnection(); PreparedStatement statement = null; String sql = "delete from oders where Id = "+id; Integer rs = null; try { statement = connection.prepareStatement(sql); rs = statement.executeUpdate(); } catch (SQLException throwables) { throwables.printStackTrace(); }finally { DataBaseDao.closeResource(connection,statement,null); } return rs; } ``` 经过测试后成功 ![image-20220528220554398](https://img.bones2674.com/Picgo/202205282205463.png) ## 13.5订单信息修改功能实现 修改前端后,依然使用bootstrap回填数据把表单数据填到隐藏的表单中 然后创建update方法判断前端传入的method为update执行update方法 向Service层传入id,odernumber,cdbname,rentpeople 注册好Servlet后,开始写Servlet层的东西,这里Servlet层只负责视图跳转 还需要创建Service层进行业务逻辑判断,Service层又需要Dao层从数据库中拿数据库Oders信息 跟Dao层一样在Service层下创建一个接口和实现类,OdersServiceImpl实现OdersService接口 > ## Dao层 ```java public Integer update ( Integer id, String ordernumber, String cdbname, String rentpeople ) { Connection connection = DataBaseDao.getConnection(); PreparedStatement statement = null; String sql = "update oders set OrderNumber = ?,cdbName = ?,RentPeople = ? where id = ?"; Integer rs = null; try { statement = connection.prepareStatement(sql); statement.setString(1,ordernumber); statement.setString(2,cdbname); statement.setString(3,rentpeople); statement.setString(4, String.valueOf(id)); rs = statement.executeUpdate(); } catch (SQLException throwables) { throwables.printStackTrace(); }finally { DataBaseDao.closeResource(connection,statement,null); } return rs; } ``` > ## Servlet层 ```java public void update(HttpServletRequest req, HttpServletResponse resp) throws IOException { Integer id = Integer.valueOf(req.getParameter("id")); String ordersnumber = req.getParameter("ordersnumber"); String cdbname = req.getParameter("cdbname"); String rentpeople = req.getParameter("rentpeople"); odersService.update(id,ordersnumber,cdbname,rentpeople); resp.sendRedirect("oders.do?method=list"); } ``` > ## Service层 ```java public void update ( Integer id, String ordernumber, String cdbname, String rentpeople ) { Integer rs = odersDao.update(id,ordernumber,cdbname,rentpeople); if(rs!= 1)throw new RuntimeException("修改订单信息失败"); } ``` 业务代码写好后进行测试 ![image-20220531202122176](https://img.bones2674.com/Picgo/202205312021428.png) 这里没报404错误页没报500错误,后台页没报程序异常错误 检查数据库后发现页也没修改,一般来说这种情况后台没报错,前端页面没跳转,都是没有执行业务代码的原因 检查后发现是前端请求的地址错了,update写成了updata ![image-20220531202656126](https://img.bones2674.com/Picgo/202205312026201.png) 修改后又报了一个500错误 检查后发现是从前端接参的时候写错name了 ![image-20220531203246089](https://img.bones2674.com/Picgo/202205312032152.png) 修改后测试成功 ## 13.6订单信息查询功能实现 订单信息查询需要从前端传4个参数到后端 可以根据id,订单编号,充电宝名称,租借人进行查询 这里的话Dao层连接数据库查询的时候就需要用变量进行嵌套了,不能单纯的插值 先创建Servlet层,根据method方法执行对应的方法,实现Servlet的复用性 > ## Servlet层 ```java public void search ( HttpServletRequest req, HttpServletResponse resp ) throws ServletException, IOException { String key = req.getParameter("key"); String value = req.getParameter("value"); req.setAttribute("list",odersService.search(key,value)); req.getRequestDispatcher("/admin/cdblist.jsp").forward(req,resp); } ``` 这里不用重定向而用转发是因为查出来的Oders要单独展示出来,重定向就又重新走了一遍getlist的请求 > ## Service层 ```java public List search ( String key, String value ) { return odersDao.search(key,value); } ``` > ## Dao层 ```java public List search ( String key, String value ) { Connection connection = DataBaseDao.getConnection(); PreparedStatement statement = null; String sql = "select * from User where "+key+" like '%"+value+"%'"; ResultSet rs = null; List list = new ArrayList<>(); try { statement = connection.prepareStatement(sql); rs = statement.executeQuery(); while (rs.next()){ Oders oders = new Oders(); oders.setId(rs.getInt(1)); oders.setOrderNumber(rs.getString(2)); oders.setCdbName(rs.getString(3)); oders.setRentPeople(rs.getString(4)); list.add(oders); } } catch (SQLException throwables) { throwables.printStackTrace(); }finally { DataBaseDao.closeResource(connection,statement,rs); } return list; } ``` 业务代码都写好后开始测试 测试查询订单编号含有763的 ![image-20220531205722305](https://img.bones2674.com/Picgo/202205312057392.png) ![image-20220531205700728](https://img.bones2674.com/Picgo/202205312057802.png) 测试成功 # 十四、租借查询功能 主要用来展示二级联动信息查询 ## 1.41前端页面重构 首先把前端页面进行重构 该页面主要用于进行二级联动用户信息和订单信息查询 将其他功能删除,重新只用于展示列表信息 1、第一个页面用于用户查询租借人的信息 2、第二个页面用于展示查询出来的信息列表展示 ## 14.2创建实体类 创建一个新的poji实体类RentPeople用来一一映射从数据库多表查询出来的数据 ```java public class RentPeople { private Integer id; private String rentpeople; private String cdbname; private String ordernumber; public Integer getId ( ) { return id; } public void setId ( Integer id ) { this.id = id; } public String getRentpeople ( ) { return rentpeople; } public void setRentpeople ( String rentpeople ) { this.rentpeople = rentpeople; } public String getCdbname ( ) { return cdbname; } public void setCdbname ( String cdbname ) { this.cdbname = cdbname; } public String getOrdernumber ( ) { return ordernumber; } public void setOrdernumber ( String ordernumber ) { this.ordernumber = ordernumber; } } ``` 实体类分别对应: - 信息id - 租借人名字 - 充电宝名字 - 订单编号 ## 14.3创建Servlet层 Servlet层的逻辑是 1. 从前端接收**租借人名字**传入到后端 2. 执行Service层 3. Dao层连接数据库进行多表查询 4. 对应的数据封装成RentPeople类传入到前端 5. 再跳转到rentpeoplelist.jsp页面进行展示 ```java public void RenotPeopleList(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { List list = rentPeopleService.getrentpeople(); req.setAttribute("list",list); req.getRequestDispatcher("/admin/rentpeople.jsp").forward(req,resp); } ``` ## 14.4创建Service层 ```java public class RentPeopleServiceImpl implements RentPeopleService { private RentPeopleDao rentPeopleDao; public RentPeopleServiceImpl ( ) { this.rentPeopleDao = new RentPeopleDaoImpl(); } @Override public List getrentpeople ( ) { return rentPeopleDao.getrentpeoplelist(); } } ``` ## 14.5创建Dao层 ```java public List getrentpeoplelist () { Connection connection = DataBaseDao.getConnection(); PreparedStatement statement = null; String sql = "select User.Id,UserName,cdbName,OrderNumber, public List getrentpeoplelist () { Connection connection = DataBaseDao.getConnection(); PreparedStatement statement = null; String sql = "select User.Id,UserName,cdbName,OrderNumber,Email from User,oders where User.UserName = oders.RentPeople"; ResultSet rs = null; List list = new ArrayList<>(); try { statement = connection.prepareStatement(sql); rs = statement.executeQuery(); while (rs.next()){ RentPeople rentPeople = new RentPeople(); rentPeople.setId(rs.getInt(1)); rentPeople.setRentpeople(rs.getString(2)); rentPeople.setEmail(rs.getString(5)); rentPeople.setCdbname(rs.getString(3)); rentPeople.setOrdernumber(rs.getString(4)); list.add(rentPeople); } } catch (SQLException throwables) { throwables.printStackTrace(); }finally { DataBaseDao.closeResource(connection,statement,rs); } return list; }Email from User,oders where User.UserName = oders.RentPeople"; ResultSet rs = null; List list = new ArrayList<>(); try { statement = connection.prepareStatement(sql); rs = statement.executeQuery(); while (rs.next()){ RentPeople rentPeople = new RentPeople(); rentPeople.setId(rs.getInt(1)); rentPeople.setRentpeople(rs.getString(2)); rentPeople.setCdbname(rs.getString(3)); rentPeople.setOrdernumber(rs.getString(4)); list.add(rentPeople); } } catch (SQLException throwables) { throwables.printStackTrace(); }finally { DataBaseDao.closeResource(connection,statement,rs); } return list; } ``` ![image-20220603180014312](https://img.bones2674.com/Picgo/202206031800378.png) 测试后可以成功全部查询出租借信息 # SSM框架整合 ## 1、前言 在写原生servlet+jsp的时候刚开始学习mybtis,刚好写完代码之后也学完了三个框架spring,springmvc,mybits 所以在原生servlet+jsp的基础上,我就用SSM框架整合后再写一个项目 ## 2、开发技术 前端:bootstrp 后端:SSM框架:spring+springmvc+mybtis ## 3、配置环境 ### 1、引入依赖包 使用maven引入相关依赖 ```xml junit junit 4.11 test org.springframework spring-webmvc 5.2.7.RELEASE org.springframework spring-jdbc 5.2.7.RELEASE org.mybatis mybatis 3.5.5 org.mybatis mybatis-spring 2.0.3 mysql mysql-connector-java 8.0.21 com.mchange c3p0 0.9.5.5 jstl jstl 1.2 javax.servlet javax.servlet-api 4.0.1 javax.servlet.jsp javax.servlet.jsp-api 2.3.1 org.projectlombok lombok 1.18.12 ``` ### 2、Web.xml配置 ```xml contextConfigLocation classpath:spring.xml org.springframework.web.context.ContextLoaderListener springmvc org.springframework.web.servlet.DispatcherServlet contextConfigLocation classpath:springmvc.xml springmvc / CharacterEncodingFilter org.springframework.web.filter.CharacterEncodingFilter encoding utf-8 forceRequestEncoding true forceResponseEncoding true CharacterEncodingFilter /* default *.css *.js *.jpg ``` ### 3、Mybatis配置 ```xml ``` ### 4、spring-mvc配置 ```xml ``` ### 5、spring配置 ```xml ``` ## 4、登录功能实现 ### 4.1Controller层 由于使用了框架之后,开发变得更简洁了, 首先创建一个LoginController的类,然后用注解@Controller让这个类被spring接管 @requestmapping控制前端跳转 其次用一个UserForm的封装类来接收前端数据,前端数据会被封装成一个对象传到后端 ![image-20220609234604311](https://img.bones2674.com/Picgo/202206092346449.png) 这里我用了**@Data**注解,他是插件lombok的一个功能,可以快速通过注解构造实体类 ![image-20220609234718075](https://img.bones2674.com/Picgo/202206092347250.png) 对于Controller层我又构建了一个dto实体类,用来接收通过后端查询到的User和一个code码,然后Controller通过判断由Service层传来的UserDto中的code码来判断是否登录成功,然后跳转到不同的页面 ![image-20220609235659496](https://img.bones2674.com/Picgo/202206092356641.png) ```java public String login ( Model model, UserForm user, HttpSession session ) { UserDto userDto = loginService.login(user); Integer code = userDto.getCode(); User user1 = userDto.getUser(); if (code == 1) { session.setAttribute("user", user1); return "index"; }else if (code == -1) { model.addAttribute("error", "用户名或者密码错误"); return "login"; } model.addAttribute("error","用户名不存在"); return "login"; } ``` > ### 逻辑 登录成功(密码匹配成功)->code=1 登录失败(密码匹配错误)->code=-1 数据库查找失败(用户名不存在)->code=0 因为引入了SpringMVC的原因,每个方法只需要返回需要跳转的视图(View)名字即可,SpringMVC会自动匹配前缀和后缀 **model.addAttribute()**类似于**request.setAttribute()** 总体来说,SpringMVC就是利用**Model and View** (模型和视图) ### 4.2Service层 Service层主要对从数据库获取到的用户和前端传过来的用户做密码匹配,然后匹配成功则向UserDto中将code码设置为1 如果匹配不成功则将code码设置为-1 如果查找失败,不存在此用户则将code码设置为0 ```java public UserDto login ( UserForm user ) { User loginuser = loginMapper.login(user.getUsername()); if (loginuser != null) { if (loginuser.getUserPassword().equals(user.getPassword())) { UserDto userDto = new UserDto(); userDto.setUser(loginuser); userDto.setCode(1); return userDto; }else { UserDto userDto = new UserDto(); userDto.setUser(null); userDto.setCode(-1); return userDto; } }else { UserDto userDto1 = new UserDto(); userDto1.setCode(0); return userDto1; } } ``` ### 4.3Mapper层 用了mabtis框架后,只需要创建一个xml文件后让他的命名空间指向相对应的接口就可以了 并定义他的返回类型 ```xml ``` ## 5、注销功能实现 ```java @RequestMapping("/loginout") public String loginout(HttpSession session){ session.removeAttribute("user"); return "error"; } ``` 在AccountController层在定义一个方法进行注销操作 ## 6、用户列表展示 ### 5.1Controller层 ```java @RequestMapping("userlist") public String getuserlist( Model model ){ List userList = userService.getuserlist(); model.addAttribute("list",userList); return "userlist"; } ``` 通过访问/user/userlist来进入这个请求,然后从数据库提取出的用户列表展示到前端 通过model添加list ### 5.2Service层 ```java public List getuserlist ( ) { List userList = userMapper.getuserlist(); return userList; } ``` Service层没什么业务逻辑时只需要返回调用mapper层从数据库查出来的数据即可 ### 5.3Mapper层 ```java ``` ## 7、查询用户功能 ### 7.1Controller层 ```java @RequestMapping("search") public String SearchUser ( String key, String value,Model model) { List userList = userService.search(key,value); model.addAttribute("list",userList); return "userlist"; } ``` ### 7.2Service层 ```java public List search ( String key, String value ) { if (key.equals("")) return this.userMapper.getuserlist(); List list = null; switch (key) { case "Id": list = userMapper.searchById(value); break; case "UserName": list = userMapper.searchByUserName(value); break; case "Eamil": list = userMapper.searchByEmail(value); break; } return list; } ``` ### 7.3Mapper层 ```xml ``` ## 8、添加用户功能 ### 8.1Controller层 因为后面的代码基本都差不多,我就不做多的解释了直接贴代码了 ```java @RequestMapping("adduser") public String addUser( AddUserForm userForm){ try { userService.adduser(userForm); } catch (Exception e) { e.printStackTrace(); } return "redirect:/user/userlist"; } ``` ### 8.2Service层 ```java public void adduser ( AddUserForm userForm ) { try { userMapper.adduser(userForm); } catch (Exception e) { e.printStackTrace(); } } ``` ### 8.3Mapper层 ```xml insert into User(UserName,PassWord,Email) values (#{Username},#{Password},#{Email}) ``` ## 9、修改用户功能 ### 9.1Controller层 ```java @RequestMapping("updateuser") public String updateUser ( User user ) { try { userService.updateuser(user); } catch (Exception e) { e.printStackTrace(); } return "redirect:/user/userlist"; } ``` 这里需要用重定向重新请求getlist方法来获取用户列表进行页面刷新 ### 9.2Service层 ```java public void updateuser ( User user ) { try { userMapper.updateuser(user); } catch (Exception e) { e.printStackTrace(); } } ``` ### 9.3Mapper层 ```xml update User set UserName = #{UserName},PassWord = #{PassWord},Email = #{Email} where id = #{Id} ``` ## 10、修改密码功能 ### 10.1Controller层 ```java @RequestMapping("/updatepwd") public String update( Model model, String rnewpassword, HttpSession session ){ User user = (User) session.getAttribute("user"); int flag = updatePwdService.update(user.getUserName(),rnewpassword); if(flag==1){ model.addAttribute("message","密码修改成功"); return "pwdmodify"; }else { model.addAttribute("message","密码修改失败"); return "pwdmodify"; } } ``` ### 10.2Service层 ```java public Integer update ( String username, String pwd ) { int flag = updatePwdMapper.update(username,pwd); if(flag ==1 ){ return 1; }else { return 0; } } ``` ### 10.3Mapper层 ```xml update User set PassWord = #{arg0} where UserName = #{arg1} ``` ![image-20220613132816273](https://img.bones2674.com/Picgo/202206131328389.png) 编写完代码后出现500错误 百度找错后得到解决方法: Mybtis传入两个或以上基本类型的参数时,如String类型,int类型,不能用变量名表示 - **当你的参数类型为实体类型时,可以使用 #{实体属性名} 。** - **当你的参数类型为基本类型时,如(Integer,String ,Boolean 等),使用 #{arg0},#{arg1}……** - **当你的方法拥有多种参数时,parameterType属性也可以不写(其实基本上都可以不写)** 所以这里Mybtis会报错找不到参数pwd ```java update User set PassWord = #{pwd} where UserName = #{username} ``` 我们这里的#{pwd}和#{username}应该改为#{arg0}和#{arg1} ![image-20220613135359442](https://img.bones2674.com/Picgo/202206131353504.png) ![image-20220613135411655](https://img.bones2674.com/Picgo/202206131354878.png) ## 11、租借人查询 ### 11.1Controller层 ```java @RequestMapping("/list") public String list ( Model model ) { List list = rentPeopleService.list(); model.addAttribute("list", list); return "rentpeople"; } ``` ### 11.2Service层 ```java public List list ( ) { return rentPeopleMapper.list(); } ``` ### 11.3Mapper层 ```xml ``` # 心得体会 至此共享充电宝后台管理系统就开发完成啦 这次大作业一共写了两个,一个是用原生的Servlet+jsp写的,一个是用SSM框架整合后写的 在用Servlet写的时候正在学习Mybtis,然后学的spring,再然后学的springmvc 我是在B站上找的教程学的 差不多用servlet写的写完了,三个框架也学完了,学习三个框架差不多耗时30多个小时 然后就立马起手开始写框架整合的系统了 用了SSM框架后真的便捷了很多 用spring容器管理再也不用new对象了,通过@atuowired直接就可以取出来用 用mybtis也只需要写一个sql语句就行了,非常方便 springmvc就只要管理好视图跳转就行,而且有实体类的话,前端传入的参数会自动封装成对象 在写这篇心得体会的时候,我正在学习springboot+vue,发现他比SSM更加便捷,因为使用SSM框架的时候你需要配置很多配置文件,简直就是**配置地狱**,springboot完美的解决了这个问题,我们可以简单的配置一下,然后其他的都使用它默认的配置参数 写两个版本的时候也都遇见了很多的问题,通过查询相关材料也都解决了,总之一行一行代码纯手工敲出来最后完成的时候真的成就感非常十足,也非常开心 两个版本代码量应该有几千行,加上这个差不多将近1万字的开发文档,项目从4月23日开始启动,到今天6月1日完成 总耗时将近1个半月,每天都会敲一些代码,然后因为刚开始学习SSM,我记得有一个报错,我花了一个下午最后才解决的 通过这次大作业,我发现其实还有很多东西需要去学,而且我也知道现在只有少部分公司在使用SSM,很多大公司都是用微服务,springcloud,mybtis plus很多很多,都还有很多未知的领域要去学习,SSM只是一个起步,希望以后的自己越来越努力!