SMBMS系统开发

1.前期准备

时间是2020.6.28,先在狂神的群里下载到了源代码,本想学狂神直接部署在tomcat上直接运行,后来发现,貌似必须打包发布才能直接运行,于是开始进行调试。

  1. 首先是打开文件,由于没有maven文件,idea报了一堆找不到类的错误,在自动提示的帮助下,让idea自动导入的包,解决了问题
  2. 似乎是由于java文件版本不对,idea也自动更新了版本
  3. 无法执行数据库连接

衍生问题:数据的导入

  1. 数据库的帐号与密码对应的是服务器:
  2. 需要新建一个数据库文件再执行导入操作
  3. 更改数据库执行文件
  4. 执行sql文件

衍生问题:问什么用类加载器来加载properties文件

通过这种方式能直接使用相对路径获取对应的配置文件,类加载器相关内容请看反射与注解

衍生问题:Class.forname()方法加载驱动类

Class.forName()方法

此方法意味着是:加载参数指定的类,并初始化它。

在jdbc连接数据库中的应用

为什么要执行Class.forName('驱动类名')方法:将驱动类的类文件加载到内存中,并且形成一个描述此驱动类结构的Class类实例,并初始化此驱动类,这样jvm就可以使用它了,这就是Class.forName()方法的含义。

在JDBC中 Class.forName(“com.mysql.jdbc.Driver”),如果换成getClass().getClassLoader().loadClass(“com.mysql.jdbc.Driver”),就不可以,因为它只是想JVM装载了Driver的类信息,但是没有实例化,也就不能执行相应的操作,因为Driver是需要被初始化才能被使用的。

Class.forName(className)装载的class已经被实例化,classLoader.loadClass().则只是将信息装载给JVM。

项目搭建准备

  1. 搭建一个maven web项目

  2. 配置tomcat

  3. 测试项目能否跑起来

  4. 导入jar包

  5. 创建项目包结构

  6. 编写实体类

    ORM映射

  7. 编写基础公共类

    1. 数据库配置文件
    2. 编写数据库公共类
    3. 编写字符过滤器
  8. 导入静态资源

2.登录功能

  1. 编写前端页面

  2. 设置首页

    <!--设置欢迎页面-->
    <welcome-file-list>
       <welcome-file>login.jsp</welcome-file>
    </welcome-file-list>
    
  3. 编写dao层登陆用户登录的接口
    //得到登录用户
    public User getLoginUser(Connection connection, String usercode) throws SQLException;
    
  4. 编写dao接口实现类
    public class UserDaoImpl implements UserDao {
       @Override
       public User getLoginUser(Connection connection, String usercode) throws SQLException {
           PreparedStatement statement = null;
           ResultSet resultSet = null;
           User user = null;
           if (connection != null) {
               String sql = "select * from smbms_user where usercode =?";
               Object[] params = {usercode};
               resultSet = BaseDao.execute(connection, resultSet, statement, sql, params);
               if (resultSet.next()) {
                   user = new User();
                   user.setId(resultSet.getInt("id"));
                   user.setUserCode(resultSet.getString("userCode"));
                   user.setUserName(resultSet.getString("userName"));
                   user.setUserPassword(resultSet.getString("userPassword"));
                   user.setGender(resultSet.getInt("gender"));
                   user.setBirthday(resultSet.getDate("birthday"));
                   user.setPhone(resultSet.getString("phone"));
                   user.setAddress(resultSet.getString("address"));
                   user.setUserRole(resultSet.getInt("userRole"));
                   user.setCreatedBy(resultSet.getInt("createdBy"));
                   user.setCreationDate(resultSet.getTimestamp("creationDate"));
                   user.setModifyBy(resultSet.getInt("modifyBy"));
                   user.setModifyDate(resultSet.getTimestamp("modifyDate"));
               }
    
               BaseDao.closeResource(null,statement,resultSet);
           }
           return user;
       }
    }
    
  5. 编写业务层接口
    //用户登录
    public User login(String usercode,String password);
    
  6. 业务层实现类
    public class UserServiceImpl implements UserService {
    
       //业务层都会调用dao层,所以要引入dao层;
       private UserDao userDao;
       public UserServiceImpl(){
           userDao = new UserDaoImpl();
       }
    
       @Override
       public User login(String usercode, String password) {
           Connection connection = null;
           User user = null;
    
           connection = BaseDao.getConnection();
           try {
               user  = userDao.getLoginUser(connection, usercode);
    
           } catch (SQLException e) {
               e.printStackTrace();
           }finally {
               BaseDao.closeResource(connection,null,null);
           }
           //对用户名的密码进行判断
           if (password.equals(user.getUserPassword())){
               return user;
           }else {
               return null;
           }
       }
    }
    
  7. 编写servlet
    public class LoginServlet extends HttpServlet {
       /*
       *  servlet:控制层,调用业务层
       */
       @Override
       protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
           System.out.println("LoginServlet————start....");
    
           //获取用户名和密码
           String username = req.getParameter("userCode");
           String password = req.getParameter("userPassword");
    
           //和数据库中的密码进行对比,调用业务层
           UserServiceImpl userService = new UserServiceImpl();
           User user = userService.login(username, password);
           //用户存在
           if (user!=null){
               //将用户的信息放到session中;
               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);
       }
    }
    
    
  8. 注册servlet

  9. 测试访问,保证成功

