目錄
JDBC
- JDBC(Java DataBase Connectivity,Java 數據庫連接):通過Java 語言操作數據庫。
- JDBC 本質是官方(Sun 公司)定義的一套操作所有關係型數據庫的規則(接口)。各個數據庫廠商去實現這套接口,提供數據庫驅動(Jar 包)。可以使用這套接口(JDBC)編程,運行時的代碼其實是驅動 Jar 包中的實現類。
1. 快速入門
- 通過 Java 代碼向數據庫 User 表插入一條記錄。
a. 準備數據庫
- 準備數據庫和表
-- 創建庫
CREATE DATABASE regino;
USE regino;
-- 創建用戶表
CREATE TABLE USER(
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50),
PASSWORD VARCHAR(50)
);
INSERT INTO USER(username, PASSWORD)VALUES('admin','123'),('tom','123'),('jack','123');
b. 導入 Jar 包
- 創建 Java 工程,導入 MySQL 驅動 Jar 包
- 相關 Jar 包已上傳至 CSDN:mysql-connector-java-5.1.37
c. 基礎語法
- 註冊驅動
確認 Java 平臺操作的數據庫是 MySQL - 建立連接
Java 平臺與數據庫通信的會話通道 - 編寫 SQL
String sql = ...
- 獲取 SQL 執行對象
- 執行 SQL 並返回結果
- 處理結果
- 釋放資源
d. 示例(重點)
package com.company;
import com.mysql.jdbc.Driver;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
public class JDBCTest {
public static void main(String[] args) throws Exception {
// 1.註冊驅動
DriverManager.registerDriver(new Driver());
// 2.建立連接
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/regino", "root", "root");
// 3.編寫sql(在Java編寫sql 結尾;可以省略)
String sql = "insert into user values(null,'regino','666')";
// 4.獲取sql執行對象
Statement statement = connection.createStatement();
// 5.執行sql並返回結果
int i = statement.executeUpdate(sql);
// 6.處理結果
if (i > 0) {
System.out.println("添加成功");
} else {
System.out.println("添加失敗");
}
// 7.釋放資源
statement.close();
connection.close();
}
}
- MySQL 的默認端口是 3306,可以在 my.ini 配置文件中修改。
2. API 詳解
- 相關 API 都是由 Sun 公司提供的,在 java.sql 包下。
a. DriverManager 類
- DriverManager 是驅動管理對象。提供了以下兩個功能:
i. 註冊驅動
- 有以下幾種方式:
① 新建 Driver 對象(不推薦)
static void registerDriver(Driver driver)
- 通過查看 MySQL Driver 實現類的源碼發現內部的靜態代碼已經提供了註冊驅動功能,相當於註冊了兩次。
② 通過反射加載到 JVM(推薦)
Class.forName("com.mysql.jdbc.Driver");
- 類加載後就會觸發初始化操作。
③ 省略(MySQL 5.5 以上支持)
- SPI 服務提供接口(Service Provider Interface):可以完成隱式註冊。
- 執行加載驅動的語句後,靜態代碼塊的代碼中就出現了 DriverManager。
- 早期的 DriverManager 不知道從哪裏去尋找驅動類,所以需要手動註冊;從 MySQL 5.0 開始 DriverManager 語句就可以省略了。該語句雖然可以不寫,但是建議還是寫上,符合市場的習慣。
ii. 建立連接
static Connection getConnection(String url, String user, String password)
- 參數說明:
- url:連接指定數據庫地址,用到的是 JDBC 協議。
- MySQL 格式:
jdbc:mysql://IP地址:端口/數據庫名
實例(默認端口號 3306):jdbc:mysql://localhost:3306/regino
或jdbc:mysql:///regino
(鏈接的是本地的數據庫且端口號沒有被修改)
- MySQL 格式:
- user:用戶名
- password:密碼
- url:連接指定數據庫地址,用到的是 JDBC 協議。
- 參數說明:
b. Connection 類
- Connection 是數據庫連接對象。提供了以下兩個功能:
i. 獲取 SQL 執行對象
Statement createStatement()
有缺陷PreparedStatement prepareStatement(String sql)
PreparedStatement 是 Statement 的子接口,性能和安全性有所提升。詳見:JDBC 的連接池
- 一個事務中可以創建多個 statement,但是多個用戶(即連接)用不了一個事務。
ii. 事務管理
- 執行完畢提交事務,出現異常回滾事務,提交或者回滾都是結束事務。詳見後文中的事務操作案例。
① 關閉自動提交(開啓事務)
void setAutoCommit(boolean autoCommit)
- 參數:
- true:自動提交(默認值)
- false:手動提交
- 參數:
② 提交事務
void commit()
③ 回滾事務
void rollback()
> 補充:關閉連接的作用
- IO 流關閉資源的目的就是釋放文件資源,因爲文件資源是有限的。
- JDBC 的 close 方法的作用就是釋放連接 Connection 對象。因爲 Connection 代表 Java 程序與 MySQL 數據庫的連接,這種連接數是有限制的,默認情況 MySQL 最高的併發 Connection 數量是 100,所以用完畢之後一定要釋放資源。
c. Statement 類
- Statement 是執行 SQL 的對象。
i. 執行所有類型 SQL 語句
boolean execute(String sql)
- 返回值:如果執行的 SQL 語句是查詢語句則返回 true,其他語句一律是 false,所以該返回值沒有任何的意義
ii. 僅執行 DML 類型 SQL 語句(常用)
int executeUpdate(String sql)
- 參數:DML 類型 SQL(即增刪改 insert、update、delete)
- 返回影響行數
iii. 僅執行 DQL 類型 SQL 語句(常用)
ResultSet executeQuery(String sql)
- 參數:DQL 類型 SQL(即 select 結果)
- 返回結果集
d. ResultSet 類
- ResultSet 是結果集對象,封裝查詢結果,這個結果集會把本次 SQL 查詢的數據全部加載到 ResultSet中。
i. 指針下移
boolean next()
:遊標向下移動一個單位,並且返回是否還有數據,如果遊標沒法向下移動代表已經是數據末尾。相當於迭代器,遍歷時使用循環:while(resultSet.next()){}
- 返回值:
- true:表示此行有數據
- false:表示此行沒有數據
ii. 獲取數據
T getXxx(int 列編號)
(不常用,需要查表才知道對應的列名是什麼)T getXxx(String 列名)
(常用)- 補充:獲取所有類型
Object getObject(String 列名)
String getString(String 列名)
> 補充:用 ResultSet 遍歷時的常見錯誤
- 遊標默認指向第一行(即列名),沒有數據無法獲取。錯誤示例如下:
@Test
public void testResultSet() throws Exception {
//2. 獲取連接
Connection connection = DriverManager.getConnection("jdbc:mysql:///regino", "root", "root");
//3. 創建sql的運輸器
Statement st = connection.createStatement();
//4. 準備sql語句 , 執行sql
String sql = "select * from user";
// resultSet是一個結果集,這個結果集會把本次sql查詢的數據全部加載到ResultSet中。
ResultSet rs = st.executeQuery(sql);
// 下一句會報錯:java.sql.SQLException: Before start of result set
System.out.println("編號:" + rs.getInt("id") + "用戶名:" + rs.getString("username") + rs.getString("password"));
//next() 遊標向下移動一個單位,並且返回是否還有數據,如果遊標沒法向下移動代表已經是數據末尾。
while (rs.next()) {
System.out.println("編號:" + rs.getInt("id") + "用戶名:" + rs.getString("username") + rs.getString("password"));
}
//5. 關閉資源
st.close();
connection.close();
}
- 用 next() 方法可以讓遊標向下移動一個單位,並且返回是否還有數據,如果遊標沒法向下移動,則代表已經是數據末尾。正確示例如下:
@Test
public void testResultSet() throws Exception {
//2. 獲取連接
Connection connection = DriverManager.getConnection("jdbc:mysql:///regino", "root", "root");
//3. 創建sql的運輸器
Statement st = connection.createStatement();
//4. 準備sql語句 , 執行sql
String sql = "select * from user";
// resultSet是一個結果集,這個結果集會把本次sql查詢的數據全部加載到ResultSet中。
ResultSet rs = st.executeQuery(sql);
//next() 遊標向下移動一個單位,並且返回是否還有數據,如果遊標沒法向下移動代表已經是數據末尾。
while (rs.next()) {
System.out.println("編號:" + rs.getInt("id") + "用戶名:" + rs.getString("username") + rs.getString("password"));
}
//5. 關閉資源
st.close();
connection.close();
}
- ResultSet 結果集的數據與查詢條件
select * from 表名
獲得的數據一致,得到所有的字段,就取所有的字段;得到具體的字段,就只能取某個具體的數據是的。例如,select name,age from 表名
,ResultSet 結果集只有 name 與 age 兩個字段的數據。
3. CRUD 示例
- 註冊驅動
- 建立連接
- 編寫 SQL
- 獲取 SQL 執行對象
- 執行 SQL 並返回結果
- 處理結果
- 釋放資源
a. 創建記錄
- 向 user 表 添加一條記錄
// 新增,見配置中的實例
@Test
public void testInsert() throws Exception {
DriverManager.registerDriver(new Driver());
// 1.註冊驅動
Class.forName("com.mysql.jdbc.Driver");
// 2.建立連接
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/regino", "root", "root");
// 3.編寫sql(在Java編寫sql,結尾的;可以省略)
String sql = "insert into user values(null,'regino','666')";
// 4.獲取sql執行對象
Statement statement = connection.createStatement();
// 5.執行sql並返回結果
int i = statement.executeUpdate(sql);
// 6.處理結果
if (i > 0) {
System.out.println("添加成功");
} else {
System.out.println("添加失敗");
}
// 7.釋放資源
statement.close();
connection.close();
}
b. 修改記錄
- 向 user 表 修改一條記錄
// 修改
@Test
public void testUpdate() throws Exception {
// 1.註冊驅動
Class.forName("com.mysql.jdbc.Driver");
// 2.建立連接
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/regino", "root", "root");
// 3.編寫sql
String sql = "update user set username = '勒布朗' where id = 5";
// 4.獲取sql執行對象
Statement statement = connection.createStatement();
// 5.執行sql並返回結果
int i = statement.executeUpdate(sql);
// 6.處理結果
if (i > 0) {
System.out.println("修改成功");
} else {
System.out.println("修改失敗");
}
// 7.釋放資源
statement.close();
connection.close();
}
c. 刪除記錄
- 向 user 表刪除一條記錄
// 刪除
@Test
public void testDelete() throws Exception {
// 1.註冊驅動
Class.forName("com.mysql.jdbc.Driver");
// 2.建立連接
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/regino", "root", "root");
// 3.編寫sql
String sql = "delete from user where id = 7";
// 4.獲取sql執行對象
Statement statement = connection.createStatement();
// 5.執行sql並返回結果
int i = statement.executeUpdate(sql);
// 6.處理結果
if (i > 0) {
System.out.println("刪除成功");
} else {
System.out.println("刪除失敗");
}
// 7.釋放資源
statement.close();
connection.close();
}
d. 查詢記錄
- 向 user 表查詢所有記錄
// 查詢
@Test
public void testFindAll() throws Exception {
// 1.註冊驅動
Class.forName("com.mysql.jdbc.Driver");
// 2.建立連接
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/regino", "root", "root");
// 3.編寫sql
String sql = "select * from user";
// 4.獲取sql執行對象
Statement statement = connection.createStatement();
// 5.執行sql並返回結果
ResultSet resultSet = statement.executeQuery(sql);
// 6.處理結果
while (resultSet.next()) {
// 獲取數據
int id = resultSet.getInt("id");
String username = resultSet.getString("username");
String password = resultSet.getString("password");
System.out.println("編號:" + id + " 用戶名:" + username + " 密碼:" + password);
}
// 7.釋放資源
resultSet.close();
statement.close();
connection.close();
}
- JDBC 可以直接創建庫或表,但是一般只是用 JDBC 來操作數據庫的數據,即增刪改查。
4. 工具類 JdbcUtils
a. 目的
- 通過上面案例需求,發現每次去執行 SQL 語句都需要註冊驅動,獲取連接,得到 Statement,以及釋放資源。經過了很多重複的勞動,爲了簡化書寫,一勞永逸,可以將重複的代碼定義到一個工具類中。
b. 步驟分析
public class JdbcUtils{
// 1.註冊驅動(保證一次)
static{
}
// 2.提供獲取連接的靜態方法
public static Connection getConnection(){
return null;
}
// 3.提供釋放資源的方法
public void close(){
}
}
c. 普通方案
import java.sql.*;
public class JdbcUtils {
// 1.註冊驅動
static {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
// e.printStackTrace();
throw new RuntimeException("加載MySQL驅動失敗");
}
}
// 2.提供獲取連接的靜態方法
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection("jdbc:mysql://localhost:3306/regino", "root", "root");
}
// 3.提供釋放資源的方法
public static void close(ResultSet resultSet, Statement statement, Connection connection) {
//傳的值如果是null就會有NPE
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
// 重載關閉方法
public static void close(Statement statement, Connection connection) {
close(null, statement, connection);
}
}
> 補充:Connection 導包問題
- 注意導入的是 JDK 中的 Connection:(用 MySQL 的 Connection 再強轉後也可以使用,但是如果更換數據庫就要改源碼)
- 確認 Connection、Statement 都是 java.sql 包下的。
d. 升級方案
i. 抽取配置文件
- Java 代碼與數據庫配置信息產生了耦合,將可能會修改的參數抽取到配置文件 properties 中。
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/regino
jdbc.user=root
jdbc.password=root
ii. 編寫工具類
import java.sql.*;
import java.util.ResourceBundle;
public class JdbcUtils {
// 聲明變量
private static String driver = null;
private static String url = null;
private static String user = null;
private static String password = null;
// 加載jdbc.properties配置文件,初始化變量
static {
// new Properties().load();
// sun公司專門提供了一從src目錄下加載properties類型的工具類 ResourceBundle
ResourceBundle jdbc = ResourceBundle.getBundle("jdbc");
driver = jdbc.getString("jdbc.driver");
url = jdbc.getString("jdbc.url");
user = jdbc.getString("jdbc.user");
password = jdbc.getString("jdbc.password");
}
// 1.註冊驅動
static {
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
// e.printStackTrace();
throw new RuntimeException("加載MySQL驅動失敗");
}
}
// 2.提供獲取連接的靜態方法
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, user, password);
}
// 3.提供釋放資源的方法
public static void close(ResultSet resultSet, Statement statement, Connection connection) {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
// 重載關閉方法
public static void close(Statement statement, Connection connection) {
close(null, statement, connection);
}
}
> 補充:判斷非空的作用
- 關閉資源一般放到 try…catch 中的 finally 塊中,Connection 和 Statement 都要在 try…catch 塊外初始化爲 null。如果配置文件 jdbc.properties 中出現錯誤,執行
connection = JdbcUitls.getConnection();
時就會出現異常,然後直接跳到 catch 塊中,所以需要在 finally 塊中關閉資源,因爲不管怎麼樣都會執行 finally 塊中的代碼。 - 示例如下:
@Test
public void whyNull() throws Exception {
Connection connection = null;
Statement st = null;
ResultSet rs = null;
try {
//2. 獲取連接
connection = JdbcUtils.getConnection(); // 執行該語句的時候就會出現異常,
//3. 創建sql的運輸器
st = connection.createStatement();
//4. 準備sql語句 , 執行sql
String sql = "select name,balance from account";
// resultSet是一個結果集,這個結果集會把本次sql查詢的數據全部加載到ResultSet中。
rs = st.executeQuery(sql);
//next() 遊標向下移動一個單位,並且返回是否還有數據,如果遊標沒法向下移動代表已經是數據末尾。
if (rs.next()) {
System.out.println("姓名:" + rs.getString("name") + " 餘額:" + rs.getDouble("balance"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//出現異常的時候資源還是還關閉,沒因爲在finally塊中
JdbcUtils.close(rs, st, connection);
}
}
e. 測試 JdbcTest
import org.junit.Test;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
public class JdbcTest {
@Test
public void findById() throws Exception {
Connection connection = JdbcUtils.getConnection();
String sql = "select * from user where id =1";
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()) {
int id = resultSet.getInt("id");
String username = resultSet.getString("username");
String password = resultSet.getString("password");
System.out.println("編號:" + id + " 用戶名:" + username + " 密碼:" + password);
}
JdbcUtils.close(resultSet, statement, connection);
}
}
5. 事務操作案例
- 事務:如果一個包含多個步驟的業務操作,被事務管理,那麼這些操作要麼同時成功,要麼同時失敗。
- MySQL 操作:
- 開啓事務
begin | start transaction;
- 提交事務
commit;
- 回顧事務
rollback;
- 開啓事務
- Java 操作(使用 Connection 對象)
- 關閉自動提交(開啓事務)
void setAutoCommit(false);
- 提交事務
void commit();
- 回顧事務
void rollback();
- 關閉自動提交(開啓事務)
a. 主要需求
- 通過 Java 代碼實現轉賬案例
b. 導入賬戶表
-- 創建庫
create database regino;
-- 使用庫
use regino;
-- 創建數據表
CREATE TABLE account ( -- 賬戶表
id INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(32),
money DOUBLE
);
-- 添加數據
INSERT INTO account (`name`, money) VALUES ('小明', 1000), ('花花', 1000);
c. 編寫轉賬代碼(重點)
- TX 是 transaction 的簡寫
import org.junit.Test;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
public class TXTest {
@Test
public void testTX() {
Connection connection = null;
Statement statement = null;
try {
// 1.獲取連接(JdbcUtils工具類)
connection = JdbcUtils.getConnection();
// 2.開啓事務
connection.setAutoCommit(false);
statement = connection.createStatement();
// 3.花花扣錢
String xiangSql = "update account set money = money - 100 where id = 2";
int xiangResult = statement.executeUpdate(xiangSql);
if (xiangResult > 0) {
System.out.println("花花支付成功");
}
// 4.小明加錢
String dieSql = "update account set money = money + 100 where id = 1";
int dieResult = statement.executeUpdate(dieSql);
if (dieResult > 0) {
System.out.println("小明收款成功");
}
// 5.提交事務
connection.commit();
} catch (Exception e) {
e.printStackTrace();
try {
// 6.回滾事務
connection.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
} finally {
// 7.釋放資源
JdbcUtils.close(statement, connection);
}
}
}
> 補充:故障測試
import org.junit.Test;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
public class TXTest {
@Test
public void testTX() {
Connection connection = null;
Statement statement = null;
try {
// 1.獲取連接(JdbcUtils工具類)
connection = JdbcUtils.getConnection();
// 2.開啓事務
connection.setAutoCommit(false);
statement = connection.createStatement();
// 3.花花扣錢
String xiangSql = "update account set money = money - 100 where id = 2";
int xiangResult = statement.executeUpdate(xiangSql);
if (xiangResult > 0) {
System.out.println("花花支付成功");
}
// 機器故障
int a = 1 / 0;
// 4.小明加錢
String dieSql = "update account set money = money + 100 where id = 1";
int dieResult = statement.executeUpdate(dieSql);
if (dieResult > 0) {
System.out.println("小明收款成功");
}
// 5.提交事務
connection.commit();
} catch (Exception e) {
e.printStackTrace();
try {
// 6.回滾事務
connection.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
} finally {
// 7.釋放資源
JdbcUtils.close(statement, connection);
}
}
}
- 機器故障,雙方存款未變:
6. 綜合案例:用戶登錄
a. 主要需求
- 用戶輸入賬號和密碼,實現登錄網站功能。
b. 需求分析
- 可以升級爲 MVC 三層架構:
c. 代碼實現
i. 創建 Web 工程,並導入 Jar 包
ii. 導入頁面資源
- 相關 Jar 包已上傳至 CSDN:前端頁面素材
iii. 複製 JdbcUtils 工具類
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/regino
jdbc.user=root
jdbc.password=root
package com.regino.util;
import java.sql.*;
import java.util.ResourceBundle;
public class JdbcUtils {
// 聲明變量
private static String driver = null;
private static String url = null;
private static String user = null;
private static String password = null;
// 加載jdbc.properties配置文件,初始化變量
static {
// new Properties().load();
// sun公司專門提供了一從src目錄下加載properties類型的工具類 ResourceBundle
ResourceBundle jdbc = ResourceBundle.getBundle("jdbc");
driver = jdbc.getString("jdbc.driver");
url = jdbc.getString("jdbc.url");
user = jdbc.getString("jdbc.user");
password = jdbc.getString("jdbc.password");
}
// 1.註冊驅動
static {
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
// e.printStackTrace();
throw new RuntimeException("加載MySQL驅動失敗");
}
}
// 2.提供獲取連接的靜態方法
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, user, password);
}
// 3.提供釋放資源的方法
public static void close(ResultSet resultSet, Statement statement, Connection connection) {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
// 重載關閉方法
public static void close(Statement statement, Connection connection) {
close(null, statement, connection);
}
}
- 注意:properties 在 src 包下。
iv. login.jsp 引出 LoginServlet
v. LoginServlet
package com.regino.web;
import com.regino.util.JdbcUtils;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 統一編碼
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
// 1.接收請求
String username = request.getParameter("username");
String password = request.getParameter("password");
try {
// 2.操作JDBC
// 2.1 獲取連接
Connection connection = JdbcUtils.getConnection();
// 2.2 編寫sql
// String sql = "select * from user where username ='admin' and password ='123'";
String sql = "select * from user where username ='" + username + "' and password ='" + password + "'";
System.out.println(sql);
// 2.3 獲取sql執行對象
Statement statement = connection.createStatement();
// 2.4 執行sql並返回結果
ResultSet resultSet = statement.executeQuery(sql);
// 3.判斷是否登錄成功
if (resultSet.next()) {// 成功
String loginUsername = resultSet.getString("username");
request.getSession().setAttribute("loginUsername", loginUsername);
response.sendRedirect(request.getContextPath() + "/list.jsp");
} else {// 失敗
request.setAttribute("error", "用戶名或密碼錯誤");
request.getRequestDispatcher("/login.jsp").forward(request, response);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
> 補充:MVC 三層架構實現
① EncodeFilter
package com.regino.web;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebFilter("/EncodeFilter")
public class EncodeFilter implements Filter {
public void init(FilterConfig config) throws ServletException {
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws ServletException, IOException {
// 向下轉型
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
// 如果是post請求,統一request編碼
if (request.getMethod().equalsIgnoreCase("post")) {
request.setCharacterEncoding("utf-8");
}
// 不管是post還是get請求,都要統一response編碼
response.setContentType("text/html;charset=utf-8");
// 放行
chain.doFilter(servletRequest, servletResponse);
}
public void destroy() {
}
}
② LoginServlet
package com.regino.web;
import com.regino.service.LoginService;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1.接收請求
String username = request.getParameter("username");
String password = request.getParameter("password");
LoginService loginService = new LoginService();
Boolean isCorrect = loginService.findById(username, password);
if (isCorrect) {
String loginUsername = username;
request.getSession().setAttribute("loginUsername", loginUsername);
response.sendRedirect(request.getContextPath() + "/list.jsp");
} else {
request.setAttribute("error", "用戶名或密碼錯誤");
request.getRequestDispatcher("/login.jsp").forward(request, response);
}
}
}
③ LoginService
package com.regino.service;
import com.regino.dao.LoginDao;
public class LoginService {
LoginDao loginDao = new LoginDao();
public boolean findById(String username, String password) {
return loginDao.findById(username, password);
}
}
④ LoginDao
package com.regino.dao;
import com.regino.util.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class LoginDao {
public boolean findById(String username, String password) {
// 調用數據庫查詢
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
// 2.操作JDBC
// 2.1 獲取連接
connection = JdbcUtils.getConnection();
// 2.2 編寫sql
String sql = "select * from user where username ='" + username + "' and password ='" + password + "'";
System.out.println(sql);
// 2.3 獲取sql執行對象
statement = connection.createStatement();
// 2.4 執行sql並返回結果
resultSet = statement.executeQuery(sql);
// 3.判斷是否登錄成功
// 正確則返回至少一條,錯誤則什麼都沒有,所以只要判斷遊標能否下移一次即可
if (resultSet.next()) {// 成功
return true;
} else {// 失敗
return false;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.close(resultSet, statement, connection);
}
return false;
}
}
vi. list.jsp 顯示登錄人
vii. login.jsp 返回登錄失敗
d. 測試