Java操作數據庫——使用JDBC連接數據庫
摘要:本文主要學習了如何使用JDBC連接數據庫。
背景
數據持久化
數據持久化就是把數據保存到可掉電式存儲設備中以供之后使用。大多數情況下,特別是企業級應用,數據持久化意味着將內存中的數據保存到硬盤上加以“固化”,而持久化的實現過程大多通過各種關系數據庫來完成。
持久化的主要應用是將內存中的數據存儲在關系型數據庫中,當然也可以存儲在磁盤文件、XML數據文件中。
基礎
概念
JDBC的全稱是Java Database Connectivity,意為Java和數據庫的連接。
JDBC是SUN公司提供的一套操作數據庫的接口規范,定義了用來訪問數據庫的標准Java類庫,使用這個類庫可以更加方便地訪問數據庫資源。
程序員在使用數據庫的時候,需要安裝數據庫驅動,不同的數據庫的驅動也是不同的。所以為了程序員開發方便,SUN公司提供了一套接口,讓數據庫廠商實現這些接口,程序員只需要使用這個接口就可以操作不同的數據庫,不需要關注底層數據庫驅動的安裝,從而大大簡化和加快了開發過程。
架構
JDBC接口包括兩個層次:
JDBC API:即面向應用的API,是一個抽象的接口,供應用程序開發人員使用,提供了程序到JDBC管理器的連接。
JDBC Driver API:即面向數據庫驅動的API,需要開發商去實現這個接口,提供了JDBC管理器到數據庫驅動程序的連接。
規范
主要有四個核心對象:
DriverManager類(java.sql.DriverManager):用於注冊驅動,創建連接對象。
Connection接口(java.sql.Connection):表示與數據庫創建的連接。
Statement接口(java.sql.Statement):執行數據庫SQL語句,並返回相應結果的對象。
ResultSet接口(java.sql.ResultSet):結果集或一張虛擬表,用於存儲表數據的對象。
其中,Statement接口還有兩個子接口:
PreparedStatement接口(java.sql.PreparedStatement):預編譯對象,是Statement接口的子接口,用於解決sql的注入問題。
CallableStatement接口(java.sql.CallableStatement):支持帶參數的SQL操作,支持調用存儲過程,是PreparedStatement接口的子接口。
連接數據庫
下面的說明以連接MySQL數據庫為例。
准備工作
擁有一個可以正常訪問的MySQL數據庫,已經可以登錄使用的用戶名和密碼。
建立一個准備連接數據庫的項目。
導入jar包
導入Java連接MySQL所用到的jar包,這個jar包通常是由數據庫的廠商提供的,這里下載的是 mysql-connector-java-5.1.32.jar 這個包。
那Eclipse為例,在項目上右鍵,然后點擊Properties:
然后在彈出的頁面左側找到 Java Build Path 目錄並進入,選擇 Libraries 標簽頁,找到標簽頁右側的 Add External JARs... 按鈕:
找到下載的jar包,點擊打開:
可以看到jar包被成功導入到項目中:
點擊OK完成導入。
加載驅動
加載數據庫驅動的方法是調用Class類的靜態方法forName。語法格式如下:
1 public static Class<?> forName(String className)
其中,傳入的參數className是每個數據庫廠商各自提供的一個驅動程序名稱。
不同的數據庫,驅動程序名稱如下:
1 MySQL驅動:com.mysql.jdbc.Drive 2 Oracle驅動:oracle.jdbc.driver.OracleDriver 3 SQLServer驅動:com.microsoft.sqlserver.jdbc.SQLServerDriver 4 PostgreSQL驅動:org.postgresql.Driver 5 DB2驅動:com.ibm.db2.jdbc.net.DB2Driver 6 Sybase驅動:com.sybase.jdbc.SybDriver
建立連接
使用DriverManager類的靜態方法getConnection建立到指定數據庫的連接。語法格式如下:
1 public static Connection getConnection(String url, String user, String password)
url是SUN公司與數據庫廠商之間的一種協議,user是連接數據庫的用戶名,password是用戶名對應的密碼。
不同的數據庫,url協議的格式如下:
1 MySQL格式:jdbc:mysql://地址或主機名:端口號/數據庫名 2 Oracle格式:jdbc:oracle:thin:@地址或主機名:端口號:數據庫名 3 SQLServer格式:jdbc:sqlserver://地址或主機名:端口號;databaseName=數據庫名 4 PostgreSQL格式:jdbc:postgresql://地址或主機名:端口號/數據庫名 5 DB2格式:jdbc:db2:地址或主機名:端口號/數據庫名 6 Sybase格式:jdbc:sybase:Tds:地址或主機名:端口號/數據庫名
如果是在本機並且用的是默認的端口號,可以將地址和端口號省略:
1 jdbc:mysql:///數據庫名
建議url中的文件編碼、數據庫連接編碼、數據庫編碼保持一致,向數據庫中添加數據時,連接參數最好包含Unicode字符支持,這樣添加的字符就能被數據庫識別並且正常顯示了:
1 jdbc:mysql://地址或主機名:端口號/數據庫名?useUnicode=true&characterEncoding=UTF-8
至此,就成功獲取到了連接MySQL數據庫的Connection對象。
執行語句
獲取到連接之后,使用Connection接口的createStatement方法獲取Statement對象。語法格式如下:
1 Statement createStatement()
獲取到了Statement對象之后,使用executeQuery方法執行查詢語句得到ResultSet類型的結果集,使用executeUpdate方法執行增加、刪除、修改語句得到int類型的記錄數。
釋放連接
數據庫連接Connection是非常稀有的資源,用完后必須馬上釋放,如果Connection不能及時正確的關閉將導致系統宕機。Connection的使用原則是盡量晚創建,盡量早的釋放。
釋放的方法是通過調用Connection的close方法,語法格式如下:
1 void close()
一次完整的操作數據庫的流程包括加載驅動、建立連接、執行語句、釋放連接。其中,加載驅動只執行一次即可,建立的連接用完之后必須馬上釋放。
連接數據庫實例
在程序里配置連接信息
完整代碼如下:
1 public static void main(String[] args) { 2 try { 3 Class.forName("com.mysql.jdbc.Driver"); 4 } catch (ClassNotFoundException e) { 5 e.printStackTrace(); 6 } 7 Connection conn = null; 8 try { 9 String url = "jdbc:mysql://192.168.35.128:3306/demo"; 10 String user = "root"; 11 String password = "123456"; 12 conn = DriverManager.getConnection(url, user, password); 13 } catch (Exception e) { 14 e.printStackTrace(); 15 } finally { 16 try { 17 conn.close(); 18 } catch (SQLException e) { 19 e.printStackTrace(); 20 } 21 } 22 }
在配置文件里配置連接信息
配置文件 jdbc.properties 在 src 目錄下,和當前的源文件的根目錄 package jdbc; 平級,內容如下:
1 driverClass=com.mysql.jdbc.Driver 2 url="jdbc:mysql://192.168.35.128:3306/demo" 3 user=root 4 password=123456
完整代碼如下:
1 public static void main(String[] args) { 2 Properties pros = new Properties(); 3 try { 4 pros.load(TestConnection.class.getClassLoader().getResourceAsStream("jdbc.properties")); 5 } catch (IOException e) { 6 e.printStackTrace(); 7 } 8 try { 9 Class.forName(pros.getProperty("driverClass")); 10 } catch (ClassNotFoundException e) { 11 e.printStackTrace(); 12 } 13 Connection conn = null; 14 try { 15 String url = pros.getProperty("url"); 16 String user = pros.getProperty("user"); 17 String password = pros.getProperty("password"); 18 conn = DriverManager.getConnection(url, user, password); 19 } catch (Exception e) { 20 e.printStackTrace(); 21 } finally { 22 try { 23 conn.close(); 24 } catch (SQLException e) { 25 e.printStackTrace(); 26 } 27 } 28 }
增刪改查
查詢
使用實例:
1 public static void main(String[] args) { 2 try { 3 Class.forName("com.mysql.jdbc.Driver"); 4 } catch (ClassNotFoundException e) { 5 e.printStackTrace(); 6 } 7 Connection conn = null; 8 Statement stmt = null; 9 ResultSet rs = null; 10 try { 11 String url = "jdbc:mysql://192.168.35.128:3306/demo"; 12 String user = "root"; 13 String password = "123456"; 14 conn = DriverManager.getConnection(url, user, password); 15 stmt = conn.createStatement(); 16 rs = stmt.executeQuery("select * from student"); 17 while (rs.next()) { 18 System.out.println(rs.getInt("id") + "\t" + rs.getString(2) + "\t" + rs.getString(3) + "\t" + rs.getString("address")); 19 } 20 } catch (Exception e) { 21 e.printStackTrace(); 22 } finally { 23 try { 24 rs.close(); 25 } catch (SQLException e) { 26 e.printStackTrace(); 27 } 28 try { 29 stmt.close(); 30 } catch (SQLException e) { 31 e.printStackTrace(); 32 } 33 try { 34 conn.close(); 35 } catch (SQLException e) { 36 e.printStackTrace(); 37 } 38 } 39 }
增加、修改、刪除
使用實例:
1 public static void main(String[] args) { 2 try { 3 Class.forName("com.mysql.jdbc.Driver"); 4 } catch (ClassNotFoundException e) { 5 e.printStackTrace(); 6 } 7 Connection conn = null; 8 Statement stmt = null; 9 try { 10 String url = "jdbc:mysql://192.168.35.128:3306/demo"; 11 String user = "root"; 12 String password = "123456"; 13 conn = DriverManager.getConnection(url, user, password); 14 stmt = conn.createStatement(); 15 int count = stmt.executeUpdate("delete from student where id = 903"); 16 System.out.println("受影響的行數:" + count); 17 } catch (Exception e) { 18 e.printStackTrace(); 19 } finally { 20 try { 21 stmt.close(); 22 } catch (SQLException e) { 23 e.printStackTrace(); 24 } 25 try { 26 conn.close(); 27 } catch (SQLException e) { 28 e.printStackTrace(); 29 } 30 } 31 }
Statement接口
Statement接口是通過Connection的createStatement方法獲取的,用來執行SQL語句並返回相應的結果。語法格式如下:
1 Statement createStatement()
使用完之后需要手動關閉Statement對象。
PreparedStatement接口
PreparedStatement接口是通過Connection的preparedStatement方法獲取的,是Statement的子接口,表示一條預編譯過的SQL語句。語法格式如下:
1 PreparedStatement prepareStatement(String sql)
傳入的SQL語句中的參數用問號“?”來表示,調用PreparedStatement對象的setXxx方法來設置這些參數。setXxx方法有兩個參數,第一個參數是要設置的參數索引(從1開始),第二個是設置的參數值。
使用Statement可能會因為字符串拼接導致被人SQL注入攻擊,但使用PreparedStatement不需要拼接字符串,而是使用占位符的方法,有效避免了SQL注入攻擊的問題。
使用舉例:
1 pstmt = conn.prepareStatement("select * from student where id = ?"); 2 pstmt.setInt(1, 904); 3 rs = pstmt.executeQuery(); 4 while (rs.next()) { 5 System.out.println(rs.getString(1) + "\t" + rs.getString(2) + "\t" + rs.getString(3) + "\t" + rs.getString("address")); 6 }
ResultSet接口
使用Statement接口的executeQuery方法執行傳入的查詢語句,並得到ResultSet類型的結果集。語法格式如下:
1 ResultSet executeQuery(String sql)
ResultSet接口由數據庫廠商實現,以邏輯表格的形式封裝了執行數據庫操作的結果集。
ResultSet對象維護了一個指向當前數據行的游標,游標默認從1開始,可以通過next方法移動到下一行。
使用完之后需要手動關閉ResultSet對象。
常用方法:
1 byte getByte(int columnIndex) throws SQLException; 2 byte getByte(String columnLabel) throws SQLException; 3 byte[] getBytes(int columnIndex) throws SQLException; 4 byte[] getBytes(String columnLabel) throws SQLException; 5 short getShort(int columnIndex) throws SQLException; 6 short getShort(String columnLabel) throws SQLException; 7 int getInt(int columnIndex) throws SQLException; 8 int getInt(String columnLabel) throws SQLException; 9 long getLong(int columnIndex) throws SQLException; 10 long getLong(String columnLabel) throws SQLException; 11 float getFloat(int columnIndex) throws SQLException; 12 float getFloat(String columnLabel) throws SQLException; 13 double getDouble(int columnIndex) throws SQLException; 14 double getDouble(String columnLabel) throws SQLException; 15 String getString(int columnIndex) throws SQLException; 16 String getString(String columnLabel) throws SQLException; 17 boolean getBoolean(int columnIndex) throws SQLException; 18 boolean getBoolean(String columnLabel) throws SQLException; 19 java.sql.Date getDate(int columnIndex) throws SQLException; 20 java.sql.Date getDate(String columnLabel) throws SQLException; 21 java.sql.Time getTime(int columnIndex) throws SQLException; 22 java.sql.Time getTime(String columnLabel) throws SQLException; 23 java.sql.Timestamp getTimestamp(int columnIndex) throws SQLException; 24 java.sql.Timestamp getTimestamp(String columnLabel) throws SQLException; 25 Object getObject(int columnIndex) throws SQLException; 26 Object getObject(String columnLabel) throws SQLException; 27 28 boolean next() throws SQLException; 29 void close() throws SQLException; 30 31 ResultSetMetaData getMetaData() throws SQLException;
使用舉例:
1 stmt = conn.createStatement(); 2 rs = stmt.executeQuery("select * from student"); 3 while (rs.next()) { 4 System.out.println(rs.getInt("id") + "\t" + rs.getString(2) + "\t" + rs.getString(3) + "\t" + rs.getString("address")); 5 }
ResultSetMetaData接口
ResultSetMetaData對象可以用來獲取ResultSet對象中列的類型和屬性信息。
常用方法:
1 int getColumnCount() throws SQLException; 2 int getColumnTypeName(int column) throws SQLException; 3 String getColumnLabel(int column) throws SQLException; 4 String getColumnName(int column) throws SQLException;
使用舉例:
1 stmt = conn.createStatement(); 2 rs = stmt.executeQuery("select * from student"); 3 while (rs.next()) { 4 ResultSetMetaData rsmd = rs.getMetaData(); 5 System.out.println(rsmd.getColumnName(1) + "=" + rs.getInt("id") + "\t" + rsmd.getColumnName(2) + "=" + rs.getString(2)); 6 }