文章目錄
一、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語句執行結果
- 加載驅動,規範
Class.forName(dirver) - 使用Connection創建Connection對象,以便可以使用Connection創建Statement對象
Connection conn = DriverManager.getConnection(url,user,pass) - 使用Connection對象創建Statement對象,以便使用Statement對象的executeXxx方法
Statement stmt = conn.createStatement() - 調用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是還額外傳入如下兩個參數
-
resultSetType:控制ResultSet的類型,可以是以下三個值
- ResultSet.TYPE_FORWARD_ONLY:該常量記錄指針只能向前移動
- ResultSet.TYPE_SCROLL_INSENSITIVE:該常量控制記錄指針可以自動移動(可滾動結果集),但是底層數據的改變不會影響ResultSet的內容變化
- ResultSet.TYPE_SCROLL_SENSITIVE:該常量控制記錄指針可以自動移動(可滾動結果集),但是底層數據的改變會影響ResultSet的內容變化
-
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();