MVC
本文为动力节点老杜web课程mvc部分笔记,以银行转账项目为例
不使用MVC框架(分析存在的问题
1.搞个数据库
CREATE TABLE `t_act` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '自然主键,与业务无关,自增',
  `actno` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '账号',
  `balance` decimal(10,2) DEFAULT NULL COMMENT '余额',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
2.创建项目bank(不使用maven,手动配置
2.1基本流程
- 创建空project (无需序号 1. 空格)
 - moudle
 - 手动添加Framwork支持
 - project structure
 - 在dependency目录下添加相关jar包,切不可添加在source目录下(没用)
 

2.2遇到的问题 Servlet包找不到(低级错误
- 
原因
上述jar包添加过程有误
 - 
措施
1.以为jdk17与tomcat9不适配,换成10仍不适配
2.在web WEB-INF中创建lib目录,将servlet-api.jar拷过去,仍不管用
 

2.3 jsp文档写一个简单页面(EL表达式不熟练
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <base href="https://www.cnblogs.com/tsangfandy/p/${pageContext.request.scheme}://${pageContext.request.serverPort}${pageContext.request.contextPath}/">
    <title>银行账户转账</title>
  </head>
  <body>
  <form action="transfer" method="post">
    转出账户:<input type="text" name="fromActno"><br>
    转入账户:<input type="text" name="toActno"><br>
    转账金额:<input type="text" name="money"><br>
    <input type="submit" name="转账">
  </form>
  </body>
</html>

2.4servlet类实现转账功能
- 
别忘了加注解 @WebServlet("/transfer")
 - 
从前端获取用户输入的转账信息
 
String fromActno = request.getParameter("fromActno");
String toActno = request.getParameter("toActno");
double money = Double.parseDouble(request.getParameter("money"));
- 
编写转账业务逻辑代码,连接数据库,进行转账操作(JDBC那一套)
 注意3:开启事务,事务手动提交与事务回滚-------------------------------------------------
 jdbc会自动提交,开启事务,不让他自动提交
 注意2:在web WEB-INF中添加mysql连接jar包
 

 注意1: 转账之前判断余额是否充足,不足则异常,(创建一个异常类)
//余额不足异常类
public class MoneyNotEnoughException extends Exception {
    //构造方法
    public MoneyNotEnoughException() {
    }
    public MoneyNotEnoughException(String msg) {
        super(msg);
    }
}
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
PreparedStatement ps2 = null;	//有了新的sql语句
PreparedStatement ps3 = null;
try {
    //注册驱动
    Class.forName("com.mysql.cj.jdbc.Driver");
    //获取连接
    String url = "jdbc:mysql://localhost:3306/mvc";
    String user = "root";
    String password = "123456";
    conn = DriverManager.getConnection(url,user,password);
    //注意3
    //开启事务
    //获取预编译的sql对象
    String sql = "select id,balance from t_act where actno = ? ";
    ps = conn.prepareStatement(sql);
    ps.setString(1,fromActno);
    //执行sql语句,返回结果集
    rs = ps.executeQuery();
    //拿到结果集后处理结果集
    //因为只有一条结果,if即可
    if (rs.next()){
        //取出余额
        double balance = rs.getDouble("balance");
        if (balance < 0){
            //余额不足
            throw new MoneyNotEnoughException("余额不足");
        }
        //程序走到这里已经说明余额充足,不必加else,因为if中如不足则exception
        //下面转账,实现功能
        //act01 - 10000
        //act02 + 10000
        String sql2 = "update t_act set balance = balance - ? where actno = ? ";
		ps2 = conn.prepareStatement("sql2");
        ps2.setDouble(1,money);
        ps2.setString(2,fromActno);
        int count = ps2.executeUpdate(); //执行更新,并返回更新的条数
        //---------------------模拟异常--------------------------------
           //此处模拟一个异常,此时钱转出,未转入
                String s = null;//空指针异常
                                //试验证明,出问题。引入事务
                s.toString();
		 //---------------------模拟异常--------------------------------
        String sql3 = "update t_act set balance = balance + ? where actno = ? ";
        ps3 = conn.prepareStatement("sql3");
        ps3.setDouble(1,money);
        ps3.setString(2,fromActno);
        count += ps3.executeUpdate();
        if (count != 2){
            //转账失败,再来异常
            throw new AppException("App异常,请联系管理员");
        }
         //提交事务(转账成功肯定能提交了-----------------------------
                conn.commit();
        //转账成功
            out.print("转账成功");
    }
} catch (Exception e) {
    //-------------------------------事务回滚--------------------
    try {
        if (conn != null) { //保险起见,不等于null才回滚
            conn.rollback();//只要遇到异常就回滚
        }
    } catch (SQLException ex) {
        ex.printStackTrace();
    }
    //-----------------------------------------------------
    //异常处理,发生上述异常后怎么做
    //e.printStackTrace();      不打印异常信息了,搞到前端去
    out.print(e.getMessage());   //getMessage可以获取到上面写的"余额不足"
                                // 因为构造方法中有参那个,调用父类的massage,父类中还有super继续上调,
                                //直到一个private的成员变量,那么值就给他了,而getMassage恰好获得他的值
}finally {
    //释放资源
    if (conn != null) {
        try {
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    if (ps != null) {
        try {
            ps.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    if (ps2 != null) {
        try {
            ps.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    if (ps3 != null) {
        try {
            ps.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    if (rs != null) {
        try {
            rs.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
下面分析mvc存在的问题
3.分析问题
3.1AccountTransferServlet负责什么业务(业务逻辑是什么)
- 数据接收
 - 核心业务处理,转账业务
 - 连接数据库,CRUD
 - 页面数据展示
 
这个servlet负责所有的事情
3.2缺点是什么
- 
代码复用性太差,实现一个功能就要搞一个servlet,比如R,查一个数据就得写一个servlet,可以搞一个专门查数据的方法,查询数据时调用即可。
原因:没有进行"职能分工",没有独立组件的概念,没有办法进行代码复用,代码之间耦合度太高,扩展力太差
 - 
耦合度高导致代码很难扩展。
 - 
操作数据库的代码和业务逻辑混杂在一起,编写代码时很容易出错。无法专注业务逻辑。所以才需要分层。
 
MVC架构模式
1.理论基础

C调用M和V
M:数据处理、业务处理
V:页面展示
2.JDBC工具类封装
jdbc.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mvc
user=root
password=123456
DBUtil
package bank.utils;
import java.sql.*;
import java.util.ResourceBundle;
/**
 * JDBC工具类
 * @author d
 * @version 1.0
 * @since 1.0
 */
public class DBUtil {
    private static ResourceBundle bundle = ResourceBundle.getBundle("resources/jdbc");
    private static String driver = bundle.getString("com.mysql.cj.jdbc.Driver");
    private static String url = bundle.getString("jdbc:mysql://localhost:3306/mvc");
    private static String user = bundle.getString("root");
    private static String password = bundle.getString("123456");
    //工具类一般搞个私有的构造方法,因为不让创建对象。工具类中的方法都是静态的,不需要创建对象
    // 为了防止创建对象,故私有化
    private DBUtil(){}
    //DBUtil在类加载时注册驱动
    static {
        try {
            Class.forName(driver);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    /**
     * 这里没有使用数据库连接池,直接创建的连接对象
     * 每一次调用这个方法,都是一个新的对象
     * @return 连接对象
     * @throws SQLException
     */
    public static Connection getConnection() throws SQLException {
        Connection connection = DriverManager.getConnection(url, user, password);
        return connection;
    }
    /**
     * 关闭资源
     * @param conn 连接对象
     * @param stmt 数据库操作对象
     * @param rs   结果集对象
     */
    public static void close(Connection conn , Statement stmt, ResultSet rs){
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (stmt != null) {
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
3.DAO与数据对象实体类
3.1DAO
AccountDao是负责Account数据增删改查的
- 什么是Dao
 
- Data Access Object(数据访问对象)
 
- Dao是一种设计模式,是j2EE的设计模式之一,不是23种设计模式
 - Dao只负责数据库的CRUD,没有任何业务逻辑
 - 没有业务逻辑,只负责表中数据增删改查对象,有一个特殊称谓:DAO对象
 - 为什么叫做AccountDao呢?
 
- 这是因为这个DAO是专门处理t_act这张表的。
 - 如果处理t_user表的话,可以叫做:UserDao
 - 如果处理t_student表的话,可以叫做:StudentDao
 
时间
2022.08.30