【Java】14 JDBC編程學習總結

一、JDBC是什麼?

JDBC-Java Database Connectivity-Java數據庫連接

二、爲什麼要使用JDBC?

可以使用相同的API實現連接、操作不同的數據庫

三、DDL、DML、DCL分別代表什麼

DDL(Data Definition Language 數據定義語言)語句:主要由create,alter,drop和truncate四個關鍵字完成
DML(Data Manipulation Language 數據操作語言)語句:主要由insert,update和delete三個關鍵字完成
DCL(Data Control Language 數據控制語言) 語句:主要由grant和revoke兩個關鍵字組成
事務性語言:主要由commit,rollback和savepoint三個關鍵字組成

四、JDBC有三個操作步驟

建立與數據庫的連接、執行SQL語句、獲得SQL語句執行結果

  1. 加載驅動,規範
    Class.forName(dirver)
  2. 使用Connection創建Connection對象,以便可以使用Connection創建Statement對象
    Connection conn = DriverManager.getConnection(url,user,pass)
  3. 使用Connection對象創建Statement對象,以便使用Statement對象的executeXxx方法
    Statement stmt = conn.createStatement()
  4. 調用Statement對象的executeUpdate()、executeQuery()、execute()方法

executeUpdate():用於執行DML語句,返回一個整數

實例代碼
/** 
 * @ClassName: ExecutionDDLTest
 * @description: 本節代碼主要實現使用executeLargeUpdate()來執行DDL語句
 * DDL語言:主要由create,drop,alter,truncate
 * @author: FFIDEAL
 * @Date: 2020年4月5日 下午9:28:59
 */  

package M13;

import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.util.Properties;

import com.mysql.cj.jdbc.Driver;

public class ExecutionDDLTest {
	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來創建一個Statement對象
				Statement stmt = conn.createStatement()
				){
			//執行DDL語句,創建數據表
			stmt.executeUpdate(sql);
		}
	}
	public static void main(String[] args) throws Exception{
		ExecutionDDLTest ed = new ExecutionDDLTest();
		ed.initParam("E:\\JavaCode\\Code\\src\\M13\\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("=======表創建完成======");
	}
}

executeQuery():執行select語句,查詢到的結果

實例代碼
/** 
 * @ClassName: ConnMySql
 * @description: 本節代碼簡單示範了JDBC編程,並通過ResultSet獲得結果集的過程
 * @author: FFIDEAL
 * @Date: 2020年4月4日 下午8:22:06
 */

package M13;

