數據庫相關知識
事務
1. 什麼是事務?
爲了完成某個業務而對數據庫進行一系列操作,這些操作要麼全部成功,要麼全部失敗。
2. 事務的四個特性(ACID)?
- 原子性(Atomicity)
- 事務所涉及的各個操作要麼全部成功,要麼全部失敗
- 一致性(Consistency)
- 事務結束之後,不能夠有非法的數據寫入到數據庫
- 隔離性(lsolation)
- 多個非事務可以同時進行,能一定程度上保證互不影響
- 持久性(Durability)
- 事務完成之後,數據一定會存放在數據庫裏面
3. 隔離級別?
-
讀未提交
- 一個事務可以讀取另外一個事務尚未提交的數據,可能會產生
髒讀
,不可重複讀取
,幻影讀取
問題
- 一個事務可以讀取另外一個事務尚未提交的數據,可能會產生
-
讀已提交
- 一個事務只能讀取另外一個事務已經提交的數據。該隔離級別解決了
髒讀
問題,但是有可能會產生不可重複讀取
和幻影讀取
問題
- 一個事務只能讀取另外一個事務已經提交的數據。該隔離級別解決了
-
可重複讀取
- 在同一個事務當中,多次讀取同一份數據,結果是一致的。該隔離級別解決了
髒讀
,不可重複讀取
,但是仍然有可能產生幻影讀取
問題
- 在同一個事務當中,多次讀取同一份數據,結果是一致的。該隔離級別解決了
-
序列化(串行化)
- 讓事務一個一個串行執行,解決了
髒讀
,不可重複讀取
和幻影讀取
問題
從上往下,隔離級別越來越高;
隔離級別越高,性能越低
- 讓事務一個一個串行執行,解決了
視圖
1. 什麼是視圖?
在已有表或者視圖上創建的虛擬表(存在內存中的)
2. 創建視圖
create view 視圖名(字段列表) as select 語句;
# 如
create view v_emp as select * from t_emp;
create view v_emp2(ename,salary) as select * from t_emp;
--視圖也可以是基於多張表
create view v_teacher_course(cname,tname,level) as select
c.name,t.name,t.level
from t_course c join t_teacher t on c.teacher_id = t.id;
3. 刪除視圖
drop view 視圖名;
4. 視圖的優點
-
簡化:
可以將一些複雜的SQL(比如多張表的join查詢)的查詢結果作爲一個視圖,調用者不用關心這個複雜的查詢怎麼寫。
-
安全:
調用者只能查詢或者修改他們所見到的數據
-
邏輯獨立性:
可以屏蔽真實表結構變化帶來的影響
約束
1. 什麼是約束
約束是一種限制,它通過對錶的行或者列的數據做出限制,確保數據的完整性和一致性
2. 約束的種類
-
主鍵約束
- 要求主鍵值必須唯一且非空
- 一張表最多隻有一個主鍵約束
-
唯一性約束(unique)
-
唯一性約束不允許重複值,但是允許有多個null
-
一個表中可以有多個unique字段
-
create table t_dept( id int primary key, dept_name varchar(50) unique, loc varchar(100) ); create table t_dept( id int primary key, dept_name varchar(50), loc varchar(100), unique(dept_name) );
-
-
外鍵約束
-
示例:
外鍵約束
create table t_dept( id int primary key, dept_name varchar(50), loc varchar(100) ); create table t_staff( id int primary key auto_increment, sname varchar(50), age int, dept_id int, foreign key(dept_id) references t_dept(id) ); # 添加數據時,先添加主表中的數據 insert into t_dept values(100,'HR','location01'); insert into t_staff values(null,'張三',22,100); # 刪除數據時,要先刪除從表中的數據 delete from t_staff where id = 1; delete from t_dept where id = 100
-
-
非空約束(not null)
存儲過程
1. 什麼是數據庫的存儲過程?
存放在數據庫端的一系列SQL語句,用於完成某個業務功能。
2. 如何創建存儲過程
create procedure 存儲過程名(in/out/inout 參數 參數類型)
參數有三種類型:
in
:(默認的)輸入參數,該參數值在調用時需要指定,在存儲過程當中該參數值不能返回out
:輸出參數,該參數值可以被返回。inout
:輸入輸出參數。
3. 調用存儲過程
call 存儲過程名;
示例:
# 執行存儲過程
delimiter //
create procedure getEmp()
begin
select * from t_emp;
end
//
# 創建完存儲過程要將分號改回來
delimiter ;
# 調用存儲過程
call getEmp;
--------------------------
# 執行存儲過程(帶參數in)
delimiter //
create procedure getEmp2(in sid int)
begin
select * from t_emp where id = sid;
end
//
# 創建完存儲過程要將分號改回來
delimiter ;
# 調用存儲過程
call getEmp2(1);
--------------------------
# 執行存儲過程(帶參數out)是可以拿到返回值的
delimiter //
create procedure getEmp3(out max_value decimal(8,2))
begin
select max(salary) into max_value from t_emp;
end
//
# 創建完存儲過程要將分號改回來
delimiter ;
# 調用存儲過程
call getEmp3(@mv);
select @mv;
因爲在控制檯,“;”表示結束,這樣存儲過程就不完整了,所以用"delimiter //“將結束符號改爲”//"
delimiter;
表示創建完存儲過程要將分號改回來,跟以前一樣SQL命令以";"結尾
java操作存儲過程
package util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DBUtil {
public static Connection getConnection() throws Exception{
Connection conn = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/test","root","123456");
}catch(Exception e) {
e.printStackTrace();
throw e;
}
return conn;
}
/**
* 關閉連接
* @param conn
*/
public static void close(Connection conn) {
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws Exception {
// 測試連接是否成功
System.out.println(getConnection());
}
}
package util;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Types;
/**
* 如何調用存儲過程
*/
public class ProcedureTest {
public static void main(String[] args) throws Exception {
test1();
}
/**
* 調用不帶參的存儲過程
* @throws Exception
*/
public static void test1() throws Exception{
Connection conn = null;
try {
conn = DBUtil.getConnection();
CallableStatement cs = conn.prepareCall("{call getEmp}");
ResultSet rs = cs.executeQuery();
while(rs.next()) {
int id = rs.getInt("id");
String ename = rs.getString("ename");
System.out.println("id:" + id + " ename:" + ename);
}
} catch (Exception e) {
e.printStackTrace();
throw e;
}finally {
DBUtil.close(conn);
}
}
/**
* 調用帶參的存儲過程
* @throws Exception
*/
public static void test2() throws Exception{
Connection conn = null;
try {
conn = DBUtil.getConnection();
// 一個參數一個問號
CallableStatement cs = conn.prepareCall("{call getEmp2(?)}");
cs.setInt(1, 1);
ResultSet rs = cs.executeQuery();
while(rs.next()) {
int id = rs.getInt("id");
String ename = rs.getString("ename");
System.out.println("id:" + id + " ename:" + ename);
}
} catch (Exception e) {
e.printStackTrace();
throw e;
}finally {
DBUtil.close(conn);
}
}
/**
* 調用帶參(out參數的)的存儲過程(能獲取返回值的)
* @throws Exception
*/
public static void test3() throws Exception{
Connection conn = null;
try {
conn = DBUtil.getConnection();
// 一個參數一個問號
CallableStatement cs = conn.prepareCall("{call getEmp3(?)}");
cs.registerOutParameter(1, Types.DECIMAL);
cs.execute();
System.out.println(cs.getBigDecimal(1));
} catch (Exception e) {
e.printStackTrace();
throw e;
}finally {
DBUtil.close(conn);
}
}
}
4. 存儲過程的優點:
- 減少應用程序和數據庫之間交互的次數,提升性能
- 將多個應用程序相同的邏輯集中寫在存儲過程裏面,可以共享一部分業務邏輯
5. 存儲過程的缺點
- 存儲過程依賴於特定的數據庫(不同數據庫存儲過程語法不同,不能移植)
- 存儲過程對於特別複雜的業務也不好寫。