zoukankan      html  css  js  c++  java
  • MVC三层架构学习总结实例

    一个简单的转账Servlet Demo

    使用MVC三层架构实现

    前端

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <style>
            #mainApp{
                font-size: 20px;
                font-family: "Microsoft YaHei UI",serif;
                text-align: center;
                margin-top: 200px;
                font-weight: bold;
            }
        </style>
    </head>
    <body>
    <div id="mainApp">
        <form action="servlet/transfer" method="post">
            <p><label for="transOut">请填写转出用户名:</label><input type="text" id="transOut" name="transOut" required /></p>
            <p><label for="transIn">请填写转入用户名:</label><input type="text" id="transIn" name="transIn" required /></p>
            <p><label for="money">请填写转账数目:</label><input type="text" id="money" name="money" required /></p>
            <p><input type="submit"><input type="reset"></p>
        </form>
    </div>
    </body>
    </html>
    

    后端

    数据库工具类保证数据库调用的统一

    public class C3P0Utils {
        /**
         * 获取连接池
         * @return 返回c3p0默认连接池
         */
        public static DataSource getDataSource(){
            return new ComboPooledDataSource();
        }
    
        /**
         * 获取连接
         * @return 返回一个基于c3p0连接池的连接
         */
        public static Connection getConnection(){
            try {
                return getDataSource().getConnection();
            } catch (SQLException e){
                throw new RuntimeException("无法获取连接,请检查数据库配置文件");
            }
        }
    
        /**
         * 实现资源的释放
         * 细节在于首先是对于顺序的先开后关
         * 对于每个对象都要有try...catch保证哪怕报错了其他的对象也可以关闭
         * @param connection 数据库连接
         * @param ps 预编译sql对象
         * @param resultSet 数据库结果集
         */
        public static void release(Connection connection, PreparedStatement ps, ResultSet resultSet){
            try {
                if (resultSet != null){
                    resultSet.close();
                }
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
            try {
                if (ps != null){
                    ps.close();
                }
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
            try {
                if (connection != null){
                    connection.close();
                }
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
    

    model用于封装数据库对象

    public class User {
    
        private String name;
        private BigDecimal money;
    
        public User() {
        }
    
        public User(String name, BigDecimal money) {
            this.name = name;
            this.money = money;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public BigDecimal getMoney() {
            return money;
        }
    
        public void setMoney(BigDecimal money) {
            this.money = money;
        }
    }
    

    Dao层用于操作数据库

    /**
     * 此接口规定对User数据库的操作
     * @author Rainful
     * @create 2021/07/29
     */
    public interface UserDao {
        /**
         * 通过name查询用户
         * @param connection 数据库连接
         * @param ps 预编译sql对象
         * @param sql sql语句
         * @param name 用户名
         * @param money 钱数
         * @throws SQLException 抛出一个查询错误让业务代码回滚
         * @return 返回一个可以查找到的用户
         */
        int moneyTransfer(Connection connection, PreparedStatement ps,
                          String sql, String name, double money) throws SQLException;
    }
    
    public class UserDaoImpl implements UserDao {
        @Override
        public int moneyTransfer(Connection connection, PreparedStatement ps,
                                 String sql, String name, double money) throws SQLException {
            ps = connection.prepareStatement(sql);
            ps.setDouble(1, money);
            ps.setString(2, name);
            return ps.executeUpdate();
        }
    }
    

    业务层用于调用Dao层验证从控制层传来的参数等

    /**
     * 此接口用于规范数据库查询
     * @author Rainful
     * @create 2021/07/29
     */
    public interface UserServlet {
        /**
         * 业务层调用dao层完成数据库更新及控制事务
         * @param name1 转出账户用户名
         * @param name2 转入账户用户名
         * @param money 修改
         * @return 修改结果
         */
        boolean moneyTransfer(String name1, String name2, double money);
    }
    
    public class UserServletImpl implements UserServlet {
        private final UserDao userDao;
    
        public UserServletImpl() {
            this.userDao = new UserDaoImpl();
        }
    
        @Override
        public boolean moneyTransfer(String name1, String name2, double money) {
            Connection connection = null;
            PreparedStatement ps = null;
            try {
                connection = C3P0Utils.getConnection();
                // 开启事务
                connection.setAutoCommit(false);
                // 转出账户
                int transOutRow = transfer(connection, ps, name1, money, -1);
                // 转入账户
                int transInRow = transfer(connection, ps, name2, money, 1);
                // 提交事务
                connection.commit();
                return transOutRow > 0 && transInRow > 0;
            } catch (Exception e) {
                // 发生异常进行回滚处理
                try {
                    connection.rollback();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
                e.printStackTrace();
                return false;
            } finally {
                // 关闭数据库连接
                try {
                    connection.setAutoCommit(true);
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
                C3P0Utils.release(connection, ps, null);
            }
        }
    
        /**
         * 返回转账sql的影响行数
         *
         * @param connection 数据库连接
         * @param ps         预编译sql对象
         * @param name       账户更改姓名
         * @param money      更改的钱数
         * @param value      为了保证方法共用性而设置的修改参数
         *                   转入为 1, 转出为 -1
         * @return 返回影响的行数
         * @throws SQLException 抛出sql异常回滚
         */
        private int transfer(Connection connection, PreparedStatement ps, String name, double money, int value) throws SQLException {
            // 转出账户的话因为钱是减少的
            String sql = "update account set money = money + ? where name = ?";
            return userDao.moneyTransfer(connection, ps, sql, name, money * value);
        }
    }
    

    控制层用于接收前端数据传输给业务层做逻辑判断

    @WebServlet("/servlet/transfer")
    public class TransferMoney extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            String transOut = req.getParameter("transOut");
            String transIn = req.getParameter("transIn");
            String money = req.getParameter("money");
    
            // 三者都在前端进行了非空判断
            // 后续如果再加上判断就好了,因为要防止前端被人恶意修改传输数据过来
            long moneyNum;
            System.out.println(money);
            //System.out.println(Long.parseLong(money));
            try {
                moneyNum = Long.parseLong(money);
            } catch (Exception e){
                resp.getWriter().print("金额不符合规范");
                return;
            }
    
            // 调用业务层进行处理
            UserServlet userServlet = new UserServletImpl();
            boolean flag = userServlet.moneyTransfer(transOut, transIn, moneyNum);
            if (flag){
                resp.getWriter().print("转账成功");
            } else {
                resp.getWriter().print("转账失败");
            }
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
    

    其他

    • 编码转换过滤

      @WebFilter("/servlet/*")
      public class CodeChange implements Filter {
          @Override
          public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
              request.setCharacterEncoding("utf-8");
              response.setCharacterEncoding("utf-8");
              response.setContentType("text/html;charset=utf-8");
      
              chain.doFilter(request, response);
          }
      }
      
    • 权限管理过滤防止恶意直接访问servlet

      @WebFilter("/servlet/*")
      public class FilterServlet implements Filter {
          @Override
          public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
              String money = request.getParameter("money");
              if (money == null){
                  ((HttpServletResponse)response).sendRedirect("../index.html");
              }
              chain.doFilter(request, response);
          }
      
          @Override
          public void init(FilterConfig filterConfig) throws ServletException {
      
          }
      
          @Override
          public void destroy() {
      
          }
      }
      

    总结

    • bug反思

      • 整体来说一次性写完,但是在数据库调用的时候发现数据库没有修改,经过单元测试排除方法最后发现调用sql的时候参数传递问题
    • 整个servlet优点

      • 完成了全部功能,实现了sql调用的时候connection复用,对一次业务进行connection的统一关闭
      • sql调用的时候进行参数传递到dao层可以一个方法完成增加和减少
      • 实现了MVC三层架构,并且使用接口实现多态,并且规范了实现类的行为
      • 实现了编码转换及权限过滤
    • 优化方向

      • 后续增加业务的时候,可以抽取sql代码完成sqlUtils类的封装规范Dao层的sql调用
      • 增加常用变量的时候可以进行一个静态变量工具类的封装等
  • 相关阅读:
    c#基础 里氏转换
    c#基础 base和this的区别,在继承上面
    c#基础值类和引用类型_字符串
    c# 基础字符串
    c#基础-构造函数 this new
    c#基础3-方法的重载静态和非静态,字段属性,方法
    c#基础2-out-ref
    .net的基础知识点
    Xamarin.Form的坑
    weboack 4 tutorial
  • 原文地址:https://www.cnblogs.com/rainful/p/15076733.html
Copyright © 2011-2022 走看看