JavaWeb(三:JDBC 与 MVC)

news2024/9/21 16:33:47

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

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1925551.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python蜂窝通信Wi-Fi和GPU变分推理及暴力哈希加密协议图消息算法

&#x1f3af;要点 &#x1f3af;图模型和消息传递推理算法 | &#x1f3af;消息传递推理和循环消息传递推理算法 | &#x1f3af;空间人工智能算法多维姿势估计 | &#x1f3af;超图结构解码算法量子计算 | &#x1f3af;GPU处理变分推理消息传递贝叶斯网络算法 | &#x1f3…

5G-A通感融合赋能低空经济-RedCap芯片在无人机中的应用

1. 引言 随着低空经济的迅速崛起&#xff0c;无人机在物流、巡检、农业等多个领域的应用日益广泛。低空飞行器的高效、安全通信成为制约低空经济发展的关键技术瓶颈。5G-A通感一体化技术通过整合通信与感知功能&#xff0c;为低空网络提供了强大的技术支持。本文探讨了5G-A通感…

【中国近代史】林则徐虎门销烟(1839年)

中国古代朝代历史经过两周时间&#xff08;7.03-7.13&#xff09;的分享已经正式结束&#xff0c;首先感谢大家通过那个专栏点赞收藏关注我&#xff0c;这是我继续创作的动力。 接下来新的专栏就是中国近代史。让我们再次走入近代史的潮流中&#xff0c;去学习去感受先辈们的拼…

计组_多处理器的基本概念

2024.06.26&#xff1a;计算机组成原理多处理器的基本概念学习笔记 第21节 多处理器的基本概念 1. 计算机体系结构1.1 SISD单指令流单数据流&#xff08;前面几章一直在学习的内容&#xff09;1.2 SIMD单指令流多数据流1.2.1 改进&#xff1a;向量处理器 1.3 MISD多指令流单数据…

应用帕累托原则学习新的编程语言

在本文中&#xff0c;我将讨论如何应用帕累托原则快速学习一门新的编程语言&#xff0c;并在加深对编程语言的理解的同时开始解决实际问题。 什么是帕累托原则&#xff1f; 帕累托原则&#xff0c;又称 80/20 法则&#xff0c;指出对于许多结果而言&#xff0c;大约 80% 的后…

数据湖仓一体(一) 编译hudi

目录 一、大数据组件版本信息 二、数据湖仓架构 三、数据湖仓组件部署规划 四、编译hudi 一、大数据组件版本信息 hudi-0.14.1zookeeper-3.5.7seatunnel-2.3.4kafka_2.12-3.5.2hadoop-3.3.5mysql-5.7.28apache-hive-3.1.3spark-3.3.1flink-1.17.2apache-dolphinscheduler-3.1.9…

我的智能辅助大师-办公小浣熊

一、基本介绍 随着2022年ChatGPT为代表的AI工具对互联网领域进行第一次冲击后&#xff0c;作为一名对编程领域涉足不算特别深的一名程序员&#xff0c;对AI大模型的接触也真的不能算少了&#xff0c;这是时代的必然趋势。在此之前也曾接触过很多的AI工具&#xff0c;他们都能在…

跨境电商系统如何进行搭建

目前越来越多的商家&#xff0c;他们都在进行跨境电商建站&#xff0c;便于自己在网络上进行营销推广&#xff0c;跨境电商系统的搭建是至关重要的&#xff0c;商家应该先了解跨境电商的模式有哪些&#xff0c;这样才能对跨境电商系统有更好的搭建结果。 跨境电商模式 目前来…

1589. 【中山市第十二届义务教育段学生信息学邀请赛】象战(bishop)(Standard IO)

题目描述 国际象棋的棋盘可以表示为一个 8 行 8 列的格子图&#xff0c;其中每个格子都可以放一枚棋子。我们将第 1 行第 2 列的格子用 (1,2) 来表示&#xff0c;以此类推。 为了帮助妹妹认识国际象棋中的“象”这种棋子&#xff0c;Jimmy 可谓是煞费苦心——他首先教会妹妹&…

【Java】数据类型及类型转换

数据类型 Java语言的数据类型分为两大类&#xff1a; 基础数据类型引用数据类型 基础数据类型 基础数据类型包括以下8种&#xff1a; 类型名称关键字占用内存取值范围区间描述字节型byte1 字节-128~127-27~27-1短整型short2 字节-32768~32767-215~215-1整型int4 字节-2147…