衍生问题:为什么在basedao和service层中传递参数而不是直接new出来?

因为要考虑资源释放,baseDao的closeResource方法需要关闭开着的公共资源,比如connection,resultSet等,而且只关闭当前调用的资源,如果在basedao的getconnection现创建,就必须在本方法内关闭资源,那么关闭资源的结构就低于basedao的其他方法一级别。

正确的做法是由调用者提供资源,调用dao得到数据,调用者关闭资源,这样一个流程,在本项目中的流程就是usedao提供参数,usedao关闭资源,然后再由service调用usedao提供参数,service关闭资源

注意:connection由服务层提供,同样由服务层关闭

3.退出功能

由于退出只需要移除session中的用户,所以不需要操作数据库也就不同写dao和service,只需要在servlet中进行就好

  1. logoutservlet
    public class LogoutServlet extends HttpServlet {
       @Override
       protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
           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);
       }
    }
    
  2. 注册xml
      <servlet>
           <servlet-name>logoutServlet</servlet-name>
           <servlet-class>com.zhaox.servlet.userServlet.LogoutServlet</servlet-class>
       </servlet>
       <servlet-mapping>
           <servlet-name>logoutServlet</servlet-name>
           <url-pattern>/jsp/logout.do</url-pattern>
       </servlet-mapping>
    

衍生问题:注册xml中的路径与前端显示不一致

前端所展现的跳转页面的路径,应该完全与xml中注册的一致,否则会造成404错误

登陆拦截优化

  1. 编写一个过滤器
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 {
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        HttpServletResponse resp = (HttpServletResponse) servletResponse;

        //从session中获取用户,注意此处getAttribute中输入的是字符串
        User user = (User)req.getSession().getAttribute(Constants.USER_SESSION);

        if(user==null){
            //req.getContextPath代表当前目录
            resp.sendRedirect(req.getContextPath()+"/error.jsp");

        }else {
            filterChain.doFilter(servletRequest,servletResponse);
        }
    }

    @Override
    public void destroy() {

    }


}

  1. 注册
