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
目录
[(1)数据库 url](#(1)数据库 url)
[(3) Statement类](#(3) Statement类)
[(4) ResultSet类](#(4) ResultSet类)
[(5) 释放资源](#(5) 释放资源)
[4.SQL 注入问题](#4.SQL 注入问题)
[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&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;
}
}