JavaWeb(三:JDBC 与 MVC)

JavaWeb(一:基础知识和环境搭建)https://blog.csdn.net/xpy2428507302/article/details/140365130?spm=1001.2014.3001.5501JavaWeb(二:Servlet与Jsp,监听器与过滤器)https://blog.csdn.net/xpy2428507302/article/details/140365159?spm=1001.2014.3001.5501

目录

十、JDBC

1.概述

2.编写JDBC程序

3.细节分析

[(1)数据库 url](#(1)数据库 url)

(2)Connection类

[(3) Statement类](#(3) Statement类)

[(4) ResultSet类](#(4) ResultSet类)

[(5) 释放资源](#(5) 释放资源)

[4.SQL 注入问题](#4.SQL 注入问题)

5.JDBC事务

6.JDBC工具类

7.数据库连接池

(1)概述

(2)使用

8.DBUtils

十一、MVC三层架构

[1. 引言](#1. 引言)

[2. mvc三层架构](#2. mvc三层架构)


十、JDBC

1.概述

JDBC(Java DataBase Connectivity) :Java数据库连接技术

JDBC 是一个独立于特定数据库的管理系统,是通用的 SQL 数据库存取和操作的公共接口。

定义了一组标准,为访问不同数据库提供了统一的途径。

JDBC 是是一种用于数据库访问的应用程序API,由一组用Java语言编写的类和接口组成。

包括两个层面:

① 面向应用的 API,供程序员调用。

② 面向数据库的 API,供厂商开发数据库的驱动程序。

JDBC 连接不同的数据库,只要加载不同的数据库驱动包即可,不用担心数据库操作语言的差异性

2.编写JDBC程序

① 创建数据库表

sql 复制代码
CREATE TABLE users(
    id INT PRIMARY KEY,
    `name` VARCHAR(40),
    `password` VARCHAR(40),
    email VARCHAR(60),
    birthday DATE
);
INSERT INTO users(id,`name`,`password`,email,birthday)
VALUES(1,'张三','123456','zs@qq.com','2000-01-01');
INSERT INTO users(id,`name`,`password`,email,birthday)
VALUES(2,'李四','123456','ls@qq.com','2000-01-01');
INSERT INTO users(id,`name`,`password`,email,birthday)
VALUES(3,'王五','123456','ww@qq.com','2000-01-01');
SELECT * FROM users;

② 导入数据库依赖

XML 复制代码
<!--mysql的驱动-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.31</version>
</dependency>

③ JDBC的使用:

Ⅰ. 加载数据库驱动(java 程序和数据库之间的桥梁)

Ⅱ. 获取 Connection,java 程序与数据库的一次连接

Ⅲ. 创建向数据库发送 SQL 的 Statement 对象,由 Connection 产生

Ⅳ. 编写 SQL 语句(根据业务,编写不同的 SQL)

Ⅴ. 执行SQL(如果要接收返回值,创建 ResultSet 对象来保存 Statement 执行后查询到的结果)

Ⅵ. 关闭连接

java 复制代码
public class JDBCTest {
    public static void main(String[] args) throws Exception {
        //配置信息
        //要连接的数据库URL(解决中文乱码问题:useUnicode=true&characterEncoding=utf8&useSSL=true)
        String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf8&useSSL=true";
        //连接的数据库时使用的用户名
        String username = "root";
        //连接的数据库时使用的密码
        String password = "123456";
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;
        
        try {
            //1.加载驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            //2.获取与数据库的链接
            conn = DriverManager.getConnection(url, username, password);
            //3.获取用于向数据库发送sql语句的statement对象
            st = conn.createStatement();
            String sql = "select id,name,password,email,birthday from users";
            //4.向数据库发sql,并获取代表结果集的resultset对象
            //查:executeQuery    增删改:executeUpdate
            rs = st.executeQuery(sql);
            //5.取出结果集的数据
            while (rs.next()) {
                System.out.println("id=" + rs.getInt("id"));
                System.out.println("name=" + rs.getString("name"));
                System.out.println("password=" + rs.getString("password"));
                System.out.println("email=" + rs.getString("email"));
                System.out.println("birthday=" + rs.getDate("birthday"));
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                //6.关闭链接,释放资源(先开后关)
                rs.close();
                st.close();
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

3.细节分析

(1)数据库 url

jdbc:mysql://localhost:3306/xxx

|----------------|-------|
| jdbc | 协议 |
| mysql | 子协议 |
| localhost:3306 | 主机:端口 |
| xxx | 数据库 |

常用数据库URL地址的写法:

  • Oracle写法:jdbc:oracle:thin:@localhost:1521:xxx
  • SqlServer写法:jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=xxx
  • MySql写法:jdbc:mysql://localhost:3306/xxx

(2)Connection类

Connection 用于代表数据库的链接,Collection是数据库编程中最重要的一个对象。

客户端与数据库所有交互都是通过connection对象完成的,这个对象的常用方法:

|-----------------------------------|----------------------------------|
| createStatement() | 创建向数据库发送sql的statement对象 |
| prepareStatement(sql) | 创建向数据库发送预编译sql的PrepareSatement对象 |
| setAutoCommit(boolean autoCommit) | 设置事务是否自动提交 |
| commit() | 在链接上提交事务 |
| rollback() | 在此链接上回滚事务 |

(3) Statement类

Statement对象用于向数据库发送SQL语句, Statement对象常用方法:

|---------------------------|-------------------------------------------|
| executeQuery(String sql) | 用于向数据发送查询语句,返回 ResultSet 结果集 |
| executeUpdate(String sql) | 用于向数据库发送insert、update或delete语句,返回数据库更新的行数 |
| execute(String sql) | 用于向数据库发送任意sql语句 |
| addBatch(String sql) | 把多条sql语句放到一个批处理中 |
| executeBatch() | 向数据库发送一批sql语句执行 |

注意:

为提高运行效率,一般不直接使用 execute 方法。

而是对应使用:查询使用 executeQuery ;增删改使用 executeUpdate。

(4) ResultSet类

ResultSet用于代表Sql语句的执行结果。

Resultset 封装执行结果集时,采用的类似于表格的方式。

ResultSet 对象维护了一个指向表格数据行的游标,初始时,游标在第一行之前。

调用 ResultSet.next() 方法,可以使游标指向具体的数据行,调用 getxxx 方法获取该行的数据。

① 获取任意类型的数据

|------------------------------|---------------------|
| getObject(int index) | 按照指定列数获取 Object 对象 |
| getObject(string columnName) | 按照指定属性名获取 Object 对象 |

② 获取指定类型的数据,如获取 String 类型

|------------------------------|---------------------|
| getString(int index) | 按照指定列数获取 String 对象 |
| getString(String columnName) | 按照指定属性名获取 String 对象 |

③ 对结果集进行滚动的方法:

|-------------------|------------------|
| next() | 移动到下一行 |
| Previous() | 移动到前一行 |
| absolute(int row) | 移动到指定行 |
| beforeFirst() | 移动resultSet的最前面 |
| afterLast() | 移动到resultSet的最后面 |

(5) 释放资源

Jdbc程序运行完后,切记要释放程序在运行过程中,创建的那些与数据库进行交互的对象.

这些对象通常是 ResultSet, Statement 和 Connection 对象。

特别是 Connection对象,它是非常稀有的资源,用完后必须马上释放。

如果 Connection 不能及时、正确的关闭,极易导致系统宕机。

为确保资源释放代码能运行,资源释放代码也一定要放在finally语句中。

4.SQL 注入问题

使用 Statement 进行开发,会有两个问题:

① 需要频繁拼接 String 字符串,出错率较高

java 复制代码
String username = "zhangsan";
String password = "123";
String sql = "select * from users where name='"+username+"' and password='"+password+"'";
ResultSet rs = st.executeQuery(sql);

② 存在 SQL 注入的潜在风险

SQL 注入:在用户输入的数据中注入非法的 SQL语句,通过巧妙的技巧来拼接字符串,造成SQL短路,从而窃取数据库数据。

java 复制代码
public class SQLTest {
    public static void main(String[] args) throws Exception {
        // 正常登陆sql:select * from users where name='张三' and password ='123456'
        //login("张三","123456");
        // SQL 注入:select * from users where name='' and password ='123456' or '1'='1'
        login("", "123456' or '1'='1");
    }

    public static void login(String username, String password) throws Exception {
        String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf8&useSSL=true";
        String dbUsername = "root";
        String dbPassword = "123456";

        //1.加载驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        //2.获取与数据库的链接
        Connection conn = DriverManager.getConnection(url, dbUsername, dbPassword);
        //3.获取用于向数据库发送sql语句的statement
        Statement st = conn.createStatement();
        String sql = "select * from users where name='" + username + "' and password='" + password + "'";
        System.out.println(sql);
        //4.向数据库发sql,并获取代表结果集的rs
        ResultSet rs = st.executeQuery(sql);
        if (rs.next()) {
            System.out.println("登录成功");
        } else {
            System.out.println("登录失败");
        }
        //6.关闭链接,释放资源
        rs.close();
        st.close();
        conn.close();
    }
}

运行结果:

注意:

SQL 语句中 and 的优先级大于 or ,所以执行的sql 等价于 select * from users where '1' = '1';


**解决方法:**使用 Statement 的子类 PreparedStatement,它提供了 SQL 占位符的功能。

既不需要拼接字符串,也会对用户输入的数据进行充分检测,更加安全。

java 复制代码
public class PSTest {
    public static void main(String[] args) throws Exception {
        // 正常登陆sql:select * from users where name='张三' and password ='123456'
        //login("张三","123456");
        // SQL 注入:select * from users where name='' and password ='123456' or '1'='1'
        login("", "123456' or '1'='1");
    }

    public static void login(String username, String password) throws Exception {
        String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf8&useSSL=true";
        String dbUsername = "root";
        String dbPassword = "123456";

        Class.forName("com.mysql.cj.jdbc.Driver");

        Connection conn = DriverManager.getConnection(url, dbUsername, dbPassword);
        //获取用于向数据库发送预编译sql语句的prepareStatement
        String sql = "select * from users where name = ? and password = ?";
        System.out.println(sql);
        PreparedStatement ps = conn.prepareStatement(sql);
        //给占位符 ? 填充数据
        ps.setString(1, username);
        ps.setString(2, password);
        ResultSet rs = ps.executeQuery();
        if (rs.next()) {
            System.out.println("登录成功");
        } else {
            System.out.println("登录失败");
        }
        rs.close();
        ps.close();
        conn.close();
    }
}

运行结果:

5.JDBC事务

事务指逻辑上的一组操作,要么全部成功,要么全部不成功(ACID原则)。

当Jdbc程序向数据库获得一个 Connection 对象时,默认情况下这个 Connection 对象会自动向数据库提交事务。

若想关闭这种默认提交方式,让多条SQL在一个事务中执行,可使用下列的 JDBC 控制事务语句。

|----------------------------------|-------------------------|
| Connection.setAutoCommit(false); | 开启事务(start transaction) |
| Connection.rollback(); | 回滚事务 |
| Connection.commit(); | 提交事务 |

① 创建账户表

java 复制代码
/*创建账户表*/
CREATE TABLE account(
    id INT PRIMARY KEY AUTO_INCREMENT,
    NAME VARCHAR(40),
    money DECIMAL(9,2)
);
/*插入测试数据*/
insert into account(name,money) values('A',1000);
insert into account(name,money) values('B',1000);
insert into account(name,money) values('C',1000);

② 模拟转账成功时的业务场景

java 复制代码
//失败后让数据库自动回滚事务
public class Demo {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf8&useSSL=true";
        String username = "root";
        String password = "123456";
        Connection conn = null;
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            conn = DriverManager.getConnection(url, username, password);

            //通知数据库开启事务,false表示开启
            conn.setAutoCommit(false);

            String sql1 = "update account set money=money-100 where name = 'A' ";
            conn.prepareStatement(sql1).executeUpdate();

            //模拟执行完SQL1之后程序出现了异常而导致后面的SQL无法正常执行,事务也无法正常提交
            int x = 1/0;

            String sql2 = "update account set money=money+100 where name = 'B' ";
            conn.prepareStatement(sql2)executeUpdate();

            //sql1 和 sql2都顺利执行,就提交事务
            conn.commit();
            System.out.println("成功!!!");
        } catch (Exception e) {
            //出现异常,通知数据库回滚事务
            conn.rollback();
            e.printStackTrace();
        } finally {
            conn.close();
        }
    }
}

6.JDBC工具类

在不同的请求中,每次都需要连接数据库,释放资源,会写很多的重复代码。

将数据库的连接准备和释放操作封装成一个工具类,使用时直接调用,就可以避免写重复代码。

java 复制代码
public class JdbcUtil {
    private static Connection connection;
    private static String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf8&useSSL=true";
    private static String username = "root";
    private static String password = "123456";

    //驱动(类)只需要加载一次,放静态代码块即可
    static {
        try {
            //加载数据库驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 获取数据库连接对象
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url, username, password);
    }

    // 释放资源(利用多态:Statement 和 PreparedStatement 都可以传进来)
    public static void release(Connection conn, Statement st, ResultSet rs) {
        try {
            if (rs != null) {
                rs.close();
            }
            if (st != null) {
                st.close();
            }
            if (conn != null) {
                conn.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

调用案例,如:增加用户

java 复制代码
    public void add(String name, String password) {
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            conn = JdbcUtil.getConnection();
            String sql = "insert into users(name,password) values(?,?)";
            ps = conn.prepareStatement(sql);
            ps.setString(1, name);
            ps.setString(2, password);
            ps.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.release(conn, ps, null);
        }
    }

7.数据库连接池

(1)概述

JDBC 开发流程:

Ⅰ. 加载数据库驱动(只需要加载一次)

Ⅱ. 建立数据库连接(Connection)

Ⅲ. 创建向数据库发送 SQL 的 Statement 对象,由 Connection 产生

Ⅳ. 编写 SQL 语句

Ⅴ. 执行SQL(查询 --> ResultSet 对象接收结果集)

Ⅵ. 关闭连接,释放资源


数据库连接对象是通过 DriverManager 来获取的,每次获取都需要向数据库申请获取连接,验证用户名和密码。

用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。

每次执行完 SQL 语句,就断开连接,这会造成资源浪费,数据库连接资源没有很好的重复利用。

假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、拓机。

解决办法:数据库连接池

数据库连接池的基本思想:

为数据库建立一个缓冲池,预先向缓冲池中放入一定数量的连接对象。

当要获取数据库连接的时候,只需要从缓冲池中取出一个对象。

用完之后再放回缓冲池中,供下一次请求使用,做到了资源的重复利用,而不需要重复创建。

当数据库连接池中没有空闲的连接对象时,新的请求就会进入等待队列,等待其他线程释放连接。

(2)使用

JDBC 的数据库连接池使用 javax.sql.DataSource 接口来完成的,DataSource 是 java 官方提供的接口。

使用时,开发者不需要自己来实现该接口,可以使用第三方的工具。

C3P0 是一个常用的第三方实现,实际开发中直接使用 C3P0 即可完成数据库连接池的操作。

使用步骤:

① 在 pom.xml 中导入依赖

XML 复制代码
	<dependency>
	    <groupId>com.mchange</groupId>
	    <artifactId>c3p0</artifactId>
	    <version>0.9.5.2</version>
	</dependency>	

② 编写代码

java 复制代码
public class DataSourceTest {
    public static void main(String[] args) {
        try {
            //创建C3P0数据库连接池
            ComboPooledDataSource dataSource=new ComboPooledDataSource();
            dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
            dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf8");
            dataSource.setUser("root");
            dataSource.setPassword("123456");
            //设置初始化连接个数
            dataSource.setInitialPoolSize(5);
            //设置最大连接个数(连接池中不够,可以继续申请,申请后最终的上限)
            dataSource.setMaxPoolSize(20);
            //当连接对象不够时,再次申请的连接对象个数
            dataSource.setAcquireIncrement(5);
            //设置最小连接数(当连接池中剩余2个连接对象时,就去申请 --> 提前做准备)
            dataSource.setMinPoolSize(2);
            Connection conn=dataSource.getConnection();

            //SQL操作...

            //将连接还回到数据库连接池中
            conn.close();
        } catch (PropertyVetoException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

注意:

传统方式拿到的 Connection:com.mysql.cj.jdbc.ConnectionImpl@3c153a1

C3P0 拿到的 Connection:com.mchange.v2.c3p0.impl.NewProxyConnection@6156496

所以,虽然都是调用 close 方法,但实现类不同,所以方法重写也不一样,这就是接口多态。

在 C3P0 中的 close 方法并不是直接销毁连接资源,而是将连接还回到数据库连接池中。


上述对数据库连接池设置参数的方式是直接写在 Java 程序中。

这是采用硬编码的方式,每次更改配置都需要重新编译,生成新的 class 文件,效率太低。

实际开发中,将 C3P0 的配置信息定义在 xml 文件中,java 程序只需要加载配置文件即可完成数据库连接池的初始化操作。

后续只要修改配置,修改 xml 中的配置即可,无需重新编译。

使用步骤:

① 在 resources 目录下,新建一个名为 c3p0-config.xml 的文件

② c3p0-config.xml 中填写配置信息:

java 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <!--配置连接池mysql-->
    <named-config name="C3P0Test">
        <!--   指定连接数据源的基本属性     -->
        <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc?useUnicode=true&amp;characterEncoding=utf8</property>
        <property name="user">root</property>
        <property name="password">123456</property>

        <!--   设置初始化连接个数     -->
        <property name="initialPoolSize">5</property>
        <!--   设置最大连接个数(连接池中不够,可以继续申请,申请后最终的上限)     -->
        <property name="maxPoolSize">20</property>
        <!--   当连接对象不够时,再次申请的连接对象个数     -->
        <property name="acquireIncrement">5</property>
        <!--   设置最小连接数(当连接池中剩余2个连接对象时,就去申请 -> 提前做准备) -->
        <property name="minPoolSize">2</property>
    </named-config>
</c3p0-config>

③ 编写 Java 程序

java 复制代码
public class DataSourceTest {
    public static void main(String[] args) {
        try {
            //创建C3P0数据库连接池
            ComboPooledDataSource dataSource=new ComboPooledDataSource("C3P0Test");
            Connection conn=dataSource.getConnection();
            System.out.println(conn);
            //将连接还回到数据库连接池中
            conn.close();
        }catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

注意:

① ComboPooledDataSource 构造方法中的参数是 c3p0-config.xml 中配置的 named-config 标签的name 属性值。

② 此时 JDBC 工具类可修改为:

java 复制代码
public class JdbcUtil {
    private static DataSource dataSource;

    static {
        dataSource = new ComboPooledDataSource("C3P0Test");
    }

    // 获取数据库连接对象
    public static Connection getConnection() throws SQLException {
        Connection conn = null;
        try {
            conn = dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }

    // 释放资源(利用多态:Statement 和 PreparedStatement 都可以传进来)
    public static void release(Connection conn, Statement st, ResultSet rs) {
        try {
            if (rs != null) {
                rs.close();
            }
            if (st != null) {
                st.close();
            }
            if (conn != null) {
                conn.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

8.DBUtils

java 复制代码
    public static Student findById(Integer idx) {
        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;
        Student stu = null;
        try {
            conn = JdbcUtil.getConnection();

            String sql = "select * from student where id = ?";
            PreparedStatement ps = conn.prepareStatement(sql);
            //给占位符 ? 填充数据
            ps.setInt(1, idx);
            rs = ps.executeQuery();
            //取出结果集的数据
            while (rs.next()) {
                Integer id = rs.getInt(1);
                String name = rs.getString(2);
                Double score = rs.getDouble(3);
                Date birthday = rs.getDate(4);
                stu = new Student(id, name, score, birthday);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                //关闭链接,释放资源
                rs.close();
                st.close();
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return stu;
    }

上述代码中,给占位符填充数据 和 取出结果集中数据的操作太过麻烦。

如果 Student 表中有 100 个属性,那么我们将要在 while 循环中写 100 行 来取出数据,填充占位符也可能需要写很多行。


**解决办法:**DBUtils 可以帮助开发者完成数据的封装(结果集到 Java 对象的映射)。

使用步骤:

① 在 pom.xml 中导入依赖

XML 复制代码
<dependency>
	<groupId>commons-dbutils</groupId>
	<artifactId>commons-dbutils</artifactId>
	<version>1.6</version>
</dependency>

② 编写代码

java 复制代码
    public static Student findById(Integer idx) {
        Connection conn = null;
        Student stu = null;
        try {
            conn = JdbcUtil.getConnection();

            String sql = "select * from student where id = ?";
            //使用DBUtils
            QueryRunner qr = new QueryRunner();
            stu = qr.query(conn, sql, new BeanHandler<>(Student.class), idx);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                //关闭链接,释放资源
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return stu;
    }

此时,填充占位符 和 取出结果集的操作,两行代码即可实现。

细节:

① query 方法需要传入 4 个参数:

Connection 对象

SQL 语句

ResultSetHandler 接口的实现类对象(需要转换后对象的类型:Student.class)

填充占位符的参数

② ResultSetHandler 接口是用来处理结果集的,可以将查询到的结果集转换为 Java 对象,提供了以下 4 种实现类。

|-----------------|-------------------------------------------------------------|
| BeanHandler | 将结果集映射成 Java 对象(如:Student 对象) |
| BeanListHandler | 将结果集映射成 List 集合(如:List<Student >) |
| MapHandler | 将结果集映射成 Map 集合对象 (即:Map<String,Object> key:属性名;value:属性值) |
| MapListHandler | 将结果集映射成 MapList 集合(即:List<Map<<String,Object>>) |

③ 填充占位符的参数是一个可变参数,所以可以传入任意数量的参数,以满足用户不同的需求。

④ 转换后的对象类(Student 类)必须要有无参构造方法,否则程序报错。

原因: 底层通过 Student.class 找到这个类,再通过反射机制找到该类的无参构造,创建其对象。

⑤ 类中属性的名字,必须和数据库表中字段的名字完全一致。

因为创建对象后,根据结果集给对象属性赋值时,是按照名字进行查找赋值的。

十一、MVC三层架构

什么是MVC?

  • Model:模型(service,dao,entity)
  • View:视图(jsp、html、app客户端)
  • Controller :控制器(Servlet、Hander、Action)

请求进入 JavaWeb 应用后,Controller 接收请求,进行业务逻辑处理,最终将结果在返回给用户(View + Model)。

1. 引言

早些年,web应用的操作,用户直接访问控制层,控制层就可以直接操作数据库:

servlet--CRUD(增删改查)-->数据库

servlet的代码中:处理请求、响应、视图跳转、处理JDBC、处理业务代码、处理逻辑代码

**弊端:**程序十分臃肿,不利于维护

**解决方案:**没有什么是加一层解决不了的,如果有,就再加一层!

2. mvc三层架构

Model

  • 业务处理:业务逻辑(Service)
  • 数据持久层: CRUD(DAO数据持久化对象)

view

  • 展示数据
  • 提供链接发起Servlet请求(a,form,img...)

Controller (Servlet)

  • 接收用户的请求 : (req, 请求参数,session信息)
  • 交给业务层处理对应的代码
  • 控制视图跳转

以用户和管理员登录为例:

Controller 层:

java 复制代码
@WebServlet("/login")
public class LoginServlet extends HttpServlet {

    private LoginService loginService = new LoginServiceImpl();

    /* 处理登录的业务逻辑*/
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String type = req.getParameter("type");
        Object object = loginService.login(username,password,type);
        if(object != null){
            HttpSession session = req.getSession();
            switch (type){
                case "reader":
                    Reader reader = (Reader) object;
                    session.setAttribute("reader",reader);
                    //跳转到用户的首页
                    resp.sendRedirect("/book?page=1");
                    break;
                case "admin":
                    Admin admin = (Admin) object;
                    session.setAttribute("admin",admin);
                    //跳转到管理员的首页
                    resp.sendRedirect("/admin?method=findAllBorrow&page=1");
                    break;
            }
        }else{
            resp.sendRedirect("login.jsp");
        }
    }

}

Service 层:

java 复制代码
public interface LoginService {
    //利用多态,动态返回不同类型的对象
    public Object login(String username,String password,String type);
}
java 复制代码
public class LoginServiceImpl implements LoginService {

    private ReaderRepository readerRepository = new ReaderRepositoryImpl();
    private AdminRepository adminRepository = new AdminRepositoryImpl();

    @Override
    public Object login(String username, String password,String type) {
        Object object = null;
        //业务逻辑处理:根据type的值,来选择调用不同的登录方法,去查找不同的表
        switch (type){
            case "reader":
                object = readerRepository.login(username,password);
                break;
            case "admin":
                object = adminRepository.login(username, password);
                break;
        }
        return object;
    }
}

Dao / Repository 层:

java 复制代码
public interface AdminRepository {
    public Admin login(String username,String password);
}
java 复制代码
public interface ReaderRepository {
    public Reader login(String username,String password);
}
java 复制代码
public class AdminRepositoryImpl implements AdminRepository {
    //管理员的登录方法(和数据库交互)
    @Override
    public Admin login(String username, String password) {
        Connection connection = JDBCTools.getConnection();
        String sql = "select * from bookadmin where username = ? and password = ?";
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        Admin admin = null;
        try {
            statement = connection.prepareStatement(sql);
            statement.setString(1,username);
            statement.setString(2,password);
            resultSet = statement.executeQuery();
            if(resultSet.next()){
                admin = new Admin(resultSet.getInt(1),resultSet.getString(2),resultSet.getString(3));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCTools.release(connection,statement,resultSet);
        }
        return admin;
    }
}
java 复制代码
public class ReaderRepositoryImpl implements ReaderRepository {
    //用户的登录方法(和数据库交互)
    @Override
    public Reader login(String username, String password) {
        Connection connection = JDBCTools.getConnection();
        String sql = "select * from reader where username = ? and password = ?";
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        Reader reader = null;
        try {
            statement = connection.prepareStatement(sql);
            statement.setString(1,username);
            statement.setString(2,password);
            resultSet = statement.executeQuery();
            if(resultSet.next()){
                reader = new Reader(resultSet.getInt(1),resultSet.getString(2),resultSet.getString(3),resultSet.getString(4),resultSet.getString(5),resultSet.getString(6),resultSet.getString(7));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCTools.release(connection,statement,resultSet);
        }
        return reader;
    }
}
相关推荐
Freestyle Coding4 小时前
使用rust自制操作系统内核
c语言·汇编·microsoft·rust·操作系统
IT枫斗者1 天前
集合工具类
java·linux·数据库·windows·算法·microsoft
笑川 孙2 天前
C++基础面试题 | C++中的构造函数可以是虚函数吗? C++中的析构函数一定要是虚函数吗?
java·linux·开发语言·c++·microsoft·面试
时光追逐者2 天前
C#/.NET/.NET Core技术前沿周刊 | 第 5 期(2024年9.9-9.15)
microsoft·c#·.net·.netcore
DisonTangor3 天前
微软发布Windows Agent Arena 为生成式AI代理提供基准测试
人工智能·microsoft
aehrutktrjk4 天前
如何从大型语言模型(LLM)流式响应
python·microsoft·ajax·语言模型
Lucky Monkey .4 天前
微软 Power Apps MDA 模型驱动应用解决Image字段查询出来缩略图问题变原图方法(c#+Plugin方式)
开发语言·microsoft·c#·power platform
小奥超人5 天前
批量操作Excel的四个方法(求和、移动、对比、合并)
windows·经验分享·microsoft·excel·办公技巧
激昂~逐流5 天前
qt操作excel(QAxObject详细介绍)
开发语言·qt·microsoft·excel·qaxobject
AI研报5 天前
微软Copilot将集成到新加坡的法律科技平台中
科技·microsoft·copilot