JDBC
一、引言
1.1 如何操作數據
使用客戶端工具訪問數據庫,需要手工建立鏈接,輸入用戶名和密碼登陸,編寫SQL語句,點擊執行,查看操作結果(結果集或受影響行數)。
1.2實際開發中,會採用客戶端操作數據庫嗎?
在實際開發過程中,當用戶的數據發生改變時,不會通過客戶端操作執行SQL語句,因爲操作量過大,無法保證效率和正確性。
二、JDBC (JAVA DATABASE CONNECTIVITY)
2.1 什麼是JDBC?
JDBC(Java DataBase Connectivity) JAVA 連接數據庫,可以使用Java語言連接數據庫完成CRUD操作
2.2 JDBC核心思想
JAVA 中定義了訪問數據庫的而接口,可以爲多種關係型數據庫提供了統一的訪問方式
由數據庫廠商提供數據庫驅動實現類(Driver驅動)
2.2.1 MySQL 數據庫驅動
- mysql-connector-java-5.1.x 適用於5.x版本
- mysql-connector-java-8.0.x 適用於8.x 版本
2.2.2 JDBC API
JDBC 是由多個接口和類進行功能實現
類型 | 全限定名 | 簡介 |
---|---|---|
class | java.sql.DriverManage | 管理多個數據庫驅動類,提供了獲取數據庫連接的方法 |
interface | java.sql.Connection | 代表一個數據庫連接(當Connection不是NULL時,標識已連接一個數據) |
interface | java.sql.Statement | 發送SQL語句到數據庫的工具 |
interface | java.sql.ResultSet | 保存SQL查詢語句的結果數據(結果集) |
class | java.sql.SQLException | 處理數據庫應用程序時所發生的異常 |
2.3 環境搭建
1.在項目中創建一個lib文件夾用於存放jar文件
2.將jar包放輸入lib文件夾下
3.將lib文件夾作爲library
三、JDBC開發步驟[重點]
3.1 註冊驅動
使用Class.forName(“com.mysql.jdbc.Driver”)手動加載字節碼文件到JVM中
Class.forName("com.mysql.jdbc.Driver");
3.2 連接數據庫
- 通過DriverManager.getConnection(url,user,password) 獲得數據庫連接對象
- URL:jdbc:mysql://主機名(ip):端口號/數據庫名
- user:用戶名
- password:密碼
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/database?useUnicode=true&characterEncoding=utf8","root","123");
3.3 獲取發送SQL的對象
通過Connection對象獲得Statement對象,用於對數據庫進行通用訪問的
Statement statement = connection.createStatement();
3.4執行SQL語句
編寫SQL語句,並執行,接收執行後的數據
int result = statement.executeUpdate("update stu set student_name='李敢敢',sex='女' where
student_id = 'S1003'");
3.5處理結果
接收並處理操作結果
if(result > 0){
System.out.println("執行成功");
}
3.6釋放資源
遵循的是先開後關的原則,釋放過程中用到的資源連接
statement.close()
connection.close()
3.7綜合案例
綜合六步:實現增刪改
public class BasicJDBC {
public static void main(String[] args) {
try {
// 註冊驅動
Class.forName("com.mysql.jdbc.Driver");
// 連接數據庫
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/companydb?useUnicode=true&characterEncoding=utf-8","root","root");
// 獲取發送SQL對象
Statement statement = connection.createStatement();
// 編寫SQL語句,執行SQL語句(返回受影響行數)
String sql = "";
int i = statement.executeUpdate(sql);
//處理數據
if(i>0){
System.out.println("執行成功");
}
//釋放資源
statement.close();
connection.close();
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
}
}
四、ResultSet(結果集)
在執行查詢SQL後,存放查詢到的查詢結果集數據
4.1接收結果集
ResultSet rs = statement.executeQuery(sql)
Result rs =statement.executeQuery("");
4.2 遍歷ResultSet中的數據
ResultSet以表(Table)結構進行臨時結果的存儲,需要通過JDBC API將其中的數據進行依此獲取
- 數據行指針:初始位置在第一行數據前,沒調用依此boolean next()方法,ResultSet中指針向下移動一行,結果true,表示當前行有數據
- rs.getXxx(“列名”); 根據列名獲得數
- rs.getXxx(“整數下標”);根據列的編號舒徐獲得!從1開始
boolean next() throws SQLException;//判斷rs結果集中下一行是否有數據
4.2.1遍歷方法
int getInt(int columnIndex) throws SQLException;// 獲取當前行的第N列的int值
int getInt(String columnLabel) throws SQLException;//獲得當前行column
4.3綜合案例
對stu表所有數據進行遍歷
4.3.1根據列的名稱進行遍歷
public class TestDql {
public static void main(String[] args) {
try {
//1.註冊驅動
Class.forName("com.mysql.jdbc.Driver");
//2.獲得連接
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/companyDB?useUnicode=true&characterEncoding=utf-8","root","lixiaoning12144014");
//3.獲得SQL執行對象
Statement statement = connection.createStatement();
//4.執行SQL語句
String sql = "select * from `Student`";
ResultSet resultSet = statement.executeQuery(sql);
//5.對結果集進行處理
while(resultSet.next()){ // 判斷結果集中是否有下一行!
// 根據列名獲取當前行的每一列數據
String id= (String)resultSet.getObject("student_id");
String name = (String)resultSet.getObject("student_name");
String sex = (String)resultSet.getObject("sex");
Date borndate = resultSet.getDate("borndate");
String phone = (String)resultSet.getObject("phone");
Integer gradeid = (Integer)resultSet.getObject("GradeId");
System.out.println(id+"\t"+name+"\t"+sex+"\t"+borndate.toString()+"\t"+phone+"\t"+gradeid);
}
//6.釋放資源
resultSet.close();
statement.close();
connection.close();
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
}
}
4.3.2根據列的編號獲取
public class BasicJDBC {
public static void main(String[] args) {
try {
// 註冊驅動
Class.forName("com.mysql.jdbc.Driver");
// 連接數據庫
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/companydb?useUnicode=true&characterEncoding=utf-8","root","lixiaoning12144014");
// 獲取發送SQL對象
Statement statement = connection.createStatement();
// 編寫SQL語句,執行SQL語句(返回受影響行數)
String sql = "";
int i = statement.executeUpdate(sql);
//處理數據
if(i>0){
System.out.println("執行成功");
}
//釋放資源
statement.close();
connection.close();
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
}
}
五、常見的錯誤
- java.lang.ClassNotFoundException 找不到類 或者 沒有導入jar包
- com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException:Unkonen colum ‘列名’ 與SQL語句相關的錯誤(表名 列名 書寫錯誤 、約束錯誤、插入的值是String類型,但是沒有加單引號)
- com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException:Duplicate entry ‘S1003’ for key ‘PRIMARY’ 原因:主鍵值已經存在!更改要插入的主鍵值
- com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException:Unknown column ‘password’ in
- 可能輸入的值的類型不對,確定插入元素時,對應的值的類型是否爭取
六、綜合案例登陸
6.1創建表
- 創建一張用戶表User
- id:主鍵 自增長
- username 字符串類型 非空
- password 字符串類型 非空
- phone 字符串類型 11爲
- 插入兩條數據
6.2實現登錄
- 通過控制檯,用戶輸入用戶名和密碼
- 用戶輸入的用戶名和密碼作爲參數,編寫SQL語句
- 如果查詢到用戶,則用戶存在,提示登陸成功,反之,提示失敗
七、SQL注入問題
7.1什麼是SQL注入
當用戶輸入的數據中有SQL關鍵字或語法時,並且參與了SQL語法的編譯,導致SQL語句編譯後條件結果爲true時
7.2 如何避免SQL注入
由於編寫的SQL語句,是在用戶輸入數據後,整合後再編譯成SQL語句。所以爲了避免SQL注入的問題,使得SQL語句再用戶輸入數據前,SQL語句已經完成編譯,成爲了完整的SQL語句,在進行填充數據。
八、PreparedStatement[重點]
PreparedStatement接口繼承了Statement接口,執行SQL語句的方法沒有區別!
8.1 PreparedStatement 的應用
作用:1.預編譯SQL語句,效率高!
2.安全,可以避免SQL注入
3.可以動態的填充數據,執行多個同構的SQL語句
8.1.1參數標記
//1.預編譯SQL語句
PreparedStatement pstms = connection.prepareStatement(sql);
8.1.2 動態參數綁定
pstmt.setXxx(下標,值); 參數下標是從1開始,爲指定佔位符下標綁定值
//2.爲佔位符下標賦值
pstm.setString(1,username)
pstm.setString(2,password)
九、綜合練習
9.1創建數據庫、表
數據庫 Account
- 創建一張表Account,有以下列
- cardId:字符串,主鍵
- password:字符串,非空
- username,字符串,非空
- balance:小數,非空
- phone:字符串,非空
9.2.創建項目通過JDBC實現功能
創建AccountSystem類,完成下列功能
- 開戶:控制檯輸入所有的賬戶信息,使用PreparedStatement添加至t_account表
- 存款:控制檯輸入卡號、密碼、存儲金額進行修改
- 取款:輸入卡號、密碼、取款金額
- 轉賬:輸入卡號、密碼、對方卡號、轉賬金額進行修改
- 修改密碼:控制檯輸入卡號、密碼、在輸入新密碼進行修改
- 註銷:控制檯輸入卡號、密碼,刪除對應的賬戶信息
創建表
CREATE DATABASE if not exists Account CHARACTER SET utf8;
USE Account;
CREATE TABLE t_account(
`cardId` VARCHAR(20) PRIMARY KEY not null,
`password` varchar(20) not null,
`balance` DOUBLE NOT NULL,
`phone` varchar(11)
)charset=utf8;
try-catch ctrl+alt+t
代碼格式化 ctrl+alt+L
快速定位異常處: F2
package com.qfedu.jdbc.t4;
import java.sql.*;
import java.util.Scanner;
public class AccountSystem {
Scanner scanner = new Scanner(System.in);
private static Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
static {
// 1.註冊驅動
try {
Class.forName("com.mysql.jdbc.Driver");
// 2.創建數據庫連接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/Account?useUnicode=true&characterEncoding=utf-8","root","lixiaoning12144014");
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
}
// 開戶
public void regiter(){
System.out.println("請輸入卡號:");
String cardId = scanner.next();
System.out.println("請輸入密碼");
String password = scanner.next();
System.out.println("請輸入用戶名");
String username = scanner.next();
System.out.println("請輸入存款金額");
String balance = scanner.next();
System.out.println("請輸入預留手機號碼:");
String phone = scanner.next();
try {
//3.創建Preparement
String sql ="insert into `t_account`(`cardId`,`password`,`username`,`balance`,`phone`) values(?,?,?,?,?)";
preparedStatement = connection.prepareStatement(sql);
//4.爲佔位符賦值
preparedStatement.setString(1,cardId);
preparedStatement.setString(2,password);
preparedStatement.setString(3,username);
preparedStatement.setString(4,balance);
preparedStatement.setString(5,phone);
//5.執行SQL語句
int i = preparedStatement.executeUpdate();
if(i>0){
System.out.println("開戶成功");
}else{
System.out.println("開戶失敗");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
try {
if(preparedStatement!=null){
preparedStatement.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
// 更新
public void saveMoney(){
System.out.println("請輸入卡號:");
String cardId = scanner.next();
System.out.println("請輸入密碼:");
String pasword = scanner.next();
System.out.println("請輸入存款金額");
double money = scanner.nextDouble();
if(money > 0){
//存款操作
String sql = "update t_account set balance = balance + ? where cardId=? and password=?";
try {
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setDouble(1,money);
preparedStatement.setString(2,cardId);
preparedStatement.setString(3,pasword);
// 執行,接收返回結果
int i = preparedStatement.executeUpdate();
if(i>0){
System.out.println("存款成功");
}else{
System.out.println("存款失敗!用戶名和密碼請覈對!");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
if(preparedStatement!=null){
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}else{
System.out.println("您輸入的金額不正確");
}
}
// 取款
public void takeMoney(){
System.out.println("請輸入卡號:");
String cardId = scanner.next();
System.out.println("請輸入密碼:");
String password = scanner.next();
System.out.println("請輸入取款金額");
double money = scanner.nextDouble();
if(money > 0){
String sql = "select balance from t_account where cardId=? and password=?";
try {
preparedStatement =connection.prepareStatement(sql);
preparedStatement.setString(1,cardId);
preparedStatement.setString(2,password);
resultSet = preparedStatement.executeQuery();
if(resultSet.next()){
double balance = resultSet.getDouble(1);
if(money<=balance){
//取款成功
String sql2="update t_account set balance = balance-? where cardId=? and password=?";
preparedStatement = connection.prepareStatement(sql2);
preparedStatement.setDouble(1,money);
preparedStatement.setString(2,cardId);
preparedStatement.setString(3,password);
int i = preparedStatement.executeUpdate();
if(i>0){
System.out.println("取款成功!");
}else{
System.out.println("取款失敗!");
}
}else{
System.out.println("餘額不足!");
}
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
if(resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(preparedStatement!=null){
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}else{
System.out.println("輸入金額有誤!");
}
}
// 轉賬
public void transferAccountS() {
System.out.println("請輸入轉賬方卡號:");
String cardId = scanner.next();
System.out.println("請輸入轉賬方密碼:");
String pasword = scanner.next();
System.out.println("請輸入轉賬金額");
double money = scanner.nextDouble();
System.out.println("請輸入接收方卡號:");
String cardId2 = scanner.next();
// 確定轉賬金額大於0
if(money > 0){
// 查詢轉賬方是否存在
String sql = "select balance from t_account where password=? and cardId=?";
try {
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1,pasword);
preparedStatement.setString(2,cardId);
resultSet = preparedStatement.executeQuery();
if(resultSet.next()){
// 賬號密碼存在
double aDouble = resultSet.getDouble(1);
// 判斷轉賬金額是否小於銀行卡金額
connection.setAutoCommit(false);
if(aDouble >= money){
// 關閉事務的自動提交
// 減錢
String sql2="update t_account set balance = balance-? where password=? and cardId=?";
preparedStatement = connection.prepareStatement(sql2);
preparedStatement.setDouble(1,money);
preparedStatement.setString(2,pasword);
preparedStatement.setString(3,cardId);
int num1 = preparedStatement.executeUpdate();
// 加錢
String sql3="update t_account set balance = balance+? where cardId=?";
preparedStatement = connection.prepareStatement(sql3);
preparedStatement.setDouble(1,money);
preparedStatement.setString(2,cardId2);
int num2 = preparedStatement.executeUpdate();
if(num1>0 && num2>0){
System.out.println("轉賬成功!");
}else {
System.out.println("轉賬失敗");
}
connection.commit();
}else{
System.out.println("餘額不足");
}
}else{
System.out.println("賬號或密碼不存在");
}
} catch (SQLException e) {
try {
if(connection!=null){
connection.rollback();
}
} catch (SQLException ex) {
ex.printStackTrace();
}
e.printStackTrace();
}finally {
if(resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
try {
connection.setAutoCommit(true);
} catch (SQLException e) {
e.printStackTrace();
}
if(preparedStatement!=null){
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
// 轉賬金額是否小於卡內餘額
//
}else{
System.out.println("轉賬金額輸入有誤");
}
}
// 關閉Connection
public void closeConnection(){
if(connection!=null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
// 修改密碼
public void changePassword() {
System.out.println("請輸入卡號:");
String cardId = scanner.next();
System.out.println("請輸入密碼:");
String password = scanner.next();
String sql = "select * from t_account where cardId=? and password=?";
try {
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1,cardId);
preparedStatement.setString(2,password);
ResultSet resultSet = preparedStatement.executeQuery();
if(resultSet.next()){
System.out.println("用戶名密碼正確");
System.out.println("請輸入新密碼:");
String newPassword = scanner.next();
System.out.println("請再次輸入新密碼:");
String newRewardPassword = scanner.next();
if(newPassword.equals(newRewardPassword)){
String sql1 ="update t_account set password=? where cardId=?";
preparedStatement = connection.prepareStatement(sql1);
preparedStatement.setString(1,newRewardPassword);
preparedStatement.setString(2,cardId);
int i = preparedStatement.executeUpdate();
if(i>0){
System.out.println("修改成功");
}else{
System.out.println("修改失敗");
}
}else{
System.out.println("兩次密碼輸入不一致,修改失敗!");
}
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
if(resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
if(preparedStatement!=null){
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
// 註銷
public void writeOff() {
System.out.println("請輸入卡號:");
String cardId = scanner.next();
System.out.println("請輸入密碼:");
String password = scanner.next();
String sql = "select * from t_account where cardId=? and password=?";
try {
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1,cardId);
preparedStatement.setString(2,password);
boolean execute = preparedStatement.execute();
if(execute){
System.out.println("是否一定要註銷此賬號? 1.是 2.否");
int result = scanner.nextInt();
if(result==1){
// 確認註銷
String sql1 ="delete from t_account where cardId=?";
preparedStatement =connection.prepareStatement(sql1);
preparedStatement.setString(1,cardId);
int i = preparedStatement.executeUpdate();
if(i>0){
System.out.println("銷戶成功!");
}else{
System.out.println("銷戶失敗!");
}
}
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
if(preparedStatement!=null){
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
package com.qfedu.jdbc.t4;
import java.util.Scanner;
public class TestAccount {
public static void main(String[] args) {
AccountSystem as = new AccountSystem();
Scanner in = new Scanner(System.in);
System.out.println("歡迎進入此係統");
int choice = 0;
do{
System.out.println("1、開戶 2、存款 3、取款 4、轉賬 5、修改密碼 6、註銷 0、退出");
System.out.println("請選擇");
choice = in.nextInt();
switch (choice){
// 開戶
case 1:
as.regiter();
break;
// 存款
case 2:
as.saveMoney();
break;
// 取款
case 3:
as.takeMoney();
break;
// 轉賬
case 4:
as.transferAccountS();
break;
// 修改密碼
case 5:
as.changePassword();
break;
// 註銷
case 6:
as.writeOff();
break;
case 0: as.closeConnection();return;
default:
System.out.println("暫無此功能");
return;
}
}while(choice!=0);
}
}