import java.security.interfaces.RSAKey;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class ConnMySql {
	public static void main(String[] args) throws Exception{
		
		//1.加載驅動,使用反射知識,現在就記住
		/* 5版本是Class.forName("com.mysql.jdbc.Driver");
		 * 8版本是Class.forName("com.mysql.cj.jdbc.Driver");
		 */
		Class.forName("com.mysql.cj.jdbc.Driver");
		try (
			/*2.使用DriverManager獲取數據庫連接
			 *  其中返回的Connection就代表了Java程序和數據庫的連接
			 *  不同數據庫URL寫法需要查驅動文檔,用戶名、密碼有DBA分配
			 */
			Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/crazyjavajdbc?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC", "root","root");
			//3.使用Connection來創建一個Statement對象
			Statement stmt = conn.createStatement();
			/*4.執行SQL語句
			 *  Statement有三種執行SQL語句的方法:
			 *  ①.execute()可執行任何SQL語句-返回一個boolean值,如果執行後第一個結果是ResultSet就返回true,否則返回false
			 *  ②.executeQuery()執行select語句 - 返回查詢到的結果集
			 *  ③.executeUpdate()用於執行DML語句-返回一個整數,代表被SQL語句影響的記錄條數
			 */
//			ResultSet rs = stmt.executeQuery("select s.* , teacher_name from student_table s,teacher_table t where s.java_teacher = t.teacher_id")
			ResultSet rs = stmt.executeQuery("select s.* , t.teacher_name "
					+ "from student_table s , teacher_table t "
					+ "where s.java_teacher = t.teacher_id")
					)
		{
			
		/*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));
			}
		}
	}
}

execute():可以執行任何一個SQL語句,返回一個boolean
但是可以執行getResultSet():獲得該Statement執行查詢語句所返回的ResultSet對象
getUpdateCount():獲取該Statement執行的DML語句所影響的記錄條數

實例代碼
/** 
 * @ClassName: ExecuteSQL
 * @description: 本機代碼主要討論使用execute()方法來執行SQL語句
 * execute()方法的有點:幾乎可以執行任何的SQL語句,而不用考慮DML(executeUpdate)還是DDL()
 * execute():可以執行任何一個SQL語句,返回一個boolean,
 * 但是可以執行
 * getResultSet():獲得該Statement執行查詢語句所返回的ResultSet對象
 * getUpdateCount():獲取該Statement執行的DML語句所影響的記錄條數 
 * @author: FFIDEAL
 * @Date: 2020年4月6日 下午2:30:58
 */  

package M13;

import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;


public class ExecuteSQL {
	private String driver;
	private String url;
	private String user;
	private String pass;
	
	public void initParam(String paramFile) throws Exception{
		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);
				Statement stmt = conn.createStatement();
				){
			//執行SQL語句,返回boolean值表示是否包含ResultSet
			boolean hasResultSet = stmt.execute(sql);
			if(hasResultSet) {
				try(
						//獲取結果集
						ResultSet rs = stmt.getResultSet();
						){
					//ResultSetMetaData是用於分析結果集合的元數據接口
					java.sql.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.println("\n");
					}
				}
			}
		}
	}
	
	public static void main(String[] args) throws Exception {
		ExecuteSQL es = new ExecuteSQL();
		es.initParam("E:\\JavaCode\\Code\\src\\M13\\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");
		es.executeSQL("insert into my_test values(3,'小紅');");
		System.out.println("=====執行查詢數據的查詢語句=====");
		es.executeSQL("select * from my_test");
	}
}

五、使用PreparedStatement執行SQL語句

PreparedStatement接口可以允許在SQL中使用佔位符(?),這是在Statement接口中中不被允許的。

PreparedStatement的效率比Statement高,這是因爲可以使用PreparedStatement對象多次高效的執行SQL語句。使用辦法和Statement方法類似,且提供的方法也是類似的,有execute()方法、executeUpdate()方法和executeQuery()方法等

除了效率較高以外,PreparedStatement還有一個優勢,就是使用PreparedStatement不需要拼接SQL語句,見一下代碼的兩個方法中的SQL語句。

除了上述兩個優點以外,使用PreparedStatement可以防止SQL注入(常見的利用SQL語句漏洞來入侵的方法)

PreparedStatement ps = conn.preparedStatement("insert into student_table values(null,?,1)");
實例代碼
/** 
 * @ClassName: PreparedStatementTest
 * @description: 本節代碼主要討論PreparedStatement和Statement性能的對比
 * @author: FFIDEAL
 * @Date: 2020年4月6日 下午6:01:24
 */  

package M13;

import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Statement;
import java.util.Properties;

public class PreparedStatementTest {
	private String driver;
	private String url;
	private String user;
	private String pass;
	
	public void initParam(String paramFile) throws Exception{
		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);
				Statement stmt = conn.createStatement();
				){
			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 insertUsePreparedStatement() throws Exception{
		long start = System.currentTimeMillis();
		try(
				Connection conn = DriverManager.getConnection(url, user, pass);
				PreparedStatement ps = conn.prepareStatement(
                    "insert into student_table values(null,1,?)");
				){
			//100次爲PreparedStatement的參數設置,可以插入100條記錄
			for (int i = 0; i < 100; i++) {
				ps.setString(1, "姓名" + i);
				ps.setInt(1, i);
				ps.executeUpdate();
			}
			System.out.println("使用PreparedStatement費時:"
                               + (System.currentTimeMillis() - start));
		}
		
	}
	//使用execute()方法來輸出查詢結果
	public void selectAll() throws Exception{
		try(Connection conn = DriverManager.getConnection(url, user, pass);
				Statement stmt = conn.createStatement();
				){
			boolean hasResultSet = stmt.execute("select * from student_table");
			if(hasResultSet) {
				try(
						//通過getResultSet方法獲得結果集
						ResultSet rs = stmt.getResultSet();
						){
					//獲得變化的數字
					ResultSetMetaData rsmd = rs.getMetaData();
					int col = rsmd.getColumnCount();
					while(rs.next()) {
						for (int i = 0; i < col; i++) {
							System.out.print(rs.getString(i + 1) + "\t");
						}
						System.out.println("\n");
					}
				}
				
			}
		}
	}
	
	public static void main(String[] args) throws Exception{
		PreparedStatementTest pst = new PreparedStatementTest();
		pst.initParam("E:\\JavaCode\\Code\\src\\M13\\mysql.ini");
		pst.insertUseStatement();
		pst.insertUsePreparedStatement();
		pst.selectAll();
	}
}


