JavaWeb(三:JDBC 与 MVC)

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

目录

十、JDBC

1.概述

2.编写JDBC程序 

3.细节分析

(1)数据库 url

 (2)Connection类

(3) Statement类

(4) ResultSet类

(5) 释放资源

4.SQL 注入问题

5.JDBC事务

6.JDBC工具类

7.数据库连接池

(1)概述

(2)使用

8.DBUtils

 十一、MVC三层架构

1. 引言

2. mvc三层架构


十、JDBC

1.概述

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

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

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

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

包括两个层面:

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

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

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

2.编写JDBC程序 

① 创建数据库表

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;

② 导入数据库依赖

<!--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 执行后查询到的结果)

Ⅵ. 关闭连接

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 字符串,出错率较高

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短路,从而窃取数据库数据。

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 占位符的功能。

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

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();提交事务

 ① 创建账户表

/*创建账户表*/
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);

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

//失败后让数据库自动回滚事务
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工具类

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

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

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();
        }
    }
}

调用案例,如:增加用户


    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 中导入依赖 

	<dependency>
	    <groupId>com.mchange</groupId>
	    <artifactId>c3p0</artifactId>
	    <version>0.9.5.2</version>
	</dependency>	

 ② 编写代码

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 中填写配置信息:

<?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 程序

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 工具类可修改为:

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

    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 中导入依赖

<dependency>
	<groupId>commons-dbutils</groupId>
	<artifactId>commons-dbutils</artifactId>
	<version>1.6</version>
</dependency>

② 编写代码

    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 层:

@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 层:

public interface LoginService {
    //利用多态,动态返回不同类型的对象
    public Object login(String username,String password,String type);
}
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 层:

public interface AdminRepository {
    public Admin login(String username,String password);
}
public interface ReaderRepository {
    public Reader login(String username,String password);
}
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;
    }
}
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;
    }
}

 

  • 21
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值