<filter>
    <filter-name>SysFilter</filter-name>
    <filter-class>com.zhaox.filter.SysFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>SysFilter</filter-name>
    <url-pattern>/jsp/*</url-pattern>

4.密码修改功能

做一个项目功能,都要从底层往上写,建楼要从下往上建,那这个“从下往上“是指什么意思呢。

MVC的结构层:"与数据库进行交互的“Dao层——”专心于业务的“Service层——”负责传话和页面跳转的“Servlet层

所以写代码要先从Dao开始,向上方写,在开始敲键盘时要先想好要做什么。

  1. 先写接口
    //修改用户密码
    public int updatePwd(Connection connection, int id ,int password) throws SQLException;
    
  2. 再写接口实现
    1. 要小心,传过来的参数再上一层可能忘了传,所以要先做个判断,加个if判断连接存不存在之类的
    2. 在这里再仔细思考一下,basedao是不做任何新东西的创建的,因为要把创建的东西关闭,以此类推:最底层的东西是不会新建的,都是传过来的参数,在传过来的的地方做关闭处理!
    //修改用户密码
    @Override
    public int updatePwd(Connection connection, int id, int password) throws SQLException {
       PreparedStatement preparedStatement = null;
    
       String sql = "UPDATE smbms_user set userPassword = ? where id = ?";
    
       Object params[] = {password,id};
    
       int execute = 0;
       if (connection!=null){
           execute = BaseDao.execute(connection, preparedStatement, sql, params);
       }
       return execute;
    }
    
  3. 写service层接口和他的实现类
    //根据id修改密码
       public boolean updatePwd( int id , String password) ;
    
    @Override
    public boolean updatePwd(int id, String password) {
    
       boolean flag = false;
       Connection connection = null;
       try {
           connection = BaseDao.getConnection();
           if (userDao.updatePwd(connection,id,password)>0){
               flag = true;
           }
       } catch (SQLException e) {
           e.printStackTrace();
       }finally {
           BaseDao.closeResource(connection,null,null);
       }
       return flag;
    }
    
  4. 写servlet

    注意,写servlet要先注册xml防止报错

    同时,思考一个问题,我们是用旧密码来确定密码的,但是在改密码的地方却是用的id,我们该怎么得到id呢?

    请看上面的登陆功能模块,我们在登陆时已经将用户的全部信息录入了session这时我们可以直接通过session得到用户的全部信息,在以后的开发生涯中,这也是我们的方法

  • 代码复用

    如何实现代码复用呢,userservlet里有很多操作,除了修改密码还有增删改查,我们可以把操作封装成方法,然后让get和post调用方法

    ```java
    //封装好的方法
    public void updatePwd(HttpServletRequest req, HttpServletResponse resp){
    Object user = req.getSession().getAttribute(Constants.USER_SESSION);
    String newpassword = req.getParameter("newpassword");

    <pre><code> //判断用户和输入的新密码是不是为空,只有两者皆为非空可以向下判断运行
    if (user!=null&& !StringUtils.isNullOrEmpty(newpassword)){
    UserServiceImpl userService = new UserServiceImpl();
    boolean flag = userService.updatePwd(((User) user).getId(), newpassword);
    if (flag){
    req.setAttribute("message","修改密码成功,请退出,使用新密码登录");
    //密码修改成功,移除当前session
    req.getSession().removeAttribute(Constants.USER_SESSION);

    }else {
    req.setAttribute("message","密码修改失败");
    }
    }else {
    req.setAttribute("message","新密码有问题");
    }
    try {
    req.getRequestDispatcher("pwdmodify.jsp").forward(req,resp);
    } catch (ServletException e) {
    e.printStackTrace();
    } catch (IOException e) {
    e.printStackTrace();
    }
    </code></pre>

    }

    ```

    ```java
    //java的doget与dopost方法
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String method = req.getParameter("method");
    if (method!=null&&(method.equals("savepwd"))){
    this.updatePwd(req, resp);
    }
    }

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

    ```

Ajax验证旧密码

先说一下整体思路:

首先,请先看修改密码的JS代码:

$(function(){
    oldpassword = $("#oldpassword");
    newpassword = $("#newpassword");
    rnewpassword = $("#rnewpassword");
    saveBtn = $("#save");

    oldpassword.next().html("*");
    newpassword.next().html("*");
    rnewpassword.next().html("*");

    oldpassword.on("blur",function(){
        $.ajax({
            type:"GET",
            url:path+"/jsp/user.do",
            data:{method:"pwdmodify",oldpassword:oldpassword.val()},
            dataType:"json",
            success:function(data){
                if(data.result == "true"){//旧密码正确
                    validateTip(oldpassword.next(),{"color":"green"},imgYes,true);
                }else if(data.result == "false"){//旧密码输入不正确
                    validateTip(oldpassword.next(),{"color":"red"},imgNo + " 原密码输入不正确",false);
                }else if(data.result == "sessionerror"){//当前用户session过期,请重新登录
                    validateTip(oldpassword.next(),{"color":"red"},imgNo + " 当前用户session过期,请重新登录",false);
                }else if(data.result == "error"){//旧密码输入为空
                    validateTip(oldpassword.next(),{"color":"red"},imgNo + " 请输入旧密码",false);
                }
            },
            error:function(data){
                //请求出错
                validateTip(oldpassword.next(),{"color":"red"},imgNo + " 请求错误",false);
            }
        });


    }).on("focus",function(){
        validateTip(oldpassword.next(),{"color":"#666666"},"* 请输入原密码",false);
    });

    newpassword.on("focus",function(){
        validateTip(newpassword.next(),{"color":"#666666"},"* 密码长度必须是大于6小于20",false);
    }).on("blur",function(){
        if(newpassword.val() != null && newpassword.val().length > 6
                && newpassword.val().length < 20 ){
            validateTip(newpassword.next(),{"color":"green"},imgYes,true);
        }else{
            validateTip(newpassword.next(),{"color":"red"},imgNo + " 密码输入不符合规范,请重新输入",false);
        }
    });


    rnewpassword.on("focus",function(){
        validateTip(rnewpassword.next(),{"color":"#666666"},"* 请输入与上面一致的密码",false);
    }).on("blur",function(){
        if(rnewpassword.val() != null && rnewpassword.val().length > 6
                && rnewpassword.val().length < 20 && newpassword.val() == rnewpassword.val()){
            validateTip(rnewpassword.next(),{"color":"green"},imgYes,true);
        }else{
            validateTip(rnewpassword.next(),{"color":"red"},imgNo + " 两次密码输入不一致,请重新输入",false);
        }
    });


    saveBtn.on("click",function(){
        oldpassword.blur();
        newpassword.blur();
        rnewpassword.blur();

        if(oldpassword.attr("validateStatus") == "true"
            &&newpassword.attr("validateStatus") == "true"
            && rnewpassword.attr("validateStatus") == "true"){
            if(confirm("确定要修改密码?")){
                $("#userForm").submit();
            }
        }

    });
});

这个js文件里封装了四个事件,分别是鼠标焦点在旧密码,新密码,重复新密码,和保存按钮上。让我们把注意力放在旧密码上:

这段代码,定义了焦点旧密码框放上去丢失之后的动作,其中type定义了请求的方法,url定义了当事件发生后去哪里进行请求,根据代码我们可以看出来,请求是通向/jsp/user.do的,其中data的内容和data的格式都给了出来。那么这个data是从哪里传出来的呢

请看下面的servlet

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getParameter("method");
        if (method!=null&&(method.equals("savepwd"))){
            this.updatePwd(req, resp);
        }else if (method!=null&&(method.equals("pwdmodify"))){
            this.pwdModify(req, resp);
        }
    }    
    public void pwdModify(HttpServletRequest req, HttpServletResponse resp){
        //从session里取ID
        Object o = req.getSession().getAttribute(Constants.USER_SESSION);
        String oldpassword = req.getParameter("oldpassword");

        //万能map:结果集
        Map<String, String> reslultMap = new HashMap<String, String>();

        //如果session失效了
        if (o==null){
            reslultMap.put("result","sessionerror");
            //输入的密码为空
        }else if (StringUtils.isNullOrEmpty(oldpassword)){
            reslultMap.put("result","error");
        }else {
            //session中用户的密码
            String userPassword = ((User) o).getUserPassword();
            if (oldpassword.equals(userPassword)){
                reslultMap.put("result","true");
            }else {
                reslultMap.put("result","false");
            }
        }
        try {
            resp.setContentType("application/json");
            PrintWriter writer = resp.getWriter();
            writer.write(JSONArray.toJSONString(reslultMap));
            writer.flush();//刷新关闭
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

这是userservlet中根据前段id写的方法,这里的doGet会根据data中对method的参数进行选择方法(data中的参数都给到了request),得到了pwdModify方法名字后进入方法,在session里得到用户对象,在data里得到的旧密码,并在下面进行判断,共以下几种情况:

  1. session失效了
  2. session没失效
    1. 输入的旧密码为空
    2. 输入的旧密码不为空
      1. 密码与session用户里密码不相等
      2. 密码与session用户里密码相等

我们新建一个map,map中是键值对:result与对应的值

在最下面的这种情况中,我们新建一个resp输出流,将这个map变成json输出去,map就到了js里,我们根据map的值做出不同的应对

5.用户管理实现

  1. 导入分页的工具类
  2. 用户列表页面导入
    1. 用户列表
    2. 分页条

1、获取用户数量

  1. UserDao
    //查询用户总数
    public int getUserCount(Connection connection,String userName,int userRole) throws SQLException;
    

    传入的参数,除了connection之外还要传入的东西,包括要查询的用户名String和权限int,在sql里查询数据总数要用count(1),因为count(*)会查询所有的数据列,十分缓慢,

  2. UserDaoImpl

    public int getUserCount(Connection connection, String userName, int userRole) throws SQLException {
       PreparedStatement pstm = null;
       ResultSet rs = null;
       Object[] params;
       ArrayList<Object> list = new ArrayList<>();//利用list来传递参数
       int count = 0;
       if (connection != null) {
           StringBuffer sql = new StringBuffer();//利用StringBuffer和if来进行不同sql语句执行
           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);//index 0
           }
           if (userRole > 0) {
               sql.append(" and r.id = ?");
               list.add(userRole);//index 1
           }
           //把list转为数组,变为参数
           params = list.toArray();
    
           //执行Basedao的查询功能返回结果集
           rs = BaseDao.execute(connection, rs, pstm, sql.toString(), params);
    
           //将结果集中Count的值赋给count
           if (rs.next()) {
              count = rs.getInt("count");
           }
    
           BaseDao.closeResource(null, pstm, rs);
    
       }
    
       return count;
    }
    

    首先还是初始化参数,包括预指令,结果集,参数数组这些最基本的

    在这个方法李我们首先还是老样子判断连接是否存在,但是在接下来有些许不同,注意,这里用了StringBuffer来进行不同的sql语句执行,append方法在查询全部成员的基础上用if来对不同的情况进行判断,添加不同的sql语句(注意空格

    同时用了list来进行参数的传递,逻辑如下:首先如果用户名或者用户权限的if判断成功,说明两个参数内部是有值的,将这个值放进列表中,同时sql语句也添加了一个?,这样在Basedao里的execute函数就可以进行执行了

  3. UserService

    //根据用户名或者角色查询用户总数
    public int getUserCount(String userName,int userRole);
    
  4. UserSerivceImpl
    //根据用户名或者角色查询用户总数
    @Override
    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、获取用户列表

  1. UserDao
    //获取用户列表
    public List<User> getUserList(Connection connection,String userName,int userRole,int currentPageNo, int pageSize) throws SQLException;
    

    这里跟获取用户数对比多了两个参数,一个是当前页面号,一个是页面显示数量

  2. UserDaoImpl

    @Override
    public List<User> getUserList(Connection connection, String userName, int userRole, int currentPageNo, int pageSize) throws SQLException {
       PreparedStatement pstm = null;
       ResultSet rs = null;
       Object[] params;
       ArrayList<Object> list = new ArrayList<>();//利用list来传递参数
       int currentPageSize = 0;
       ArrayList<User> users = new ArrayList<>();
    
       if (connection != null) {
           StringBuffer sql = new StringBuffer();
           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);//index 0
           }
    
           if (userRole > 0) {
               sql.append(" and r.id = ?");
               list.add(userRole);//index 1
           }
    
           sql.append(" order by creationDate DESC limit ?,?");//为sql添加分页和排序
           currentPageSize = (currentPageNo - 1) * pageSize;//当前页面数等于d
           list.add(currentPageSize);
           list.add(pageSize);
    
           params = list.toArray();
           rs = BaseDao.execute(connection, rs, pstm, sql.toString(), params);
           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"));
               users.add(user);
           }
           BaseDao.closeResource(null,pstm,rs);
       }
       return users;
    }
    

    几乎是与获取用户数量是相同的,除了

       sql.append(" order by creationDate DESC limit ?,?");//为sql添加分页和排序
       currentPageSize = (currentPageNo - 1) * pageSize;//当前页面数等于(当前页面号-1)×页面尺寸
       list.add(currentPageSize);//添加当前页面数
       list.add(pageSize);//添加页面尺寸
    

    这里涉及到数据库的limit指令,其中第一个currentPageSize是当前页数,第二个pageSize是告诉数据库每页显示的数据多少

  3. UserService

    //获取用户列表
    public List<User> getUserList(String userName, int userRole, int currentPageNo, int pageSize) ;
    
  4. UserSerivceImpl
    //获取用户列表
    @Override
    public List<User> getUserList(String userName, int userRole, int currentPageNo, int pageSize) {
       Connection connection = null;
       List<User> userList = null;
       try {
           connection = BaseDao.getConnection();
           userList= userDao.getUserList(connection, userName, userRole, currentPageNo, pageSize);
       } catch (SQLException e) {
           e.printStackTrace();
       }finally {
           BaseDao.closeResource(connection,null,null);
       }
       return userList;
    }
    

3、角色管理实现

衍生问题:BaseDao有一部分用静态块做初始化,而服务层实现里都是用构造函数初始化,两者有何区别,后者与直接赋值又有何区别?

构造函数:执行时间比构造代码块时间晚,也是在对象初始化的时候运行。没有返回值,构造函数名称和类名一致。

构造代码块:执行时间比静态代码块晚,比构造函数早,和构造函数一样,只在对象初始化的时候运行。没有名字、参数和返回值。

静态代码块:最早执行,类被载入内存时执行,只执行一次。没有名字、参数和返回值,有关键字static。

静态代码块只会在类被载入内存时加载一次,是最先执行的,然后是构造代码块,最后才是构造函数。

构造代码块和构造函数都是在对象创建的时候执行,有几个对象就会执行几次。

因为BaseDao里的代码每次执行都会使用,有很大的重复性,所以放在静态代码块里,在内存中生成一次就结束了,减少调用次数,而服务层实现里调用的不是那么频繁,所以放在构造函数里。

构造函数与直接赋值是有区别的,在一个类没有实例化的时候,前者不会给变量赋值,节省内存

获取角色列表

对应职责,每一种操作对应一个包,比如对角色的操作就放在角色的包里

  1. RoleDao
    //获取角色列表
    public List<Role> getRoleList(Connection connection)throws SQLException;
    

    参数只需要连接就可以了

  2. RoleDaoImpl

    //获取角色列表
    @Override
    public List<Role> getRoleList(Connection connection) throws SQLException {
       PreparedStatement pstm = null;
       ResultSet rs = null;
       Object[] params ={};
       ArrayList<Object> list = new ArrayList<>();//利用list来传递参数
       ArrayList<Role> roles = new ArrayList<>();
       if(connection!=null){
           String sql ="Select * from smbms_role";
           rs = BaseDao.execute(connection, rs, pstm, sql, params);
           while (rs.next()){
               Role role = new Role();
               role.setId(rs.getInt("id"));
               role.setRoleName(rs.getString("roleName"));
               role.setRoleCode(rs.getString("roleCode"));
               roles.add(role);
           }
           BaseDao.closeResource(null,pstm,rs);
       }
    
       return roles;
    }
    
  3. RoleService
    //获取用户列表
    public List<User> getUserList(String userName, int userRole, int currentPageNo, int pageSize) ;
    
  4. RoleServiceImpl
    //获取用户列表
    @Override
    public List<User> getUserList(String userName, int userRole, int currentPageNo, int pageSize) {
       Connection connection = null;
       List<User> userList = null;
       try {
           connection = BaseDao.getConnection();
           userList= userDao.getUserList(connection, userName, userRole, currentPageNo, pageSize);
       } catch (SQLException e) {
           e.printStackTrace();
       }finally {
           BaseDao.closeResource(connection,null,null);
       }
       return userList;
    }
    

4、servlet具体实现

  1. 获取前端的信息,并初始化参数
    String queryUserName = req.getParameter("queryname");//前端输入的查询名字
    String temp = req.getParameter("queryUserRole");//前端选择的用户权限
    String pageIndex = req.getParameter("pageIndex");//当前页号
    int queryUserRole = 0;//默认的用户权限为0
    int userCount = 0;//默认的查询出来的用户总数
    List<Role> roleList = null;//权限列表
    List<User> userList = null;//用户列表
    
    RoleService roleService = new RoleServiceImpl();
    UserService userService = new UserServiceImpl();
    //两个服务层
    
    int paseSize = 5;//设置页面容量
    int currentPageNo = 1;//设置当前页面
    int totalCount = 0;//总记录数————用户数量
    int totalPageCount = 0; //总页数————用户数量除以页面容量+1;
    

    在这一步,先要明确接下来都有哪些初始化参数

  2. 判断前端信息是否正确

    if (queryUserName == null) {
             queryUserName = "";
         }
         if (temp != null && !temp.equals("")) {
             queryUserRole = Integer.parseInt(temp);
         }
         /*
    1.currentPageNo    当前页号
    2.pageIndex       当前页号
    3.totalCount      记录总条数
    4.totalPageCount   总页数
    5.pageSize    页面容量
    */
         if (pageIndex != null) {
             currentPageNo = Integer.parseInt(pageIndex);
         }
    

    分别对前端获得信息进行非空判断来保证安全性

  3. 获取具体的列表(调用业务层)

    //获取用户列表
    userList = userService.getUserList(queryUserName, queryUserRole, currentPageNo, paseSize);
    //获取用户总数
    userCount = userService.getUserCount(queryUserName,queryUserRole);
    //获取角色列表
    roleList = roleService.getRoleList();
    

    通过调取业务层对一开始的参数进行赋值

  4. 开始进行分页工作

    1. 先初始化分页工具类

    2. 设置好参数

    3. 为变量赋值

    4. 控制首尾页

    PageSupport pageSupport = new PageSupport();
    pageSupport.setCurrentPageNo(currentPageNo);
    pageSupport.setPageSize(paseSize);
    pageSupport.setTotalCount(userCount);
    pageSupport.setTotalPageCount();
    
    totalCount = pageSupport.getTotalCount();
    totalPageCount = pageSupport.getTotalPageCount();
    //控制首页和尾页
           if(currentPageNo < 1){
               currentPageNo = 1;
           }else if(currentPageNo > totalPageCount){
               currentPageNo = totalPageCount;
           }
    
    

    新建分页工具类,得到总记录数,总页数等信息,然后通过if来限制页数的加减

  5. 向前端传递值

    req.setAttribute("roleList",roleList);
    req.setAttribute("userList",userList);
    req.setAttribute("totalCount",totalCount);
    req.setAttribute("currentPageNo",currentPageNo);
    req.setAttribute("totalPageCount",totalPageCount);
    req.setAttribute("queryUserName", queryUserName);
    req.setAttribute("queryUserRole", queryUserRole);
    
  6. 转发
    req.getRequestDispatcher("userlist.jsp").forward(req,resp);
    

