Java學習筆記-Day60 MySQL資料庫(二)
Java學習筆記-Day60 MySQL資料庫(二)
一、連線資料庫
連線資料庫的方式:
(1)DOS命令視窗
(2)MySQL命令視窗
(3)Navicat軟體
連線資料庫的命令:
命令格式:mysql -h hostname -u username -p
- -h 指定要連線的MySQL資料庫所在的主機,如果是本機,可以省略。
- -u 指定登入的使用者名稱,如果不指定,預設是作業系統的登入使用者名稱。
- -p 指定密碼。可以在-p之後直接輸入密碼,但是這種方式密碼將顯示出來。也可以先不輸入密碼,回車之後系統會提示輸入密碼,此時再輸入密碼,密碼將會使用*顯示。
正確連線後將出現歡迎介面。
二、函式
1、字串函式
函式 | 功能 |
---|---|
concat(s1,s2,…sn) | 連線s1,s2,…,sn為一個字串 |
insert(str,x,y,instr) | 將字串str從第x位置開始,y個字元長的子串替換為字串instr |
lower(str) | 將字串str中字元變為小寫 |
upper(str) | 將字串str中字元變為大寫 |
left(str,x) | 返回字串str最左邊的x個字元 |
right(str,x) | 返回字串str最右邊的x個字元 |
lpad(str,n,pad) | 用字串pad對str最左邊進行填充,直到長度為n個字元長度 |
rpad(str,n,pad) | 用字串pad對str最右邊進行填充,直到長度為n個字元長度 |
ltrim(str) | 去掉字串str左側的空格 |
rtrim(str) | 去掉字串str右側的空格 |
repeat(str,x) | 返回str重複x次的結果 |
replace(str,a,b) | 用字串b替換字串str中所有出現的字串a |
strcmp(s1,s2) | 比較字串s1和s2 |
trim(str) | 去掉字串行尾和行頭的空格 |
substring(str,x,y) | 返回從字串str的x位置起y個字元長度的字串 |
2、數值函式
函式 | 功能 |
---|---|
abs(x) | 返回x的絕對值 |
ceil(x) | 返回大於x的最小整數值 |
floor(x) | 返回小於x的最大整數值 |
mod(x,y) | 返回x/y的模 |
rand() | 返回0~1內的隨機值 |
round(x,y) | 返回引數x的四捨五入的有y位小數的值 |
truncate(x,y) | 返回數字x截斷為y |
3、日期與時間函式
函式 | 功能 |
---|---|
curdate() | 返回當前日期 |
curtime() | 返回當前時間 |
now() | 返回當前的日期和時間 |
sysdate() | 返回返回當前日期時間 |
unix_timestamp(date) | 返回日期date的unix時間戳 |
from_unixtime | 返回unix時間戳的日期值 |
week(date) | 返回日期date為一年中的第幾周 |
year(date) | 返回日期date的年份 |
hour(time) | 返回time的小時值 |
minute(time) | 返回time的分鐘值 |
monthname(date) | 返回日期date的月份名 |
date_format(date,fmt) | 返回按字串fmt格式化日期datee值 |
date_add(date,interval expr type) | 返回一個日期或時間值加上一個時間間隔的時間值 |
datediff(expr,expr2) | 返回起始時間expr和結束時間expr2之間的天數 |
timediff(time1,time2) | 返回兩個時間相減得到的差值 (time1 - time2) |
timestampdiff(interval,datetime1,datetime2) | 返回datetime2-datetime1的差值,結果單位有interval引數給出 |
4、流程函式
函式 | 功能 |
---|---|
if(value,t,f) | 如果value是真,返回t;否則返回f |
ifnull(value1,value2) | 如果value1不為空,返回value1,否則value2 |
case when[value1] then [result]…else [default] end | 如果value1是真,返回result,否則返回default |
case [expr] when [value1] then [result1] … else [default] end | 如果expr等於value1,返回result1,否則返回default |
5、常用函式
函式 | 功能 |
---|---|
DATABASE() | 返回當前資料庫名 |
VERSION() | 返回當前資料庫版本 |
USER() | 返回當前登入使用者名稱 |
INET_ATON(IP) | 返回IP地址的數字表示 |
INET_NTOA(num) | 返回數字代表的IP地址 |
PASSWORD(str) | 返回字串str的加密版本 |
MD5() | 返回字串str的MD5值 |
6、其它函式
函式 | 功能 |
---|---|
GREATEST(value1,value2,…) | 獲取最大值 |
LEAST(value1,value2,…) | 獲取最小值 |
COALESCE(value1,value2,…) | 取第一個非null值的欄位 (coalesce(age,sex):有年齡就取年齡,沒有年齡,就取性別) |
ISNULL(expr) | 如果值為空,就返回1 |
三、字串加密
MD5(Message-Digest Algorithm 5)加密是一種不可逆的加密規則,用於確保資訊的完整。任意長度的資料經過MD5加密後得到的值的長度都是固定的(16位或32位),並且對原資料修改一個字元對於加密後的值都有很大的變動。
資料庫存放的密碼是以密文的形式存在,使用者輸入的密碼通過MD5加密成密文,再將此密文與資料庫中的密碼進行對比,如果相同即密碼正確。
- MD5Util.java (自定義MD5演算法)
package com.etc.bs.utils;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* 自定義MD5演算法實現加密
* @author Administrator
*
*/
public class MD5Util {
/**
* 加密演算法
* @param str
* @return
*/
public static String getEncodeByMd5(String str) {
try {
int i;
// 得到MessageDigest的物件
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(str.getBytes());
byte b[] = md.digest();
// 建立StringBuffer物件
StringBuffer buf = new StringBuffer("");
for (int offset = 0; offset < b.length; offset++) {
i = b[offset];
if (i < 0)
i += 256;
if (i < 16)
buf.append("0");
buf.append(Integer.toHexString(i));
}
// 返回結果為32位加密
return buf.toString();
// 返回結果為16位的加密
// return buf.toString().substring(8, 24);
} catch (NoSuchAlgorithmException e) {
// e.printStackTrace();
return null;
}
}
}
- 使用自定義MD5演算法實現加密
String pwd = MD5Util.getEncodeByMd5(pwd);
四、MySQL儲存引擎
MySQL5.7支援的儲存引擎包括:
- InnoDB
- MyISAM
- MEMORY
- CSV
- BLACKHOLE
- ARCHIVE
- MERGE
- FEDERATED
- EXAMPLE
- NDE
- 其中InnoDB和NDB提供事務安全表,其他儲存引擎都是非事務安全表。
MySQL5.5之前預設儲存引擎是MyISAM,5.5版本之後改為InnoDB。
(1)修改預設儲存引擎,在引數檔案中設定:default_storage_engine
(2)檢視當前的預設引擎:show variables like 'default_storage_engine'
(3)查詢當前資料庫支援的儲存引擎:show engines \G
, 其中Support不同值的含義分別為:
- DEFAULT:支援並啟用,並且為預設引擎
- YES:支援並啟用
- NO:不支援
- DISABLED:支援,但是資料庫啟動的時候被禁用
(4)在建立新表的時候,可以通過增加engine關鍵字設定新建表的儲存引擎。
(5)可以使用alter table語句,將一個已經存在的表修改成其他的儲存引擎。
注意:修改表的儲存引擎需要鎖表並複製資料,對於線上環境的表進行這個操作非常危險,除非你非常瞭解可能造成的影響,否則在線上環境請使用其他方式(藉助OSC工具)
1、MyISAM
MyISAM既不支援事務,也不支援外來鍵,對事務完整性沒有要求或者以SELECT、INSERT為主的應用可以使用這個引擎來建立表。可以用於只需要展示資料場景。
2、InnoDB
InnoDB提供了具有提交、回滾和崩潰恢復能力的事務安全保障,同時提供了更小粒度和更強的併發能力,擁有自己獨立的快取和日誌。
對比MyISAM儲存引擎,InnoDB會佔用更多的磁碟空間以保留資料和索引。InnoDB支援事務和外來鍵。
不同於使用其他儲存引擎的表的特點:
(1)自動增長列
(2)外來鍵約束
(3)主鍵和索引
- 滿足唯一和非空約束;
- 優先考慮使用最經常被當作查詢條件的欄位或者自增欄位
- 欄位值基本不會被修改
- 使用盡可能短的欄位
(4)儲存方式
- 共享表空間儲存
- 多有空間儲存
五、SQL注入
結構化查詢語言(SQL)是一種用來和資料庫互動的文字語言。
SQL注入就是利用某些資料庫的外部介面將使用者資料插入到實際的資料庫操作語言當中,從而達到入侵資料庫乃至作業系統的目的。它的產生主要是由於程式對使用者輸入的資料沒有進行嚴格的過濾,導致非法資料庫查詢語句的執行。
SQL注入攻擊的危害:
- 讀取、修改或刪除資料庫資料。
- 獲取資料庫中的敏感資料。
- 獲取資料庫管理員的許可權。
(1)' or 1=1 --
(2)' or 1=1 /*
(3)' or 1=1 #
因為在SQL語句中,–、/*、# 都可以將後面的語句註釋掉,在1=1(條件為真)恆成立的情況下,不用考慮使用者名稱和密碼是否正確。
//SQL注入(Statement)
String sql = "SELECT * FROM users WHERE uname = '"+uname+"' and upwd = '"+upwd+"';";
當 uname 為 ' or 1=1 --
,而upwd為123456,sql語句就會變為 SELECT * FROM users WHERE uname = '' or 1=1 -- ' and upwd = '123456';
,此時SQL語句就會通過。
解決方法:Statement(不安全)使用拼接字串來傳遞引數的,而PrepareStatement(安全)是通過 ?佔位符來傳遞引數,就不會出現SQL注入。
六、事務
事務概念:在單個會話期間,執行一系列有序的資料庫操作。
特性:
(1)原子性:事務的所有步驟必須成功完成,否則,任何步驟都不會被提交。
(2)一致性:事務的所有步驟必須成功完成,否則,所有資料都會被恢復到事務開始前的狀態。
(3)隔離性:未完成事務的所做得步驟必須與系統隔離,直到認為事務完成為止。
(4)永續性:所有的資料都以某種形式儲存,確保系統出現故障時,可以恢復到原始的狀態。
事務只針對採用innodb儲存引擎的表,對於採用myisam儲存引擎的表不支援。
步驟:
(1)開啟事務:start transaction;
/ begin;
(2)執行語句
(3)提交事務:commit;
/ 回滾事務:rollback;
應用場景:使用者轉賬功能
- TestTransfer.java
package com.etc.bs.test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class TestTransfer {
//資料庫連線的url
private static final String url = "jdbc:mysql://localhost:3306/blogdb?useSSL=FALSE&serverTimezone=Asia/Shanghai";
//資料庫連線的user
private static final String user = "root";
//資料庫連線的password
private static final String password = "root";
public static void main(String[] args) {
transfer(1,2,50);
}
/**
* 轉賬功能
* @param payid 付款賬戶id
* @param receiptid 收款賬戶id
* @param balance 金額
*/
public static void transfer(int payid,int receiptid,double balance) {
Connection conn = null;
PreparedStatement pstmt1 = null;
PreparedStatement pstmt2 = null;
String sql1="update users set ubalance=ubalance+? where userid=?;";
String sql2="update users set ubalance=ubalance-? where userid=?;";
try {
// 載入驅動
Class.forName("com.mysql.cj.jdbc.Driver");
// 建立資料庫連線
conn = DriverManager.getConnection(url, user, password);
// 設定手動提交事務(開始事務)
conn.setAutoCommit(false);
// 建立prepareStatement物件
pstmt1 = conn.prepareStatement(sql1);
pstmt2 = conn.prepareStatement(sql2);
// 給空格佔位符賦值
setParameters(pstmt1, balance,receiptid);
setParameters(pstmt2, balance,payid);
// 執行具體操作,獲取結果集
int n1 = pstmt1.executeUpdate();
int n2 = pstmt2.executeUpdate();
System.out.println(n1+"----"+n2);
// 判斷sql是否都成功執行
if(n1>0 && n2>0) {
//提交事務
conn.commit();
System.out.println("轉賬成功!");
}else {
//回滾事務
System.out.println("事務自動回滾!");
conn.rollback();
}
} catch (Exception e) {
//處理異常
try {
//回滾事務
System.out.println("發生異常,事務自動回滾!");
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
} finally {
//關閉資源
try {
pstmt1.close();
pstmt2.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 設定PreparedStatement的SQl語句的引數
*
* @param pstmt PreparedStatement物件
* @param params sql語句的引數
* @throws SQLException SQLException異常
*/
private static void setParameters(PreparedStatement pstmt, Object... params) throws SQLException {
if (params != null) {
for (int i = 0; i < params.length; i++) {
pstmt.setObject(i + 1, params[i]);
}
}
}
}