六、使用CallableStatement調用存儲過程

前提是要在數據庫裏有這個事務

//使用Connection來創建一個CallableStatement對象
cstmt = conn.prepareCall("{call add_pro(?,?,?)}");
//傳入參數可以用setXxx(index, number);
//使用registerOutParameter()方法來註冊參數
//之後用execute()來執行
//getXxx(int index)來獲得參數
實例代碼
/** 
 * @ClassName: CallableStatemnetTest
 * @description: 本節代碼主要討論CallableStatement調用存儲過程
 * @author: FFIDEAL
 * @Date: 2020年4月6日 下午8:51:05
 */  

package M13;

import java.io.FileInputStream;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Types;
import java.util.Properties;

public class CallableStatemnetTest {
	private String driver;
	private String url;
	private String user;
	private String pass;
	
	public void initParam(String paramFile) throws Exception{
		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 CallProcedure() throws Exception{
		try(
				Connection conn = DriverManager.getConnection(url, user, pass);
				//通過Connection對象創建一個CallableStatement對象,"{call 存儲過程名稱(佔位符參數1,佔位符參數2,……)}"
				CallableStatement cs = conn.prepareCall("{call add_pro(?,?,?)}");
				){
			cs.setInt(1, 2);
			cs.setInt(2, 3);
			//輸出int類型
			cs.registerOutParameter(3, Types.INTEGER);
			//執行程序
			cs.execute();
			//輸出結果
			System.out.println("執行結果是:" + cs.getInt(3));
		}
	}
	public static void main(String[] args) throws Exception {
		CallableStatemnetTest cst = new CallableStatemnetTest();
		cst.initParam("E:\\JavaCode\\Code\\src\\M13\\mysql.ini");
		cst.CallProcedure();
	}
}

七、可滾動、可更新結果集

ResultSet除了next()指針外,還可以使用absolute()、previous()、afterLast()等方法自由移動記錄指針

在使用Connection創建了Statement或者PreparedStatement是還額外傳入如下兩個參數

  1. resultSetType:控制ResultSet的類型,可以是以下三個值

    • ResultSet.TYPE_FORWARD_ONLY:該常量記錄指針只能向前移動
    • ResultSet.TYPE_SCROLL_INSENSITIVE:該常量控制記錄指針可以自動移動(可滾動結果集),但是底層數據的改變不會影響ResultSet的內容變化
    • ResultSet.TYPE_SCROLL_SENSITIVE:該常量控制記錄指針可以自動移動(可滾動結果集),但是底層數據的改變會影響ResultSet的內容變化
  2. ResultSetConcurrent:控制ResultSet的併發類型,該參數可以接收如下兩個值

    • ResultSet.CONCUR_READ_ONLY:該常量表示ResultSet是隻讀的併發模式(默認)
    • ResultSet.CONCUR_UPDATABLE:該常量表示ResultSet是可更新的併發模式

    下面代碼通過這兩個參數創建一個PreparedStatement對象,有該對象生成的ResultSet對象將是可滾動的、可更新的

//使用Connection創建一個PreparedStatement對象
//傳入控制結果集可滾動、可更新的參數
ps = conn.preparedStatement(sql,ResultSet.TYPE_SCROLL_INSENSITIVE,SesultSet.CONCUR_UPDATABLE);
實例代碼
/** 
 * @ClassName: ResultSetTest
 * @description: 本段代碼主要討論通過Connection來創建PreparedStatement對象,並通過這一對象來是數據庫達到可滾動可更新的目的
 * @author: FFIDEAL
 * @Date: 2020年4月6日 下午11:18:39
 */  

package M13;

import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Properties;

public class ResultSetTest {
	private String driver;
	private String url;
	private String user;
	private String pass;
	