衍生问题:前端页面下拉框数据回显问题

看前端代码的时候有一块代码我不是很明白

<select name="queryUserRole">
    <c:if test="{roleList != null }">
        <option value="0">--请选择--</option>
        <c:forEach var="role" items="{roleList}">
            <option
                    <c:if test="美元符号{role.id == queryUserRole}">selected="selected"</c:if>
                    value="{role.id}">{role.roleName}</option>
        </c:forEach>
    </c:if>
</select>

这里是权限选择下拉框的地方,其中在option标签里做了一个role.id queryUserRole的判断,我一直没整明白是干啥的。

在搜索引擎的帮助下,我似乎了解了,在选择好权限后显示的页面如果有分页,在点击下一页后就会改变。

因为虽然你在这一页李对权限进行了选择,但是进入下一页的时候权限又回到了默认状态,相当于查看的是全体权限。

因此需要进行判断并进行数据回显。

6.用户增删改查

1、增加

先看jsp页面,在userlist里有增加按钮的jsp页面(useradd.jsp),jsp页面提交了一个表单,那么就可以从dao层开始先写增加了。

套路还是一样的,

dao层

//增加用户
@Override
public int addUser(Connection connection,User user) throws Exception {
    PreparedStatement preparedStatement = null;

    String sql = "insert into smbms_user (userCode,userName,userPassword," +
            "userRole,gender,birthday,phone,address,creationDate,createdBy) " +
            "values(?,?,?,?,?,?,?,?,?,?)";

    Object params[] = {user.getUserCode(),user.getUserName(),user.getUserPassword(),user.getUserRole(),user.getGender(),user.getBirthday(),user.getPhone(),user.getAddress(),user.getCreationDate(),user.getCreatedBy()};

    int execute = 0;
    if (connection != null) {
        execute = BaseDao.execute(connection, preparedStatement, sql, params);
    }
    BaseDao.closeResource(null, preparedStatement, null);
    return execute;
}

