JDBC學習總結
JDBC
- 概念:Java DataBase Connectivity
- JDBC本質:官方(sun公司)定義的一套操作所有關係型數據庫的規則,即接口。各個數據庫廠商去實現這套接口,提供數據庫驅動jar包。我們可以使用這套接口(JDBC)編程,真正執行的代碼是驅動jar包中的實現類。
快速入門
-
導入jar包,步驟如下:
- 在IDEA中,新建一個文件夾爲libs
- 把jar包貼進libs文件夾中
- 右鍵點擊libs文件夾,選擇Add as library
-
樣例
import java.sql.Connection; import java.sql.DriverManager; import java.sql.Statement; public class quicklyrun { public static void main(String[] args) throws Exception { //註冊驅動 Class.forName("com.mysql.jdbc.Driver"); //獲取數據庫連接對象,第一個爲數據庫名稱,第二個爲用戶名,第三個爲用戶密碼 Connection conn = DriverManager.getConnection("jdbc:mysql:///db3", "root", "root"); //設置sql語句 String sql="UPDATE account SET balance=10 WHERE id=1"; //獲取sql的執行對象 Statement st = conn.createStatement(); //執行sql語句 int result = st.executeUpdate(sql); //查看sql語句的影響行數 System.out.println(result); //釋放資源 st.close(); conn.close(); } }
詳解各個對象
-
DriverManager:驅動管理對象
功能
-
註冊驅動:告訴程序該使用哪一個數據庫驅動jar
static void registerDriver(Driver driver) //註冊與給定的驅動程序 DriverManager
在快速入門中我們爲什麼是這樣註冊驅動的呢?
Class.forName("com.mysql.jdbc.Driver");
查看源碼,原來在com.mysql.jdbc.Driver類中存在靜態代碼塊
static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } }
注意:MySQL5之後的驅動jar包可以省略註冊驅動的步驟。
-
獲取數據庫連接
方法
static Connection getConnection(String url, String user, String password)
參數解釋
- url:指定連接的路徑
- 語法:jdbc:mysql://ip地址(域名):端口號/數據庫名稱
- 例子:jdbc:mysql://localhost:3306/db3
- 細節:如果連接的是本機mysql服務器,並且mysql服務默認端口是3306,則url可以簡寫爲:jdbc:mysql:///數據庫名稱
- user:用戶名
- password:密碼
-
-
Connection:數據庫連接對象
功能
-
獲取執行sql 的對象
Statement createStatement()
PreparedStatement prepareStatement(String sql)
-
管理事務
-
開啓事務
setAutoCommit(boolean autoCommit) //調用該方法設置參數爲false,即開啓事務
-
提交事務
commit()
-
回滾事務
rollback()
-
-
-
Statement:執行sql的對象
執行sql
boolean execute(String sql) //可以執行任意的sql 瞭解
//返回值:影響的行數,可以通過這個影響的行數判斷DML語句是否執行成功 返回值>0的則執行成功,反之,則失敗。 int executeUpdate(String sql) //執行DML(insert、update、delete)語句、DDL(create,alter、drop)語句
ResultSet executeQuery(String sql) //執行DQL(select)語句
-
ResultSet:結果集對象,封裝查詢結果
boolean next() //遊標向下移動一行,判斷當前行是否是最後一行末尾(是否有數據),如果是,則返回false,如果不是則返回true
getXxx(參數) //獲取數據 /* Xxx:代表數據類型 如: int getInt() , String getString() 參數: 1. int:代表列的編號,從1開始 如: getString(1) 2. String:代表列名稱。 如: getDouble("balance") */
-
注意
- 使用步驟
- 遊標向下移動一行
- 判斷是否有數據
- 獲取數據
- 使用步驟
-
練習1
定義一個方法,查詢emp表的數據將其封裝爲對象,然後裝載集合,返回。
我的數據庫定義
-
定義emp類
package demo_jdbc; import java.util.Date; /* 該類用來存放emp表的數據 */ public class emp { private int id; private String ename; private int job_id; private int mgr; private Date joindate; private double salary; private double bonus; private int dept_id; public emp(int id, String ename, int job_id, int mgr, Date joindate, double salary, double bonus, int dept_id) { this.id = id; this.ename = ename; this.job_id = job_id; this.mgr = mgr; this.joindate = joindate; this.salary = salary; this.bonus = bonus; this.dept_id = dept_id; } public emp() { } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getEname() { return ename; } public void setEname(String ename) { this.ename = ename; } public int getJob_id() { return job_id; } public void setJob_id(int job_id) { this.job_id = job_id; } public int getMgr() { return mgr; } public void setMgr(int mgr) { this.mgr = mgr; } public Date getJoindate() { return joindate; } public void setJoindate(Date joindate) { this.joindate = joindate; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } public double getBonus() { return bonus; } public void setBonus(double bonus) { this.bonus = bonus; } public int getDept_id() { return dept_id; } public void setDept_id(int dept_id) { this.dept_id = dept_id; } @Override public String toString() { return "emp{" + "id=" + id + ", ename='" + ename + '\'' + ", job_id=" + job_id + ", mgr=" + mgr + ", joindate=" + joindate + ", salary=" + salary + ", bonus=" + bonus + ", dept_id=" + dept_id + '}'; } }
-
在主類中定義方法 public List findAll(){},實現方法select * from emp
package demo_jdbc; import java.sql.*; import java.util.ArrayList; import java.util.List; public class jdbc_demo1 { public static void main(String[] args) { List<emp> list=new jdbc_demo1().findall(); for (emp em : list) { System.out.println(em); } } public List<emp> findall(){ Connection conn =null; Statement st=null; ResultSet rs =null; List<emp> list=null; try { //註冊驅動,MySQL5.0之後可不寫 Class.forName("com.mysql.jdbc.Driver"); //獲得數據庫連接 conn = DriverManager.getConnection("jdbc:mysql:///db3", "root", "root"); //定義sql語句 String sql="SELECT * FROM emp"; //創建一個sql執行對象 st = conn.createStatement(); //執行sql命令,返回結果集 rs = st.executeQuery(sql); //遊標獲取數據 list=new ArrayList<emp>(); emp e=null; while(rs.next()){ int id = rs.getInt("id"); String ename = rs.getString("ename"); int job_id = rs.getInt("job_id"); int mgr = rs.getInt("mgr"); Date joindate = rs.getDate("joindate"); double salary = rs.getDouble("salary"); double bonus = rs.getDouble("bonus"); int dept_id = rs.getInt("dept_id"); e=new emp(id,ename,job_id,mgr,joindate,salary,bonus,dept_id); list.add(e); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException throwables) { throwables.printStackTrace(); } finally { if(rs!=null){ try { rs.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } if(st!=null){ try { st.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } if(conn!=null){ try { conn.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } } return list; } }
-
結果
-
-
PreparedStatement:執行sql的對象
-
SQL注入問題:在拼接sql時,有一些sql的特殊關鍵字參與字符串的拼接。會造成安全性問題
- 輸入用戶隨便,輸入密碼:a’ or ‘a’ = 'a
- sql:select * from user where username = ‘fhdsjkf’ and password = ‘a’ or ‘a’ = ‘a’
-
解決sql注入問題:使用PreparedStatement對象來解決
-
預編譯的SQL:參數使用?作爲佔位符
-
步驟
-
導入驅動jar包 mysql-connector-java-5.1.37-bin.jar
-
註冊驅動
-
獲取數據庫連接對象 Connection
-
定義sql
注意:sql的參數使用?作爲佔位符。 如:
select * from user where username = ? and password = ?;
-
獲取執行sql語句的對象 PreparedStatement
Connection.prepareStatement(String sql)
-
給?賦值
方法:setXxx(參數1,參數2)
- 參數1:?的位置編號 從1 開始
- 參數2:?的值
-
執行sql,接受返回結果,不需要傳遞sql語句
-
處理結果
-
釋放資源
-
-
注意:後期都會使用PreparedStatement來完成增刪改查的所有操作
- 可以防止SQL注入
- 效率更高
-
抽取JDBC工具類:JDBCUtils
-
目的:簡化書寫
-
分析:
- 抽取註冊驅動
- 抽取一個方法獲取連接對象
- 需求:不想傳遞參數(麻煩),還得保證工具類的通用性。
- 解決:配置文件
- 抽取一個方法釋放資源
-
樣例,給練習1進化
我的文件樹
-
在src目錄下,新建一個名爲jdbc.properties的配置文件
driver=com.mysql.jdbc.Driver url=jdbc:mysql:///db3 user=root password=root
-
在當前包下再新建一個Utils包,再在這個包下新建一個JDBCUtils工具類
package demo_jdbc.Utils; import java.io.IOException; import java.io.InputStream; import java.sql.*; import java.util.Properties; public class JDBCUtils { private static String driver; private static String url; private static String user; private static String password; static{ //創建Properties對象 Properties pro=new Properties(); //用類加載器獲取jdbc.properties文件的輸入流 ClassLoader cl = JDBCUtils.class.getClassLoader(); InputStream is = cl.getResourceAsStream("jdbc.properties"); //獲得文件內容 try { pro.load(is); driver=pro.getProperty("driver"); url=pro.getProperty("url"); user=pro.getProperty("user"); password=pro.getProperty("password"); Class.forName(driver); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } //連接資源 public static Connection getConnection(){ Connection conn=null; try { conn=DriverManager.getConnection(url, user, password); } catch (SQLException throwables) { throwables.printStackTrace(); } return conn; } //釋放資源,這裏有可能沒有遊標,所以寫兩種情況的重載 public static void close(Connection conn, Statement st){ if(st!=null){ try { st.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } if(conn!=null){ try { conn.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } } public static void close(Connection conn, Statement st,ResultSet rs){ if(rs!=null){ try { rs.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } if(st!=null){ try { st.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } if(conn!=null){ try { conn.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } } }
-
在練習1的主類的基礎上寫一個新的主類
package demo_jdbc; import demo_jdbc.Utils.JDBCUtils; import java.sql.*; import java.util.ArrayList; import java.util.List; public class jdbc_demo2 { public static void main(String[] args) { List<emp> list=new jdbc_demo2().findall(); for (emp em : list) { System.out.println(em); } } public List<emp> findall(){ Connection conn =null; Statement st=null; ResultSet rs =null; List<emp> list=null; try { //註冊驅動,MySQL5.0之後可不寫 //獲得數據庫連接 conn = JDBCUtils.getConnection(); //定義sql語句 String sql="SELECT * FROM emp"; //創建一個sql執行對象 st = conn.createStatement(); //執行sql命令,返回結果集 rs = st.executeQuery(sql); //遊標獲取數據 list=new ArrayList<emp>(); emp e=null; while(rs.next()){ int id = rs.getInt("id"); String ename = rs.getString("ename"); int job_id = rs.getInt("job_id"); int mgr = rs.getInt("mgr"); Date joindate = rs.getDate("joindate"); double salary = rs.getDouble("salary"); double bonus = rs.getDouble("bonus"); int dept_id = rs.getInt("dept_id"); e=new emp(id,ename,job_id,mgr,joindate,salary,bonus,dept_id); list.add(e); } } catch (SQLException throwables) { throwables.printStackTrace(); } finally { JDBCUtils.close(conn,st,rs); } return list; } }
-
結果(與練習1一致)
-
-
練習2
需求
1. 通過鍵盤錄入用戶名和密碼
2. 判斷用戶是否登錄成功步驟
-
創建數據庫表USER
CREATE TABLE USER( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(32), PASSWORD VARCHAR(32) ); INSERT INTO USER VALUES(NULL,'zhangsan','123'); INSERT INTO USER VALUES(NULL,'lisi','234');
-
JDBCUtils和樣例一致,這裏寫一個新的主類
package demo_jdbc; import demo_jdbc.Utils.JDBCUtils; import java.sql.*; import java.util.Scanner; public class jdbc_demologin { public static void main(String[] args) { Scanner sc=new Scanner(System.in); System.out.println("請輸入用戶名"); String username=sc.nextLine(); System.out.println("請輸入密碼"); String password=sc.nextLine(); boolean login = new jdbc_demologin().login(username, password); if(login){ System.out.println("登陸成功"); }else{ System.out.println("用戶名或密碼錯誤"); } } public boolean login(String username,String password){ Connection conn=null; Statement st=null; ResultSet rs =null; if(username==null && password==null){ return false; } try { //獲得數據庫的連接 conn= JDBCUtils.getConnection(); //定義sql語句 String sql="SELECT * FROM USER WHERE username='"+username+"' AND PASSWORD='"+password+"'"; //創建sql執行對象 st = conn.createStatement(); rs = st.executeQuery(sql); return rs.next(); } catch (SQLException throwables) { throwables.printStackTrace(); }finally { JDBCUtils.close(conn,st,rs); } return false; } }
-
上述例子我使用了普通的執行對象Statement,會導致SQL注入的問題,如圖
-
執行對象改用prepareStatement,代碼如下
package demo_jdbc; import demo_jdbc.Utils.JDBCUtils; import java.sql.*; import java.util.Scanner; public class jdbc_demologin { public static void main(String[] args) { Scanner sc=new Scanner(System.in); System.out.println("請輸入用戶名"); String username=sc.nextLine(); System.out.println("請輸入密碼"); String password=sc.nextLine(); boolean login = new jdbc_demologin().login(username, password); if(login){ System.out.println("登陸成功"); }else{ System.out.println("用戶名或密碼錯誤"); } } public boolean login(String username,String password){ Connection conn=null; PreparedStatement pst=null; ResultSet rs =null; if(username==null && password==null){ return false; } try { //獲得數據庫的連接 conn= JDBCUtils.getConnection(); //定義sql語句 String sql="SELECT * FROM USER WHERE username=? AND PASSWORD=?"; //創建sql執行對象 pst = conn.prepareStatement(sql); pst.setString(1,username); pst.setString(2,password); rs = pst.executeQuery(); return rs.next(); } catch (SQLException throwables) { throwables.printStackTrace(); }finally { JDBCUtils.close(conn,pst,rs); } return false; } }
-
再次使用剛剛的輸入查看是否會有SQL注入的問題
-
JDBC控制事務
-
事務:一個包含多個步驟的業務操作。如果這個業務操作被事務管理,則這多個步驟要麼同時成功,要麼同時失敗。
-
操作
- 開啓事務
- 提交事務
- 回滾事務
-
使用Connection對象來管理事務
- 開啓事務:setAutoCommit(boolean autoCommit) :調用該方法設置參數爲false,即開啓事務
- 在執行sql之前開啓事務
- 提交事務:commit()
- 當所有sql都執行完提交事務
- 回滾事務:rollback()
- 在catch中回滾事務
- 開啓事務:setAutoCommit(boolean autoCommit) :調用該方法設置參數爲false,即開啓事務
-
樣例
我的數據庫定義
代碼
package demo_jdbc;
import demo_jdbc.Utils.JDBCUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class jdbc_demoshiwu {
public static void main(String[] args) {
PreparedStatement pst1 = null;
PreparedStatement pst2 = null;
Connection conn= null;
try {
conn= JDBCUtils.getConnection();
conn.setAutoCommit(false);
String sql="UPDATE account SET balance=balance - ? WHERE id=?";
String sql2="UPDATE account SET balance=balance + ? WHERE id=?";
pst1 = conn.prepareStatement(sql);
pst2 = conn.prepareStatement(sql2);
pst1.setDouble(1,500);
pst1.setInt(2,1);
pst2.setDouble(1,500);
pst2.setInt(2,2);
pst1.executeUpdate();
//手動製造異常
int i=1/0;
pst2.executeUpdate();
conn.commit();
} catch (Exception e) {
if(conn!=null){
try {
conn.rollback();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
e.printStackTrace();
}finally {
JDBCUtils.close(conn,pst1);
JDBCUtils.close(null,pst2);
}
}
}