	public void initParam(String paramFile) throws Exception{
		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 query(String sql) throws Exception{
		try(
				Connection conn = DriverManager.getConnection(url, user, pass);
				PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
				ResultSet rs = ps.executeQuery();
				){
			rs.last();
			//獲得行數
			int rowCount = rs.getRow();
			System.out.println("共有" + rowCount + "行");
			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 rst = new ResultSetTest();
		rst.initParam("E:\\JavaCode\\Code\\src\\M13\\mysql.ini");
		rst.query("select * from student_table");
	}
}

八、處理Bob類型的數據

Bob(Binary Long Object)是二進制長對象的意思,Blob列通常用於存儲大文件,典型的Blob內容是一張圖片或者一個聲音文件,由於它的特殊性,必須使用特殊的方法來存儲。而Blob列可以吧圖片、聲音等文件轉化爲二進制數據保存在數據庫裏,並可以從數據庫中恢復指定文件。

Blob數據插入數據庫需要使用PreparedStatement,該對象有一個方法:setBinaryStream()

九、 Java7新增的RowSetFactory與RowSet

RowSet繼承自ResultSet接口,RowSet接口下包含JdbcRowSet、CacheRowSet、FilteredRowSet、JoinRowSet和WebRowSet常用子接口。除了JdbcRowSet需要與數據庫保持連接以外,其他的子接口都是離線的RowSet,但是並不好用(僅做了解)——可滾動、客休該的特性

Java新增的RowSetFactory接口和RowSetProvider類,其中RowSetProvider負責創建RowSetFactory,而RowSetFactory則提供了以下方法創建RowSet實例

  • CachedRowSet createCachedRowSet():創建一個默認的CachedRowSet
  • FilteredRowSet createFilteredRowSet():創建一個默認的FilteredRowSet
  • JdbcRowSet createJdbcRowSet():創建一個默認的JdbcRowSet
  • JoinRowSet createJoinRowSet():創建一個默認的JoinRowSet
  • WebRowSet createWebRowSet():創建一個默認的WebRowSet
實例代碼
/** 
 * @ClassName: JdbcRowSetTest
 * @description: 本節代碼主要討論通過JdbcRowSetImpl示範了使用JdbcRowSet的可滾動、可修改特性
 * @author: FFIDEAL
 * @Date: 2020年4月7日 下午4:34:09
 */  

package M13;

import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Properties;

import javax.sql.rowset.JdbcRowSet;
import javax.sql.rowset.RowSetFactory;
import javax.sql.rowset.RowSetProvider;

public class JdbcRowSetTest {
	private String driver;
	private String url;
	private String user;
	private String pass;
	
	public void initParam(String paramFile) throws Exception{
		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 update(String sql) throws Exception{
		//使用RowSetProvider創建RowSetFactory
		RowSetFactory rsf = RowSetProvider.newFactory();
		try(
				//使用RowSetFactory創建默認的JdbcRowSet實例
				JdbcRowSet jrs = rsf.createJdbcRowSet()
				){
			//設置必要的連接信息
			jrs.setUrl(url);
			jrs.setUsername(user);
			jrs.setPassword(pass);
			//設置查詢語句
			jrs.setCommand(sql);
			//執行查詢
			jrs.execute();
			jrs.afterLast();
			//將前滾動結果集
			while(jrs.previous()) {
				System.out.println("===1====");
				System.out.println(jrs.getString(1)
						+ "\t" + jrs.getString(2)
						+ "\t" + jrs.getString(3)
						+ "\t" + jrs.getInt("student_id"));
				if(jrs.getInt("student_id") == 3) {
					//修改制定記錄行
					jrs.updateString("student_name","Java");
					System.out.println("===2====");
					jrs.updateRow();
				}
			}
		}
	}
	
	public static void main(String[] args) throws Exception{
		JdbcRowSetTest jrst = new JdbcRowSetTest();
		jrst.initParam("E:\\JavaCode\\Code\\src\\M13\\mysql.ini");
		jrst.update("select s.* from student_table s");
	}
}

十、離線RowSet

使用離線RowSet可以將底層數據庫數據讀入內存,封裝成RowSet對象,該對象不僅安全,編程還比較簡單。

實例代碼
/** 
 * @ClassName: CachedRowSetTest
 * @description: 本節代碼討論用CachedRowSet來處理讀取數據
 * @author: FFIDEAL
 * @Date: 2020年4月7日 下午6:14:27
 */  

package M13;

import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;

import javax.sql.rowset.CachedRowSet;
import javax.sql.rowset.RowSetFactory;
import javax.sql.rowset.RowSetProvider;

public class CachedRowSetTest {
	private String driver;
	private static String url;
	private static String user;
	private static String pass;
	
	public void initParam(String paramFile) throws Exception{
		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 CachedRowSet query(String sql) throws Exception{
		Connection conn = DriverManager.getConnection(url, user, pass);
		Statement stmt = conn.createStatement();
		ResultSet rs = stmt.executeQuery(sql);
		//使用RowSetProvider創建RowSetFactory
		RowSetFactory factory = RowSetProvider.newFactory();
		//創建默認的CachedRowSet實例
		CachedRowSet cachers = factory.createCachedRowSet();
		//使用ResultSet裝填RowSet,調用了RowSet的populate(ResultSet rs)方法來包裝給定的ResultSet 
		//接下來就關閉了ResultSet、Statement、Connection等數據庫資源
		cachers.populate(rs);
		//關閉資源
		rs.close();
		stmt.close();
		conn.close();
		return cachers;
	}
	
	public static void main(String[] args) throws Exception{
		CachedRowSetTest ct = new CachedRowSetTest();
		ct.initParam("E:\\JavaCode\\Code\\src\\M13\\mysql.ini");
		CachedRowSet rs = ct.query("select * from student_table");
		rs.afterLast();
		//向前滾動結果集
		while(rs.previous()) {
			System.out.println(rs.getString(1)
					+ "\t" + rs.getString(2)
					+ "\t" + rs.getString(3));
			if(rs.getInt("student_id") == 3) {
				//修改制定記錄行
				rs.updateString("student_name","Java");
				rs.updateRow();
			}
		}
		
		//重新獲取數據庫連接
		Connection conn = DriverManager.getConnection(url, user, pass);
		conn.setAutoCommit(false);
		//把對RowSet所做的修改同步到底底層數據庫
		rs.acceptChanges(conn);
	}
}

十一、離線RowSet的分頁查詢

若一次性將數據加載到內存中,就會造成內存資源緊張的問題,因此在CachedRowSet提供了分頁的功能

CachedRowSet通過以下方法控制分頁

  • populate(ResultSet rs, int satrtRow):使用給定的ResultSet裝填RowSet,從ResultSet的第startRow條記錄開始填裝
  • setPageSize(int PageSize):設置CachedRowSet每次返回多少條記錄
  • previousPage():在底層ResultSet可用的情況下,讓CachedRowSet讀取上一頁記錄
  • nextPage():在底層ResultSet可用的情況下,讓CachedRowSet讀取下一頁記錄
實例代碼
/** 
 * @ClassName: CachedRowSetPage
 * @description: 本節代碼主要討論RowSet的查詢分頁的問題
 * @author: FFIDEAL
 * @Date: 2020年4月7日 下午8:02:59
 */  

package M13;

import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;

import javax.sql.rowset.CachedRowSet;
import javax.sql.rowset.RowSetFactory;
import javax.sql.rowset.RowSetProvider;

public class CachedRowSetPage {
	private String driver;
	private static String url;
	private static String user;
	private static String pass;
	
	public void initParam(String paramFile) throws Exception{
		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 CachedRowSet query(String sql, int pageSize, int page) throws Exception{
		try(
				Connection conn = DriverManager.getConnection(url, user, pass);
				Statement stmt = conn.createStatement();
				ResultSet rs = stmt.executeQuery(sql);){
			//要通過RowSetprovider創建RowSetFactory
			RowSetFactory factory = RowSetProvider.newFactory();
			//通過RowSetFactory創建默認的CachedRowSet
			CachedRowSet cachedRs = factory.createCachedRowSet();
			//顯示每頁顯示的pagesize條記錄
			cachedRs.setPageSize(10);
			//使用ResultSet裝填RowSet,設置從第幾條記錄開始
			cachedRs.populate(rs, (page - 1) * pageSize + 1);
			return cachedRs;
		}
	}
	
	public static void main(String[] args) throws Exception{
		CachedRowSetPage cachedRsp = new CachedRowSetPage();
		cachedRsp.initParam("E:\\JavaCode\\Code\\src\\M13\\mysql.ini");
		//顯示要看第十頁的記錄,每頁顯示10行
		CachedRowSet rs = cachedRsp.query("select * from student_table", 10, 10);
		//向後滾動
		while(rs.next()) {
			System.out.println(rs.getString(1)
					+ "\t" + rs.getString(2)
					+ "\t" + rs.getString(3));
		}
	}
}

十二、事務處理

事務具備四個特性(ACID):分別爲原子性、一致性、隔離性、持續性

數據庫事務由以下語句組成:

  • 一組DML,經過這組DML語句修改之後數據保持較好的一致性
  • 一條DDL語句
  • 一條DCL語句

提交事務:顯示提交(commit);自動提交(執行DDL或者DCL語句,或者程序正常退出)

事務遇到執行失敗會如何——回滾:顯式回滾(rollback);自動回滾(系統錯誤或者強行退出)

#歷史開始事務
begin
#向student_table表中插入3條數據
insert into jdbc_test
values(null,'xx',1);
insert into jdbc_test
values(null,'yy',1);
insert into jdbc_test
values(null,'zz',1);
#查詢student_table表的記錄
select * from jdbc_test;
#回滾事務①
rollback;
#再次查詢②
select * from jdbc_test;

以上代碼在①處發現插入的三條記錄看不到了(隔離性),執行②時看到的是剛插入時候的樣子

在JDBC中可以調用Connection的setAutoCommit()方法來關閉自動提交,開啓事務;同時可以通過Connection中 getAutoCommit()方法來返回該鏈接的自動提交模式

//關閉自動提交,開啓事務
conn.setAutoCommit(false);

一旦事務創建完畢,就像平時創建Statement對象,然後執行任意多條語句

stmt.executeUpdate(...);
stmt.executeUpdate(...);
stmt.executeUpdate(...);

以上代碼只是被執行但是沒有生效,最後要調用Connection中的commit()來提交

conn.commit(); //提交事務

如果任意一條語句失敗,就要調用Connection中的rollback()來回滾

//回滾事務
conn.rollback();
實例代碼
/** 
 * @ClassName: TranscationTest
 * @description: 本節代碼討論了當程序出現了SQLException錯誤時,系統將回滾事務
 * @author: FFIDEAL
 * @Date: 2020年4月7日 下午10:17:59
 */  

package M13;

import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.util.Properties;

public class TranscationTest {
	private String driver;
	private static String url;
	private static String user;
	private static String pass;
	
	public void initParam(String paramFile) throws Exception{
		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 insetInTransaction(String[] sqls) throws Exception{
		try(
				Connection conn = DriverManager.getConnection(url, user, pass);
				){
			//關閉自動提交,開啓事務
			conn.setAutoCommit(false);
			try(
					//使用Connection來創建一個Statement對象
					Statement stmt = conn.createStatement();
					){
				//循環多次執行SQL語句
				for(String sql:sqls) {
					stmt.executeUpdate(sql);
				}
			}
			conn.commit();
		}
	}
	
	public static void main(String[] args) throws Exception{
		TranscationTest tt = new TranscationTest();
		tt.initParam("E:\\JavaCode\\Code\\src\\M13\\mysql.ini");
		String[] sqls = new String[] {
				"insert into jdbc_test values(null,'xx',1);",
				"insert into jdbc_test values(null,'yy',1);",
				"insert into jdbc_test values(null,'zz',1);",
				"insert into jdbc_test values(null,'ccc',5)"
		};
		tt.insetInTransaction(sqls);
		System.out.println("==");
	}
}

十三、使用DatabaseMetaData分析數據庫信息

JDBC提供了DatabaseMetaData來封裝數據庫信息,通過Connection提供的getMetaData()就可以獲取數據庫對應的DatabaseMetaData對象

實例代碼
/** 
 * @ClassName: DatabaseMetaDataTest
 * @description: 本節代碼主要討論通過DatabaseMetaData分析當前Connection連接對應數據庫的一些基本信息,
 * 包括當前數據庫包含多少數據表,存儲過程,student_table表的數據列、主鍵、外鍵等信息
 * @author: FFIDEAL
 * @Date: 2020年4月7日 下午11:05:41
 */  

package M13;

import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

import com.mysql.cj.jdbc.result.ResultSetMetaData;

public class DatabaseMetaDataTest {
	private String driver;
	private static String url;
	private static String user;
	private static String pass;
	
	public void initParam(String paramFile) throws Exception{
		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 info() throws Exception{
		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, "student_table", null, null, "teacher_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);
		}
	}

	/**
	 * @param rs
	 */
	private void printResultSet(ResultSet rs) throws SQLException{
		// TODO Auto-generated method stub
		java.sql.ResultSetMetaData rsmd = rs.getMetaData();
		//打印ResultSet所有的列標題
		for (int i = 0; i < rsmd.getColumnCount(); i++) {
			System.out.print(rsmd.getColumnName(i + 1) + "\t");
		}
		System.out.println("");
		//打印ResultSet所有的數據
		while(rs.next()) {
			for (int i = 0; i < rsmd.getColumnCount(); i++) {
				System.out.print(rs.getString(i + 1) + "\t");
			}
			System.out.println("");
		}
		rs.close();
	}
	
	public static void main(String[] args) throws Exception{
		DatabaseMetaDataTest dmdt = new DatabaseMetaDataTest();
		dmdt.initParam("E:\\JavaCode\\Code\\src\\M13\\mysql.ini");
		dmdt.info();
	}
}

十四、使用連接池管理連接

數據庫連接池是Connection對象工廠。數據庫連接池的主要參數有:

  • 數據庫的初始連接數
  • 連接池的最大連接數
  • 連接池的最小連接數
  • 連接池每次增加的容量
實例代碼1
//以下代碼示範了Tomcat使用DBCP獲取數據庫的方式
//創建數據源對象
BasicDataSource ds = new BasicDataSource();
//設置連接池所需的驅動
ds.setDriverClassName("com.mysql.jc.jdbc.Driver");
//設置連接數據庫的URL
ds.setUrl("jdbc:mysql://127.0.0.1:3306/crazyjavajdbc?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC");
//設置連接池數據庫的用戶名
ds.setUsername("root");
//設置連接數據庫的密碼
ds.setPassword("root");
//設置連接池的初始連接數
ds.setInitialSize(5);
//設置連接池最多可有多少個活動連接數
ds.setMaxActive(20);
//設置連接池中最小有2個空閒的連接
ds.setMinIdle(2);
//所依賴的jar包:commons-dbcp.jar/commons-pool.jar
//通過數據源獲取數據庫連接誒
Connection conn = ds.getConnection();
//釋放數據庫連接
conn.close();
實例代碼2
//以下代碼示範了C3P0連接數據庫的方式
//創建數據源對象
ComboPooledDataSource ds = new ComboPooledDataSource();
//設置連接池所需的驅動
ds.setDriverClass("com.mysql.jc.jdbc.Driver");
//設置連接數據庫的URL
ds.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/crazyjavajdbc?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC");
//設置連接池數據庫的用戶名
ds.setUser("root");
//設置連接數據庫的密碼
ds.setPassword("root");
//設置連接池的初始連接數
ds.setInitialPoolSize(5);
//設置連接池最多可有多少個活動連接數
ds.setMaxPoolSize(20);
//設置連接池中最小有2個空閒的連接
ds.setMinPoolSize(2);
//所依賴的jar包:c3p0-0.9.1.2.jar
//通過數據源獲取數據庫連接誒
Connection conn = ds.getConnection();
//釋放數據庫連接
conn.close();
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章