业务层

//增加用户
@Override
public boolean addUser(User user) {
    Connection connection = null;
    boolean flag = false;

    try {
        connection = BaseDao.getConnection();
        connection.setAutoCommit(false);
        int updateRows = userDao.addUser(connection, user);
        connection.commit();
        if (updateRows>0){
            flag = true;
        }
    } catch (Exception e) {
        e.printStackTrace();
        try {
            connection.rollback();
        } catch (SQLException ex) {
            ex.printStackTrace();
        }
    }finally {
        BaseDao.closeResource(connection,null,null);

    }
return flag;
}

Servlet

在servletl里遇到了两个问题,首先是角色权限的jsp页面并没有像狂神那样直接出现下拉表单,jsp页面里也并没有出现select的option,我想了半天终于在js页面里找到了,原来是有一个ajax代码(前端真的不太行,之后要好好看看,mvc阶段必须顺带着看熟练了,不会写也得会看吧),ajax代码需求了一个servlet方法,不多说给他吧,用的业务层还是请求权限列表那个,ajax方面的参数是要求json返回,那我们就用map加fastJson包的方法和response流给他返回去。

遇到的第二个问题,填写完信息后没法提交,来吧,看看这个提交按钮的事件,果然还是js代码,对于按钮会验证前面几个东西是否填写的正确,都正确了啊,定睛一看原来是用户名根本就没有验证。还是要servlet代码!淦