算法学习笔记(8.3)-(0-1背包问题)

目录 最常见的0-1背包问题&#xff1a; 第一步&#xff1a;思考每轮的决策&#xff0c;定义状态&#xff0c;从而得到dp表 第二步&#xff1a;找出最优子结构&#xff0c;进而推导出状态转移方程 第三步&#xff1a;确定边界条件和状态转移顺序 方法一&#xff1a;暴力搜素…

经典元启发式算法的适用范围及基本思想

元启发式算法是针对优化问题设计的一类高级算法&#xff0c;它们具有广泛的适用性&#xff0c;可以解决不同类型的问题。不同的元启发式算法由于其特定的搜索机制和策略&#xff0c;适用的优化问题类型也有所不同。以下是一些常见元启发式算法及其适用范围&#xff1a; 1. 遗传…

OrangePi AIpro 浅上手及搭建卡通图像生成多元化AI服务

前言 很高兴&#xff0c;收到了一份新款 OrangePi AIpro 开发板&#xff0c;这是香橙派第一次与华为昇腾合作&#xff0c;使用昇腾系列 AI 处理器来设计这款高性价比的 AI 开发板。这块开发板不仅性能强大&#xff0c;还支持丰富的硬件接口&#xff0c;为AI开发者提供了一个理…

Nginx的访问限制与访问控制

访问限制 访问限制是一种防止恶意访问的常用手段&#xff0c;可以指定同一IP地址在固定时间内的访问次数&#xff0c;或者指定同一IP地址在固定时间内建立连接的次数&#xff0c;若超过网站指定的次数访问将不成功。 请求频率限制配置 请求频率限制是限制客户端固定时间内发…

代码随想录第十二天|226.翻转二叉树、 101.对称二叉树、104.二叉树的最大深度、111.二叉树的最小深度

文章目录 226.翻转二叉树思路解法一、前序遍历递归解法二、深度优先搜索--迭代 101.对称二叉树思路解法一、递归解法二、迭代 104.二叉树的最大深度解法一、深度优先搜索--递归解法二、广度优先搜索--迭代 111.二叉树的最小深度解法一、深度优先搜索--递归解法二、广度优先搜索…

制作显卡版docker并配置TensorTR环境

感谢阅读 相关概念docker准备下载一个自己电脑cuda匹配的docker镜像拉取以及启动镜像安装cudaTensorRT部署教程 相关概念 TensorRT是可以在NVIDIA各种GPU硬件平台下运行的一个模型推理框架&#xff0c;支持C和Python推理。即我们利用Pytorch&#xff0c;Tensorflow或者其它框架…

2、matlab打开显示保存点云文件(.ply/.pcd)以及经典点云模型数据

1、点云数据简介 点云数据是三维空间中由大量二维点坐标组成的数据集合。每个点代表空间中的一个坐标点&#xff0c;可以包含有关该点的颜色、法向量、强度值等额外信息。点云数据可以通过激光扫描、结构光扫描、摄像机捕捉等方式获取&#xff0c;广泛应用于计算机视觉、机器人…

常用控件(六)

布局管理器 布局管理器垂直布局QHBoxLayoutQGridLayoutQFormLayoutQSpacerItem 布局管理器 之前使⽤ Qt 在界⾯上创建的控件, 都是通过 “绝对定位” 的⽅式来设定的. 也就是每个控件所在的位置, 都需要计算坐标, 最终通过 setGeometry 或者 move ⽅式摆放过去.这种设定⽅式其…

JAVA NIO组件之Buffer详解

❃博主首页 &#xff1a; 「码到三十五」 &#xff0c;同名公众号 :「码到三十五」&#xff0c;wx号 : 「liwu0213」 ☠博主专栏 &#xff1a; <mysql高手> <elasticsearch高手> <源码解读> <java核心> <面试攻关> ♝博主的话 &#xff1a…

标签-镜像拉取策略-重启策略-pod优雅终止-pod中容器资源限制-容器类型-容器守护进程-日志排错-容器命令exec-cp

一.标签 1.概述&#xff1a; 标签是附加到kubernets对象&#xff08;比如pod&#xff09;上的键值对&#xff0c;标签可以在创建时附加到对象&#xff0c;随后也可以随时添加修改&#xff1b;标签不支持唯一性。 在k8s中大多数资源都是通过标签进行关联的&#xff08;如pod与s…