SQL語句主要分為五種:
1. 查詢語句:主要由select關鍵字完成,查詢語句是SQL語句中最復雜、功能最豐富的語句;
2. DML(Data Manipulation Language,數據操作語言):包含insert、update、delete
3. DDL(Data Definition Language, 數據定義語言):包含create、 alter、drop、truncate
4. DCL(Data Control Language,數據控制語言):包含grant 、revoke
5. 事務控制語句: commit、rollback、savepoint
JDBC編程步驟:
1. 加載數據庫驅動。通過Class類的forName()靜態方法來加載驅動
Class.forName(driverClass)
2. 通過DriverManager獲取數據庫連接Connection對象。
DriverManager.getConnection(String url,String user,String pass)
3. 通過Connection對象創建Statement對象。創建Statement有三種方法。
- createStatement(): 創建基本的Statement對象
- prepareStatement(String sql): 根據傳入的SQL語句創建預編譯的Statement對象
- prepareCall(String sql): 根據傳入的SQL語句創建CallableStatement對象。
4. 使用Statement執行SQL語句。
execute(): 可以執行任何SQL語句,但比較麻煩,在不清SQL語句類型時,只能使用這個方法。
executeUpdate():主要用於執行DML和DDL語句。執行DML語句返回受SQL語句影響的行數,執行DDL語句返回0。
executeQuery():只能執行查詢語句,執行后返回代表查詢結果的ResultSet對象。
5. 操作結果集。有兩類方法來操作結果集:
- next()、 previous()、 first()、 last()、 beforeFirst()、 afterLast()、 absolute()
- getXxx()方法獲取記錄指針指向行、特定列的值。
6. 回收數據庫資源,包括ResultSet、Statement和Connection等資源。
例子:
import java.sql.*; public class ConnMySql { public static void main(String[] args) throws Exception { // 1.加載驅動,使用反射的知識,現在記住這么寫。 Class.forName("com.mysql.jdbc.Driver"); try( // 2.使用DriverManager獲取數據庫連接, // 其中返回的Connection就代表了Java程序和數據庫的連接 // 不同數據庫的URL寫法需要查驅動文檔知道,用戶名、密碼由DBA分配 Connection conn = DriverManager.getConnection( "jdbc:mysql://127.0.0.1:3306/select_test" , "root" , "32147"); // 3.使用Connection來創建一個Statment對象 Statement stmt = conn.createStatement(); // 4.執行SQL語句 /* Statement有三種執行sql語句的方法: 1 execute 可執行任何SQL語句。- 返回一個boolean值, 如果執行后第一個結果是ResultSet,則返回true,否則返回false 2 executeQuery 執行Select語句 - 返回查詢到的結果集 3 executeUpdate 用於執行DML語句。- 返回一個整數, 代表被SQL語句影響的記錄條數 */ ResultSet rs = stmt.executeQuery("select s.* , teacher_name" + " from student_table s , teacher_table t" + " where t.teacher_id = s.java_teacher"))//在try語句中,執行完后會自動關閉。 { // ResultSet有系列的getXxx(列索引 | 列名),用於獲取記錄指針 // 指向行、特定列的值,不斷地使用next()將記錄指針下移一行, // 如果移動之后記錄指針依然指向有效行,則next()方法返回true。 while(rs.next()) { System.out.println(rs.getInt(1) + "\t" + rs.getString(2) + "\t" + rs.getString(3) + "\t" + rs.getString(4)); } } } }
執行DDL語句的例子:
import java.util.*; import java.io.*; import java.sql.*; public class ExecuteDDL { private String driver; private String url; private String user; private String pass; public void initParam(String paramFile) throws Exception { // 使用Properties類來加載屬性文件 Properties props = new Properties(); props.load(new FileInputStream(paramFile)); driver = props.getProperty("driver"); url = props.getProperty("url"); user = props.getProperty("user"); pass = props.getProperty("pass"); } public void createTable(String sql)throws Exception //注意這個返回值類型 { // 加載驅動 Class.forName(driver); try( // 獲取數據庫連接 Connection conn = DriverManager.getConnection(url , user , pass); // 使用Connection來創建一個Statment對象 Statement stmt = conn.createStatement()) { // 執行DDL,創建數據表 stmt.executeUpdate(sql); } } public static void main(String[] args) throws Exception { ExecuteDDL ed = new ExecuteDDL(); ed.initParam("mysql.ini"); ed.createTable("create table jdbc_test " + "( jdbc_id int auto_increment primary key, " + "jdbc_name varchar(255), " + "jdbc_desc text);"); System.out.println("-----建表成功-----"); } }
執行DML語句的例子 :
import java.util.*; import java.io.*; import java.sql.*; public class ExecuteDML { private String driver; private String url; private String user; private String pass; public void initParam(String paramFile) throws Exception { // 使用Properties類來加載屬性文件 Properties props = new Properties(); props.load(new FileInputStream(paramFile)); driver = props.getProperty("driver"); url = props.getProperty("url"); user = props.getProperty("user"); pass = props.getProperty("pass"); } public int insertData(String sql)throws Exception//注意這里的返回值類型 { // 加載驅動 Class.forName(driver); try( // 獲取數據庫連接 Connection conn = DriverManager.getConnection(url , user , pass); // 使用Connection來創建一個Statment對象 Statement stmt = conn.createStatement()) { // 執行DML,返回受影響的記錄條數 return stmt.executeUpdate(sql); } } public static void main(String[] args)throws Exception { ExecuteDML ed = new ExecuteDML(); ed.initParam("mysql.ini"); int result = ed.insertData("insert into jdbc_test(jdbc_name,jdbc_desc)" + "select s.student_name , t.teacher_name " + "from student_table s , teacher_table t " + "where s.java_teacher = t.teacher_id;"); System.out.println("--系統中共有" + result + "條記錄受影響--"); } }
當不清楚SQL語句類型時的例子:
import java.util.*; import java.io.*; import java.sql.*; public class ExecuteSQL { private String driver; private String url; private String user; private String pass; public void initParam(String paramFile)throws Exception { // 使用Properties類來加載屬性文件 Properties props = new Properties(); props.load(new FileInputStream(paramFile)); driver = props.getProperty("driver"); url = props.getProperty("url"); user = props.getProperty("user"); pass = props.getProperty("pass"); } public void executeSql(String sql)throws Exception { // 加載驅動 Class.forName(driver); try( // 獲取數據庫連接 Connection conn = DriverManager.getConnection(url , user , pass); // 使用Connection來創建一個Statement對象 Statement stmt = conn.createStatement()) { // 執行SQL,返回boolean值表示是否包含ResultSet boolean hasResultSet = stmt.execute(sql); // 如果執行后有ResultSet結果集 if (hasResultSet) { try( // 獲取結果集 ResultSet rs = stmt.getResultSet()) { // ResultSetMetaData是用於分析結果集的元數據接口 ResultSetMetaData rsmd = rs.getMetaData(); int columnCount = rsmd.getColumnCount(); // 迭代輸出ResultSet對象 while (rs.next()) { // 依次輸出每列的值 for (int i = 0 ; i < columnCount ; i++ ) { System.out.print(rs.getString(i + 1) + "\t"); } System.out.print("\n"); } } } else { System.out.println("該SQL語句影響的記錄有" + stmt.getUpdateCount() + "條"); } } } public static void main(String[] args) throws Exception { ExecuteSQL es = new ExecuteSQL(); es.initParam("mysql.ini"); System.out.println("------執行刪除表的DDL語句-----"); es.executeSql("drop table if exists my_test"); System.out.println("------執行建表的DDL語句-----"); es.executeSql("create table my_test" + "(test_id int auto_increment primary key, " + "test_name varchar(255))"); System.out.println("------執行插入數據的DML語句-----"); es.executeSql("insert into my_test(test_name) " + "select student_name from student_table"); System.out.println("------執行查詢數據的查詢語句-----"); es.executeSql("select * from my_test"); } }
使用PreparedStatement執行SQL語句的例子(使用這種方法,可以防止SQL注入,比如在登錄框中輸入‘or true or’)
import java.util.*; import java.io.*; import java.sql.*; public class PreparedStatementTest { private String driver; private String url; private String user; private String pass; public void initParam(String paramFile)throws Exception { // 使用Properties類來加載屬性文件 Properties props = new Properties(); props.load(new FileInputStream(paramFile)); driver = props.getProperty("driver"); url = props.getProperty("url"); user = props.getProperty("user"); pass = props.getProperty("pass"); // 加載驅動 Class.forName(driver); } public void insertUseStatement()throws Exception { long start = System.currentTimeMillis(); try( // 獲取數據庫連接 Connection conn = DriverManager.getConnection(url , user , pass); // 使用Connection來創建一個Statment對象 Statement stmt = conn.createStatement()) { // 需要使用100條SQL語句來插入100條記錄 for (int i = 0; i < 100 ; i++ ) { stmt.executeUpdate("insert into student_table values(" + " null ,'姓名" + i + "' , 1)"); } System.out.println("使用Statement費時:" + (System.currentTimeMillis() - start)); } } public void insertUsePrepare()throws Exception { long start = System.currentTimeMillis(); try( // 獲取數據庫連接 Connection conn = DriverManager.getConnection(url , user , pass); // 使用Connection來創建一個PreparedStatement對象 PreparedStatement pstmt = conn.prepareStatement( "insert into student_table values(null,?,1)")) { // 100次為PreparedStatement的參數設值,就可以插入100條記錄 for (int i = 0; i < 100 ; i++ ) { pstmt.setString(1 , "姓名" + i); pstmt.executeUpdate(); } System.out.println("使用PreparedStatement費時:" + (System.currentTimeMillis() - start)); } } public static void main(String[] args) throws Exception { PreparedStatementTest pt = new PreparedStatementTest(); pt.initParam("mysql.ini"); pt.insertUseStatement(); pt.insertUsePrepare(); } }
使用CallableStatement調用存儲過程的例子
import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.util.*; import java.io.*; import java.sql.*; public class CallableStatementTest { private String driver; private String url; private String user; private String pass; public void initParam(String paramFile)throws Exception { // 使用Properties類來加載屬性文件 Properties props = new Properties(); props.load(new FileInputStream(paramFile)); driver = props.getProperty("driver"); url = props.getProperty("url"); user = props.getProperty("user"); pass = props.getProperty("pass"); } public void callProcedure()throws Exception { // 加載驅動 Class.forName(driver); try( // 獲取數據庫連接 Connection conn = DriverManager.getConnection(url , user , pass); // 使用Connection來創建一個CallableStatment對象 CallableStatement cstmt = conn.prepareCall( "{call add_pro(?,?,?)}")) { cstmt.setInt(1, 4); cstmt.setInt(2, 5); // 注冊CallableStatement的第三個參數是int類型 cstmt.registerOutParameter(3, Types.INTEGER); // 執行存儲過程 cstmt.execute(); // 獲取,並輸出存儲過程傳出參數的值。 System.out.println("執行結果是: " + cstmt.getInt(3)); } } public static void main(String[] args) throws Exception { CallableStatementTest ct = new CallableStatementTest(); ct.initParam("mysql.ini"); ct.callProcedure(); } }
管理結果集:
可滾動、可更新的結果集
需要在創建Statement或者PrepareStatement時傳入額外的參數,如下面例子:
import java.util.*; import java.io.*; import java.sql.*; public class ResultSetTest { private String driver; private String url; private String user; private String pass; public void initParam(String paramFile)throws Exception { // 使用Properties類來加載屬性文件 Properties props = new Properties(); props.load(new FileInputStream(paramFile)); driver = props.getProperty("driver"); url = props.getProperty("url"); user = props.getProperty("user"); pass = props.getProperty("pass"); } public void query(String sql)throws Exception { // 加載驅動 Class.forName(driver); try( // 獲取數據庫連接 Connection conn = DriverManager.getConnection(url , user , pass); // 使用Connection來創建一個PreparedStatement對象 // 傳入控制結果集可滾動,可更新的參數。 PreparedStatement pstmt = conn.prepareStatement(sql , ResultSet.TYPE_SCROLL_INSENSITIVE , ResultSet.CONCUR_UPDATABLE); ResultSet rs = pstmt.executeQuery()) { rs.last(); int rowCount = rs.getRow(); for (int i = rowCount; i > 0 ; i-- ) { rs.absolute(i); System.out.println(rs.getString(1) + "\t" + rs.getString(2) + "\t" + rs.getString(3)); // 修改記錄指針所有記錄、第2列的值 rs.updateString(2 , "學生名" + i); // 提交修改 rs.updateRow(); } } } public static void main(String[] args) throws Exception { ResultSetTest rt = new ResultSetTest(); rt.initParam("mysql.ini"); rt.query("select * from student_table"); } }
處理Blob類型數據
Blob(Binary Long object)是二進制長對象的意思。使用該類可以吧圖片、聲音等文件的二進制數據保存在數據庫中,並可以從數據庫中恢復指定文件。
通過PrepareStatement的setBinaryStream(int parameterIndex,InputStream x),該方法可以指定參數傳入二進制流。
如果需要從ResultSet中取出Blob數據,可以調用ResultSet的getBlob(int columnIndex)來返回一個Blob對象,然后再通過Blob對象的getBinaryStream()方法獲取該Blob數據的輸入流,也可以使用Blob對象的getBytes()方法直接讀取該Blob對象封裝的二進制數據。
使用ResultSetMetaData分析結果集:
該對象可以通過ResultSet的getMetaData()方法來放回。常用的方法有:
- int getColumnCount():返回該ResultSet的列數量
- String getColumnName(int column):返回指定索引的列名
- int getColumnType(int column):返回指定索引的列類型
事務處理
事務是由一步或幾步數據庫操作序列組成的邏輯執行單元,這系列操作要么全部執行,要么全部放棄執行。
事務有四個特性:原子性、一致性、隔離性、持續性。
數據庫事務由下列語句組成。
一組DML語句,一條DDL語句,一條DCL語句。DDL和DCL語句最多只能有一條,因為DDL和DCL語句都會導致事務立即提交。
事務提交方式(沒有提交前事務中的語句不會生效)
1.顯式提交:使用commit
2.自動提交:執行DDL或DCL語句,或者程序正常退出。
事務回滾方式:(沒有生效的事務可以回滾)
1.顯式回滾:使用rollback
2.自動回滾:系統錯誤或者強行退出
自動提交和開始事務是對立的,開始事務功能后,就會將自動提交的功能關閉。正常情況下需要手動關閉自動提交。
JDBC對事務的支持
可以調用Connection的setAutoCommit()方法來關閉自動提交,開啟事務。getAutoCommit()返回自動提交模式。commit()提交事務。rollback()回滾事務。
例子:
import java.sql.*; import java.io.*; import java.util.*; public class TransactionTest { private String driver; private String url; private String user; private String pass; public void initParam(String paramFile)throws Exception { // 使用Properties類來加載屬性文件 Properties props = new Properties(); props.load(new FileInputStream(paramFile)); driver = props.getProperty("driver"); url = props.getProperty("url"); user = props.getProperty("user"); pass = props.getProperty("pass"); } public void insertInTransaction(String[] sqls) throws Exception { // 加載驅動 Class.forName(driver); try( Connection conn = DriverManager.getConnection(url , user , pass)) { // 關閉自動提交,開啟事務 conn.setAutoCommit(false); try( // 使用Connection來創建一個Statment對象 Statement stmt = conn.createStatement()) { // 循環多次執行SQL語句 for (String sql : sqls) { stmt.executeUpdate(sql); } } // 提交事務 conn.commit(); } } public static void main(String[] args) throws Exception { TransactionTest tt = new TransactionTest(); tt.initParam("mysql.ini"); String[] sqls = new String[]{ "insert into student_table values(null , 'aaa' ,1)", "insert into student_table values(null , 'bbb' ,1)", "insert into student_table values(null , 'ccc' ,1)", // 下面這條SQL語句將會違反外鍵約束, // 因為teacher_table中沒有ID為5的記錄。 "insert into student_table values(null , 'ccc' ,5)" //① }; tt.insertInTransaction(sqls); } }
使用DatabaseMetaData分析數據庫信息
通過Connection提供的getMetaData()方法獲取DatabaseMetaData對象。許多DatabaseMetaData方法是以ResultSet對象的形式返回查詢信息,然后使用ResultSet中的常規方法獲取內容。如果沒有內容就返回一個空的ResultSet對象。
DatabaseMetaData的許多方法都需要傳入一個xxxPattern模式字符串,這里的xxxPattern不是正則表達式,而是SQL里的模式字符串。
即用百分號(%)代表任意多個字符,使用下划線(_)代表一個字符。通常情況下,如果把該模式字符串的參數值設置為null,即表明該參數不過為過濾條件。
例子:
import java.sql.*; import java.util.*; import java.io.*; public class DatabaseMetaDataTest { private String driver; private String url; private String user; private String pass; public void initParam(String paramFile)throws Exception { // 使用Properties類來加載屬性文件 Properties props = new Properties(); props.load(new FileInputStream(paramFile)); driver = props.getProperty("driver"); url = props.getProperty("url"); user = props.getProperty("user"); pass = props.getProperty("pass"); } public void info() throws Exception { // 加載驅動 Class.forName(driver); try( // 獲取數據庫連接 Connection conn = DriverManager.getConnection(url , user , pass)) { // 獲取的DatabaseMetaData對象 DatabaseMetaData dbmd = conn.getMetaData(); // 獲取MySQL支持的所有表類型 ResultSet rs = dbmd.getTableTypes(); System.out.println("--MySQL支持的表類型信息--"); printResultSet(rs); // 獲取當前數據庫的全部數據表 rs = dbmd.getTables(null,null, "%" , new String[]{"TABLE"}); System.out.println("--當前數據庫里的數據表信息--"); printResultSet(rs); // 獲取student_table表的主鍵 rs = dbmd.getPrimaryKeys(null , null, "student_table"); System.out.println("--student_table表的主鍵信息--"); printResultSet(rs); // 獲取當前數據庫的全部存儲過程 rs = dbmd.getProcedures(null , null, "%"); System.out.println("--當前數據庫里的存儲過程信息--"); printResultSet(rs); // 獲取teacher_table表和student_table之間的外鍵約束 rs = dbmd.getCrossReference(null,null, "teacher_table" , null, null, "student_table"); System.out.println("--teacher_table表和student_table之間" + "的外鍵約束--"); printResultSet(rs); // 獲取student_table表的全部數據列 rs = dbmd.getColumns(null, null, "student_table", "%"); System.out.println("--student_table表的全部數據列--"); printResultSet(rs); } } public void printResultSet(ResultSet rs)throws SQLException { ResultSetMetaData rsmd = rs.getMetaData(); // 打印ResultSet的所有列標題 for (int i = 0 ; i < rsmd.getColumnCount() ; i++ ) { System.out.print(rsmd.getColumnName(i + 1) + "\t"); } System.out.print("\n"); // 打印ResultSet里的全部數據 while (rs.next()) { for (int i = 0; i < rsmd.getColumnCount() ; i++ ) { System.out.print(rs.getString(i + 1) + "\t"); } System.out.print("\n"); } rs.close(); } public static void main(String[] args) throws Exception { DatabaseMetaDataTest dt = new DatabaseMetaDataTest(); dt.initParam("mysql.ini"); dt.info(); } }