原来这个js事件是要求去数据库里查询是否有这个用户名,天,难道还要从dao从头写一个出来?其实不用,在登录验证的时候我们写了得到登录用户的dao这里可以直接用。

首先判断,这个输入的用户名是否为空,如果为空直接给他报错,如果不为空悠悠两种情况

  1. 根据得到的登录用户为空
  2. 根据得到的鞥路用户不为空

第一种说明这个用户不存在

第二种说明这个用户在数据库里是有的

怎么返回给js信息我就不用多说了吧,上面角色权限都说了,map一套返回去,大功告成

2、删除

依旧是js触发事件,需要servlet的deluser方法,还是从dao开始,首先需要要从前端得到要删除的用户的id,根据id删除数据库内容,再根据返回的json消息进行相应的提示。

3、修改

这个修改分为两个步骤,首先,在页面点击修改按钮,会有一个跳转到修改的界面,这里是直接调用了查看页面写的getuserById的方法,直接通过ID得到一个User,放入req里,跳转到modify界面,并显示出来,接下来就要在js进行判断输入的正确性,让偶点击保存按钮,这时汇集提交到servlet里,同时隐藏域里写着方法名字,对应方法以及得到的数据,调取对应的业务层modify方法,进行修改操作。

4、查看

对比修改来可简单太多了,只需要在数据库通过id拿到User,返回给前端就可以了


醉后不知天在水,满船清梦压星河