JDBC簡介
SUN公司為了簡化与殃、統(tǒng)一對數(shù)據(jù)庫的操作咐容,定義了一套Java操作數(shù)據(jù)庫的規(guī)范栅螟,稱之為JDBC执俩。JDBC就是就是使用java代碼(程序)發(fā)送sql語句的技術(shù) !
JDBC全稱為:Java Data Base Connectivity(java數(shù)據(jù)庫連接), 它主要由接口組成徐钠。
組成JDBC的2個包 :
java.sql
,javax.sql
, 以上2個包已經(jīng)包含在J2SE中,所以不用導(dǎo)入役首,開發(fā)這只需要導(dǎo)入JDBC的實現(xiàn)類即數(shù)據(jù)庫驅(qū)動包尝丐。開發(fā)JDBC應(yīng)用需要以上2個包的支持外,還需要導(dǎo)入相應(yīng)JDBC的數(shù)據(jù)庫實現(xiàn)(即數(shù)據(jù)庫驅(qū)動)沒有JDBC之前操作數(shù)據(jù) :
1)通過mysql的客戶端工具衡奥,登錄數(shù)據(jù)庫服務(wù)器 (mysql -u root -p 密碼)
2)編寫sql語句
3)發(fā)送sql語句到數(shù)據(jù)庫服務(wù)器執(zhí)行-
使用JDBC發(fā)送sql前提:
- 登錄數(shù)據(jù)庫服務(wù)器(連接數(shù)據(jù)庫服務(wù)器)
- 數(shù)據(jù)庫的IP地址
- 端口
- 數(shù)據(jù)庫用戶名
- 密碼
JDBC的使用
- 第一個JDBC程序 : 編寫一個程序爹袁,這個程序從user表中讀取數(shù)據(jù),并打印在命令行窗口中:
- 搭建實驗環(huán)境 :
- 在mysql中創(chuàng)建一個庫矮固,并創(chuàng)建user表和插入表的數(shù)據(jù)失息。
- 新建一個Java工程,并導(dǎo)入數(shù)據(jù)驅(qū)動档址。
- 編寫程序盹兢,在程序中加載數(shù)據(jù)庫驅(qū)動 : DriverManager. registerDriver(Driver driver)
- 建立連接(Connection) : Connection conn = DriverManager.getConnection(url, user, pass);
- 創(chuàng)建用于向數(shù)據(jù)庫發(fā)送SQL的Statement對象 : Statement st = conn.createStatement();
- 發(fā)送sql語句 : ResultSet rs = st.excuteQuery(sql);
- 從代表結(jié)果集的ResultSet中取出數(shù)據(jù),打印到命令行窗口
- 斷開與數(shù)據(jù)庫的連接守伸,并釋放相關(guān)資源
//連接數(shù)據(jù)庫的URL
private String url = "jdbc:mysql://localhost:3306/User";
// jdbc協(xié)議:數(shù)據(jù)庫子協(xié)議:主機:端口/連接的數(shù)據(jù)庫 //
private String user = "root";//用戶名
private String password = "root";//密碼
/**
* 第一種方法
* @throws Exception
*/
@Test
public void test1() throws Exception{
//1.創(chuàng)建驅(qū)動程序類對象
Driver driver = new com.mysql.jdbc.Driver(); //新版本
//Driver driver = new org.gjt.mm.mysql.Driver(); //舊版本
//設(shè)置用戶名和密碼
Properties props = new Properties();
props.setProperty("user", user);
props.setProperty("password", password);
//2.連接數(shù)據(jù)庫绎秒,返回連接對象
Connection conn = driver.connect(url, props);
System.out.println(conn);
}
JDBC接口核心的API : java.sql.*
和 javax.sql.*
-
DriverManager : 表示java驅(qū)動程序接口。所有的具體的數(shù)據(jù)庫廠商要來實現(xiàn)此接口
- Jdbc程序中的DriverManager用于加載驅(qū)動尼摹,并創(chuàng)建與數(shù)據(jù)庫的鏈接替裆,這個API的常用方法 :
DriverManager.registerDriver(new Driver())
DriverManager.getConnection(url, user, password)
- url語法 : jdbc協(xié)議:數(shù)據(jù)庫子協(xié)議://主機:端口/數(shù)據(jù)庫;
- user : 數(shù)據(jù)庫的用戶名 ;
- password : 數(shù)據(jù)庫用戶密碼
- 常用數(shù)據(jù)庫URL地址的寫法:
- Oracle:
jdbc:oracle:thin:@localhost:1521:sid
- SqlServer :
jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=sid
- MySql :
jdbc:mysql://localhost:3306/sid
- Oracle:
- 注意:在實際開發(fā)中并不推薦采用registerDriver方法注冊驅(qū)動。原因有二:
- 查看Driver的源代碼可以看到窘问,如果采用此種方式辆童,會導(dǎo)致驅(qū)動程序注冊兩次,也就是在內(nèi)存中會有兩個Driver對象惠赫。
- 程序依賴mysql的api把鉴,脫離mysql的jar包,程序?qū)o法編譯,將來程序切換底層數(shù)據(jù)庫將會非常麻煩
- 推薦方式 :
Class.forName(“com.mysql.jdbc.Driver”);
采用此種方式不會導(dǎo)致驅(qū)動對象在內(nèi)存中重復(fù)出現(xiàn)庭砍,并且采用此種方式场晶,程序僅僅只需要一個字符串,不需要依賴具體的驅(qū)動怠缸,使程序的靈活性更高诗轻。同樣,在開發(fā)中也不建議采用具體的驅(qū)動類型指向getConnection方法返回的connection對象
- Jdbc程序中的DriverManager用于加載驅(qū)動尼摹,并創(chuàng)建與數(shù)據(jù)庫的鏈接替裆,這個API的常用方法 :
-
Connection接口 : 表示java程序和數(shù)據(jù)庫的連接對象
- Jdbc程序中的Connection揭北,它用于代表數(shù)據(jù)庫的鏈接扳炬,Collection是數(shù)據(jù)庫編程中最重要的一個對象,客戶端與數(shù)據(jù)庫所有交互都是通過connection對象完成的搔体,這個對象的常用方法:
- createStatement() : 創(chuàng)建向數(shù)據(jù)庫發(fā)送sql的statement對象
- prepareStatement(sql) : 創(chuàng)建向數(shù)據(jù)庫發(fā)送預(yù)編譯sql的PrepareSatement對象
- prepareCall(sql) : 創(chuàng)建執(zhí)行存儲過程的CallableStatement對象
- setAutoCommit(boolean autoCommit) : 設(shè)置事務(wù)是否自動提交
- commit() : 在鏈接上提交事務(wù)
- rollback() : 在此鏈接上回滾事務(wù)
- Jdbc程序中的Connection揭北,它用于代表數(shù)據(jù)庫的鏈接扳炬,Collection是數(shù)據(jù)庫編程中最重要的一個對象,客戶端與數(shù)據(jù)庫所有交互都是通過connection對象完成的搔体,這個對象的常用方法:
-
Statement接口 : 用于執(zhí)行靜態(tài)的sql語句
- Jdbc程序中的Statement對象用于向數(shù)據(jù)庫發(fā)送SQL語句恨樟, Statement對象常用方法:
- executeQuery(String sql) :用于向數(shù)據(jù)發(fā)送查詢語句。
- executeUpdate(String sql):用于向數(shù)據(jù)庫發(fā)送insert疚俱、update或delete語句
- execute(String sql):用于向數(shù)據(jù)庫發(fā)送任意sql語句
- addBatch(String sql) :把多條sql語句放到一個批處理中劝术。
- executeBatch():向數(shù)據(jù)庫發(fā)送一批sql語句執(zhí)行。
- Jdbc程序中的Statement對象用于向數(shù)據(jù)庫發(fā)送SQL語句恨樟, Statement對象常用方法:
-
PreparedStatement接口 : 用于執(zhí)行預(yù)編譯sql語句
- PreperedStatement 是 Statement 的孩子, PreperedStatement的實例對象可以通過調(diào)用 Connection.preparedStatement()方法獲得呆奕,相對于Statement對象而言:
- PreperedStatement 可以避免SQL注入的問題
- Statement會使數(shù)據(jù)庫頻繁編譯SQL养晋,可能造成數(shù)據(jù)庫緩沖區(qū)溢出。
- PreparedStatement 可對SQL進行預(yù)編譯梁钾,從而提高數(shù)據(jù)庫的執(zhí)行效率匙握。
- 并且PreperedStatement對于sql中的參數(shù),允許使用占位符的形式進行替換陈轿,簡化sql語句的編寫
- PreperedStatement 是 Statement 的孩子, PreperedStatement的實例對象可以通過調(diào)用 Connection.preparedStatement()方法獲得呆奕,相對于Statement對象而言:
-
ResultSet接口 : 用于封裝查詢出來的數(shù)據(jù)
- Jdbc程序中的ResultSet用于代表SQL語句的執(zhí)行結(jié)果圈纺。Resultset封裝執(zhí)行結(jié)果時,采用的類似于表格的方式麦射。ResultSet 對象維護了一個指向表格數(shù)據(jù)行的游標蛾娶,初始的時候,游標在第一行之前潜秋,調(diào)用ResultSet.next() 方法蛔琅,可以使游標指向具體的數(shù)據(jù)行,進行調(diào)用方法獲取該行的數(shù)據(jù)峻呛。
- ResultSet既然用于封裝執(zhí)行結(jié)果的罗售,所以該對象提供的都是用于獲取數(shù)據(jù)的get方法:
- 獲取任意類型的數(shù)據(jù) :
getObject(int index)
,getObject(string columnName)
- 獲取指定類型的數(shù)據(jù) :
getString(int index)
,getString(String columnName)
- 獲取任意類型的數(shù)據(jù) :
- 如果數(shù)據(jù)庫中列的類型是varchar,獲取該列的數(shù)據(jù)調(diào)用什么方法钩述?Int類型呢寨躁?bigInt類型呢?Boolean類型牙勘?
- 注意:列的索引從1開始职恳。
- ResultSet還提供了對結(jié)果集進行滾動的方法:
- next():移動到下一行
- previous():移動到前一行
- absolute(int row):移動到指定行
- beforeFirst():移動resultSet的最前面
- afterLast() :移動到resultSet的最后面
-
釋放資源
- Jdbc程序運行完后所禀,切記要釋放程序在運行過程中,創(chuàng)建的那些與數(shù)據(jù)庫進行交互的對象放钦,這些對象通常是ResultSet, Statement和Connection對象
- 特別是Connection對象色徘,它是非常稀有的資源,用完后必須馬上釋放操禀,如果Connection不能及時褂策、正確的關(guān)閉,極易導(dǎo)致系統(tǒng)宕機颓屑。Connection的使用原則是盡量晚創(chuàng)建斤寂,盡量早的釋放。(使用原則:盡量晚的創(chuàng)建邢锯,盡量早的釋放)
- 為確保資源釋放代碼能運行,資源釋放代碼也一定要放在finally語句中
-
使用JDBC對數(shù)據(jù)庫進行CRUD
- Jdbc中的statement對象用于向數(shù)據(jù)庫發(fā)送SQL語句搀别,想完成對數(shù)據(jù)庫的增刪改查丹擎,只需要通過這個對象向數(shù)據(jù)庫發(fā)送增刪改查語句即可
-
Statement對象的executeUpdate方法,用于向數(shù)據(jù)庫發(fā)送增歇父、刪蒂培、改的sql語句,executeUpdate執(zhí)行完后榜苫,將會返回一個整數(shù)(即增刪改語句導(dǎo)致了數(shù)據(jù)庫幾行數(shù)據(jù)發(fā)生了變化)
- Statement.executeQuery方法用于向數(shù)據(jù)庫發(fā)送查詢語句护戳,executeQuery方法返回代表查詢結(jié)果的ResultSet對象
使用Statement執(zhí)行sql語句(具體使用方法)
- 執(zhí)行DDL語句
/**
* 執(zhí)行DDL語句(創(chuàng)建表)
*/
@Test
public void test1(){
Statement stmt = null;
Connection conn = null;
try {
//1.驅(qū)動注冊程序
Class.*forName*("com.mysql.jdbc.Driver");
//2.獲取連接對象
conn = DriverManager.*getConnection*(url, user, password);
//3.創(chuàng)建Statement
stmt = conn.createStatement();
//4.準備sql
String sql = "CREATE TABLE student(id INT PRIMARY KEY AUTO_INCREMENT,NAME VARCHAR(20),gender VARCHAR(2))";
//5.發(fā)送sql語句,執(zhí)行sql語句,得到返回結(jié)果
int count = stmt.executeUpdate(sql);
//6.輸出
System.out.println("影響了"+count+"行垂睬!");
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
} finally{
//7.關(guān)閉連接(順序:后打開的先關(guān)閉)
if(stmt!=null)
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
if(conn!=**null**)
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
- 執(zhí)行DML語句
/**
* 使用Statement執(zhí)行DML語句
* @author APPle
*
*/
public class Demo2 {
private String url = "jdbc:mysql://localhost:3306/day17";
private String user = "root";
private String password = "root";
/**
* 增加
*/
@Test
public void testInsert(){
Connection conn = null;
Statement stmt = null;
try {
//通過工具類獲取連接對象
conn = JdbcUtil.getConnection();
//3.創(chuàng)建Statement對象
stmt = conn.createStatement();
//4.sql語句
String sql = "INSERT INTO student(NAME,gender) VALUES('李四','女')";
//5.執(zhí)行sql
int count = stmt.executeUpdate(sql);
System.out.println("影響了"+count+"行");
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
} finally{
//關(guān)閉資源
/*if(stmt!=null)
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
if(conn!=null)
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}*/
JdbcUtil.close(conn, stmt);
}
}
/**
* 修改
*/
@Test
public void testUpdate(){
Connection conn = null;
Statement stmt = null;
//模擬用戶輸入
String name = "陳六";
int id = 3;
try {
/*//1.注冊驅(qū)動
Class.forName("com.mysql.jdbc.Driver");
//2.獲取連接對象
conn = DriverManager.getConnection(url, user, password);*/
//通過工具類獲取連接對象
conn = JdbcUtil.getConnection();
//3.創(chuàng)建Statement對象
stmt = conn.createStatement();
//4.sql語句
String sql = "UPDATE student SET NAME='"+name+"' WHERE id="+id+"";
System.out.println(sql);
//5.執(zhí)行sql
int count = stmt.executeUpdate(sql);
System.out.println("影響了"+count+"行");
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
} finally{
//關(guān)閉資源
/*if(stmt!=null)
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
if(conn!=null)
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}*/
JdbcUtil.close(conn, stmt);
}
}
/**
* 刪除
*/
@Test
public void testDelete(){
Connection conn = null;
Statement stmt = null;
//模擬用戶輸入
int id = 3;
try {
/*//1.注冊驅(qū)動
Class.forName("com.mysql.jdbc.Driver");
//2.獲取連接對象
conn = DriverManager.getConnection(url, user, password);*/
//通過工具類獲取連接對象
conn = JdbcUtil.getConnection();
//3.創(chuàng)建Statement對象
stmt = conn.createStatement();
//4.sql語句
String sql = "DELETE FROM student WHERE id="+id+"";
System.out.println(sql);
//5.執(zhí)行sql
int count = stmt.executeUpdate(sql);
System.out.println("影響了"+count+"行");
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
} finally{
//關(guān)閉資源
/*if(stmt!=null)
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
if(conn!=null)
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}*/
JdbcUtil.close(conn, stmt);
}
}
}
- 執(zhí)行DQL語句
/**
* 使用Statement執(zhí)行DQL語句(查詢操作)
*/
public class Demo {
@Test
public void test1(){
Connection conn = null;
Statement stmt = null;
try{
//獲取連接
conn = JdbcUtil.getConnection();
//創(chuàng)建Statement
stmt = conn.createStatement();
//準備sql
String sql = "SELECT * FROM student";
//執(zhí)行sql
ResultSet rs = stmt.executeQuery(sql);
//移動光標
/*boolean flag = rs.next();
flag = rs.next();
flag = rs.next();
if(flag){
//取出列值
//索引
int id = rs.getInt(1);
String name = rs.getString(2);
String gender = rs.getString(3);
System.out.println(id+","+name+","+gender);
//列名稱
int id = rs.getInt("id");
String name = rs.getString("name");
String gender = rs.getString("gender");
System.out.println(id+","+name+","+gender);
}*/
//遍歷結(jié)果
while(rs.next()){
int id = rs.getInt("id");
String name = rs.getString("name");
String gender = rs.getString("gender");
System.out.println(id+","+name+","+gender);
}
}catch(Exception e){
e.printStackTrace();
throw new RuntimeException(e);
} finally {
JdbcUtil.close(conn, stmt);
}
}
}
- 使用PreparedStatement執(zhí)行sql語句
public class Demo1 {
/**
* 增加
*/
@Test
public void testInsert() {
Connection conn = null;
PreparedStatement stmt = null;
try {
//1.獲取連接
conn = JdbcUtil.getConnection();
//2.準備預(yù)編譯的sql
String sql = "INSERT INTO student(NAME,gender) VALUES(?,?)"; //?表示一個參數(shù)的占位符
//3.執(zhí)行預(yù)編譯sql語句(檢查語法)
stmt = conn.prepareStatement(sql);
//4.設(shè)置參數(shù)值
/**
* 參數(shù)一: 參數(shù)位置 從1開始
*/
stmt.setString(1, "李四");
stmt.setString(2, "男");
//5.發(fā)送參數(shù)媳荒,執(zhí)行sql
int count = stmt.executeUpdate();
System.out.println("影響了"+count+"行");
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
} finally {
JdbcUtil.close(conn, stmt);
}
}
/**
* 修改
*/
@Test
public void testUpdate() {
Connection conn = null;
PreparedStatement stmt = null;
try {
//1.獲取連接
conn = JdbcUtil.getConnection();
//2.準備預(yù)編譯的sql
String sql = "UPDATE student SET NAME=? WHERE id=?"; //?表示一個參數(shù)的占位符
//3.執(zhí)行預(yù)編譯sql語句(檢查語法)
stmt = conn.prepareStatement(sql);
//4.設(shè)置參數(shù)值
/**
* 參數(shù)一: 參數(shù)位置 從1開始
*/
stmt.setString(1, "王五");
stmt.setInt(2, 9);
//5.發(fā)送參數(shù),執(zhí)行sql
int count = stmt.executeUpdate();
System.out.println("影響了"+count+"行");
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
} finally {
JdbcUtil.close(conn, stmt);
}
}
/**
* 刪除
*/
@Test
public void testDelete() {
Connection conn = null;
PreparedStatement stmt = null;
try {
//1.獲取連接
conn = JdbcUtil.getConnection();
//2.準備預(yù)編譯的sql
String sql = "DELETE FROM student WHERE id=?"; //?表示一個參數(shù)的占位符
//3.執(zhí)行預(yù)編譯sql語句(檢查語法)
stmt = conn.prepareStatement(sql);
//4.設(shè)置參數(shù)值
/**
* 參數(shù)一: 參數(shù)位置 從1開始
*/
stmt.setInt(1, 9);
//5.發(fā)送參數(shù)驹饺,執(zhí)行sql
int count = stmt.executeUpdate();
System.out.println("影響了"+count+"行");
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
} finally {
JdbcUtil.close(conn, stmt);
}
}
/**
* 查詢
*/
@Test
public void testQuery() {
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
//1.獲取連接
conn = JdbcUtil.getConnection();
//2.準備預(yù)編譯的sql
String sql = "SELECT * FROM student";
//3.預(yù)編譯
stmt = conn.prepareStatement(sql);
//4.執(zhí)行sql
rs = stmt.executeQuery();
//5.遍歷rs
while(rs.next()){
int id = rs.getInt("id");
String name = rs.getString("name");
String gender = rs.getString("gender");
System.out.println(id+","+name+","+gender);
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
} finally {
//關(guān)閉資源
JdbcUtil.close(conn,stmt,rs);
}
}
}
PreparedStatement 與 Statment ( 推薦使用PreparedStatement)
語法不同 : PreparedStatement可以使用預(yù)編譯的sql钳枕,而Statment只能使用靜態(tài)的sql
效率不同 : PreparedStatement可以使用sql緩存區(qū),效率比Statment高
安全性不同 : PreparedStatement可以有效防止sql注入赏壹,而Statment不能防止sql注入
CallableStatement執(zhí)行存儲過程
/**
* 使用CablleStatement調(diào)用存儲過程
* @author APPle
*
*/
public class Demo1 {
/**
* 調(diào)用帶有輸入?yún)?shù)的存儲過程
* CALL pro_findById(4);
*/
@Test
public void test1(){
Connection conn = null;
CallableStatement stmt = null;
ResultSet rs = null;
try {
//獲取連接
conn = JdbcUtil.getConnection();
//準備sql
String sql = "CALL pro_findById(?)"; //可以執(zhí)行預(yù)編譯的sql
//預(yù)編譯
stmt = conn.prepareCall(sql);
//設(shè)置輸入?yún)?shù)
stmt.setInt(1, 6);
//發(fā)送參數(shù)
rs = stmt.executeQuery(); //注意: 所有調(diào)用存儲過程的sql語句都是使用executeQuery方法執(zhí)行S愠础!蝌借!
//遍歷結(jié)果
while(rs.next()){
int id = rs.getInt("id");
String name = rs.getString("name");
String gender = rs.getString("gender");
System.out.println(id+","+name+","+gender);
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
} finally {
JdbcUtil.close(conn, stmt ,rs);
}
}
/**
* 執(zhí)行帶有輸出參數(shù)的存儲過程
* CALL pro_findById2(5,@NAME);
*/
@Test
public void test2(){
Connection conn = null;
CallableStatement stmt = null;
ResultSet rs = null;
try {
//獲取連接
conn = JdbcUtil.getConnection();
//準備sql
String sql = "CALL pro_findById2(?,?)"; //第一個昔瞧?是輸入?yún)?shù),第二個菩佑?是輸出參數(shù)
//預(yù)編譯
stmt = conn.prepareCall(sql);
//設(shè)置輸入?yún)?shù)
stmt.setInt(1, 6);
//設(shè)置輸出參數(shù)(注冊輸出參數(shù))
/**
* 參數(shù)一: 參數(shù)位置
* 參數(shù)二: 存儲過程中的輸出參數(shù)的jdbc類型 VARCHAR(20)
*/
stmt.registerOutParameter(2, java.sql.Types.VARCHAR);
//發(fā)送參數(shù)自晰,執(zhí)行
stmt.executeQuery(); //結(jié)果不是返回到結(jié)果集中,而是返回到輸出參數(shù)中
//得到輸出參數(shù)的值
/**
* 索引值: 預(yù)編譯sql中的輸出參數(shù)的位置
*/
String result = stmt.getString(2); //getXX方法專門用于獲取存儲過程中的輸出參數(shù)
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
} finally {
JdbcUtil.close(conn, stmt ,rs);
}
}
}