本文實例講述了JDBC基礎知識與技巧。分享給大家供大家參考。具體分析如下:
1.什么是JDBC?
通俗來講JDBC技術(shù)就是通過java程序來發(fā)送SQL語句到數(shù)據(jù)庫,數(shù)據(jù)庫收到SQL語句后執(zhí)行,把結(jié)果返回給java程序管理。
2.使用JDBC要有什么條件呢?
A)目標數(shù)據(jù)庫主機的地址
B)數(shù)據(jù)庫軟件在該主機上所占用的端口號
C)登陸數(shù)據(jù)庫用的用戶名
D)該用戶名的密碼
E)連接數(shù)據(jù)庫
3.JDBC技術(shù)的原理
我們知道,數(shù)據(jù)庫是有各種類型的,不同的廠商生產(chǎn)的數(shù)據(jù)庫標格和規(guī)范是不同的,這時候,如果我們用JAVA代碼來發(fā)送SQL語句,就要根據(jù)不同的數(shù)據(jù)庫來寫一套又一套的操作代碼,這對程序開發(fā)者的開發(fā)成本是十分巨大的,所以,SUN公司在開發(fā)JDBC技術(shù)的時候,規(guī)定了一套標準接口,數(shù)據(jù)庫產(chǎn)商都必須提供一個驅(qū)動來實現(xiàn)這套接口,那么,只要程序開發(fā)者在開發(fā)時使用了該數(shù)據(jù)庫的驅(qū)動,就用一致的方法來開發(fā)了,而不需自己寫一套有一套的代碼去適應不同的數(shù)據(jù)庫。
4.JDBC中的核心API
|- Driver : 驅(qū)動程序類實現(xiàn)的接口。
|-Connection connect(String url, Properties info) --用于連接數(shù)據(jù)庫,得到連接對象
Properties 里需要設置的參數(shù):
url: 數(shù)據(jù)庫連接的URL字符串。協(xié)議+數(shù)據(jù)庫子協(xié)議+主機+端口+數(shù)據(jù)庫
user: 數(shù)據(jù)庫用戶名
password: 用戶的密碼
|-Connection : 與數(shù)據(jù)庫連接的接口
|- Statement createStatement() --創(chuàng)建Statement對象,用于發(fā)送sql語句
|- PreparedStatement prepareStatement(String sql) -創(chuàng)建PreparedStatement對象,用于發(fā)送預編譯的sql語句
|-CallableStatement prepareCall(String sql) --創(chuàng)建CallableStatement對象,用于調(diào)用存儲過程。
|-Statement: 用于執(zhí)行靜態(tài)sql語句
|-int executeUpdate(String sql) --執(zhí)行更新操作(DDL+DML)
|-ResultSet executeQuery(String sql) --執(zhí)行查詢操作(DQL)
|- PreparedStatement: 用于執(zhí)行預編譯的sql語句
|- int executeUpdate() -- 執(zhí)行更新操作
|- ResultSet executeQuery() -- 執(zhí)行查詢操作
|- CallableStatement: 用于執(zhí)行存儲過程的sql
|- ResultSet executeQuery() --調(diào)用存儲過程
|- ResultSet: 結(jié)果集。用于封裝數(shù)據(jù)庫的查詢后的數(shù)據(jù)
|- boolean next() --將記錄光標移動到下一行
|- Object getObject(int columnIndex) -- 得到字段上的值
了解完又哪些API,下面我們就來使用JDBC發(fā)送SQL語句吧~
5.使用Statement對象操作數(shù)據(jù)庫
DDL與DML操作
步驟1
導包,因為我使用的是MySQL數(shù)據(jù)庫,所以要使用JDBC技術(shù),必須使用由MySQL數(shù)據(jù)庫產(chǎn)商提供的數(shù)據(jù)庫驅(qū)動,所以,第一步我們要把數(shù)據(jù)庫驅(qū)動包導入工程里。
使用的包名:mysql-connector-java-5.1.7-bin.jar
步驟2
創(chuàng)建一個普通的類,在里面添加一個方法,在該方法中按照以下步驟
//URL
private String url = "jdbc:mysql://localhost:3306/vmaxtam";
//user
private String user = "root";
//password
private String password = "root";
public void testDDL()throws Exception{
//1.注冊驅(qū)動
Class.forName("com.mysql.jdbc.Driver");
//2.獲取連接
Connection conn = DriverManager.getConnection(url, user, password);
//3.創(chuàng)建Statement對象
Statement stmt = conn.createStatement();
//4.準備sql語句
String sql ="CREATE TABLE student(sid INT PRIMARY KEY,sname VARCHAR(20),age INT)";
//5.通過statement對象發(fā)送sql語句,返回執(zhí)行結(jié)果
int count = stmt.executeUpdate(sql);
//6.打印執(zhí)行結(jié)果
System.out.println("影響了"+count+"條記錄");
}
//7.關(guān)閉資源
if(statement!=null)
{
statement.close();
}
if(conn!=null)
{
conn.close();
}
如果要進行DQL與DDL操作,都可以把SQL語句寫好,然后調(diào)用statement的executlUpdate方法來給數(shù)據(jù)庫執(zhí)行SQL語句,這個方法返回一個整數(shù)值,表示數(shù)據(jù)庫中有多少行受到了影響。
如果我們不改變上述的程序,想要再向數(shù)據(jù)庫發(fā)出SQL語句,那么又要寫一個程序來再次連接,操作完后又要關(guān)閉statement對象 和connection對象,這是十分繁瑣的。所以,我們一般把連接過程和釋放對象的過程抽取到一個工具類中。工具類中的代碼如下:
public class sqlUtil {
private static String url = "jdbc:mysql://localhost:3306/vmaxtam";
private static String user = "root";
private static String password = "root";
// 獲取連接
public static Connection getconnection() {
Connection conn = null;
try {
// 1.注冊驅(qū)動
Class.forName("com.mysql.jdbc.Driver");
// 2.獲取連接
conn = DriverManager.getConnection(url, user, password);
// 3.獲得statement對象
Statement statement = conn.createStatement();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return conn;
}
// 7.關(guān)閉資源
public static void close(Statement statement, Connection connection) {
{
try {
if (statement != null)
statement.close();
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
還要考慮的情況就是:
一)一個用戶只需要注冊一次驅(qū)動就行,不用每次連接數(shù)據(jù)庫都注冊驅(qū)動,所以我們把注冊驅(qū)動的過程寫在靜態(tài)代碼塊中。
二)url 和用戶名,密碼還有驅(qū)動類名,在程序里是寫死的,為了能夠在不改代碼的前提下更換數(shù)據(jù)庫 或者更換用戶,我們通常把這些信息寫到一份配置文件中。
配置文件寫在工程的src目錄下,名為db.properties
user=root
password=root
driverClass=com.mysql.jdbc.Drive
然后再sqlUtil中讀取該配置文件,最后優(yōu)化成下面代碼
public class sqlUtil {
private static String url = null;
private static String user = null;
private static String password = null;
private static String driverClass= null;
static{
try {
//1.獲得字節(jié)碼對象
Class clazz = sqlUtil.class;
//2.調(diào)用getResourceAsStream獲取路徑
InputStream inputStream = clazz.getResourceAsStream("/db.properties");
Properties pro = new Properties();
pro.load(inputStream);
//3.讀取參數(shù)
url=pro.getProperty("url");
password=pro.getProperty("password");
user=pro.getProperty("user");
driverClass=pro.getProperty("driverClass");
Class.forName(driverClass);
} catch (Exception e) {
e.printStackTrace();
System.out.println("注冊失敗!" + e.getMessage());
throw new RuntimeException(e);
}
}
// 獲取連接
public static Connection getconnection() {
Connection conn = null;
try {
// 獲取連接
conn = DriverManager.getConnection(url, user, password);
// 獲得statement對象
Statement statement = conn.createStatement();
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}
// 關(guān)閉資源
public static void close(Statement statement, Connection connection) {
{
try {
if (statement != null)
statement.close();
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
DQL操作
那么如何用JDBC來查詢數(shù)據(jù)庫中的數(shù)據(jù)呢?
public void testdsl() throws Exception {
//獲取連接
cnn2=sqlUtil.getconnection();
Statement statement = cnn2.createStatement();
//準備SQL語句
String sql = "select * from subject";
//調(diào)用executeQuery執(zhí)行查詢語句
ResultSet res = statement.executeQuery(sql);
//查詢結(jié)束后res會指向表頭,想要獲取數(shù)據(jù)必須不斷地指向查詢結(jié)果的下一行,當沒有下一行數(shù)據(jù)時,返回0.
while(res.next())
{
//獲取查詢結(jié)果中字段為“sjid”的值,并且要明確類型
int id = res.getInt("sjid");
//獲取查詢結(jié)果中字段為“sjname”的值,并且要明確類型
String name = res.getString("sjname");
System.out.println("ID:" + id + " NAME:" + name);
}
sqlUtil.close(statement, cnn2);
}
以上就是使用Statement對象來操作數(shù)據(jù)庫了~
6.使用PreparedStatement操作數(shù)據(jù)庫
PreparedStatement對象其實就是一個特殊的Statement對象,它能夠預編譯SQL語句,當你把參數(shù)設置好,然后就可以去執(zhí)行SQL語句了~
DDL與DML操作
package com.vmaxtam.sqltest;
import java.sql.Connection;
import java.sql.PreparedStatement;
import org.junit.Test;
public class PreparedStatementTest {
Connection connection = null;
@Test
public void ddldmlTest() throws Exception {
// 1.獲取連接
connection = sqlUtil.getconnection();
// 2.準備SQL語句,預編譯語句,參數(shù)用?號占位
String sql = "INSERT INTO SUBJECT VALUES(?,?)";
// 3.獲得對象
PreparedStatement preparedStatement = connection.prepareStatement(sql);
/*
* 4.設置SQL參數(shù) 需要參數(shù)是第幾個,并且知道它的類型 下面第一句表示:SQL語句第一個參數(shù)是int類型,參數(shù)值設置為3,如此類推
*/
preparedStatement.setInt(1, 3);
preparedStatement.setString(2, "英語");
// 5.交給數(shù)據(jù)庫執(zhí)行SQL
int num = preparedStatement.executeUpdate();
System.out.println("有" + num + "條記錄受到了影響");
sqlUtil.close(preparedStatement , connection );
}
}
以上就是使用PreparedStatement對象來進行插入語句的發(fā)送,同理,DDL與DML類的語句都可以根據(jù)這樣來發(fā)送.
PreparedStatement預編譯的好處:
PreparedStatement的預編譯可以使你可以通過設置不同的參數(shù)來查詢不同的目標,在數(shù)據(jù)庫端,只會保存一段預編譯語句,但是如果你使用Statement來發(fā)送語句,每發(fā)送一條,數(shù)據(jù)庫中就會存一條,這可能會造成占用大量內(nèi)存。
DQL操作
@Test
public void dqlTest() throws Exception {
// 1.獲取連接
connection = sqlUtil.getconnection();
// 2.準備SQL語句,預編譯語句,參數(shù)用?號占位
String sql = "select * from subject where sjid=? or sjname=?";
// 3.獲得對象
PreparedStatement preparedStatement = connection.prepareStatement(sql);
/*
* 4.設置SQL參數(shù) 需要參數(shù)是第幾個,并且知道它的類型 下面第一句表示:SQL語句第一個參數(shù)是int類型,參數(shù)值設置為3,如此類推
*/
preparedStatement.setInt(1, 2);
preparedStatement.setString(2, "語文");
// 5.交給數(shù)據(jù)庫執(zhí)行SQL
ResultSet rst = preparedStatement.executeQuery();
//6.迭代結(jié)果集
while(rst.next())
{
int id = rst.getInt("sjid");
String name = rst.getString("sjname");
System.out.println("ID:" + id + " NAME:" + name);
}
//7.關(guān)閉連接
sqlUtil.close(preparedStatement, connection);
}
也是調(diào)用executeQuery();方法即可,得到結(jié)果集后迭代輸出~
既然Statement 與 PreparedStatement那么相似,比較它們的優(yōu)缺點吧~
Statement 與 PreparedStatement的區(qū)別:
1.語法不同
Statement只支持靜態(tài)編譯,SQL語句是寫死的。
PreparedStatement支持預編譯,用?號來占位。
2.效率不同
Statement每次都要發(fā)送一條SQL語句,不支持緩存,執(zhí)行效率低。
PreparedStatement支持預編譯,緩存在數(shù)據(jù)庫,只需發(fā)送參數(shù),執(zhí)行效率快。
3.安全性不同
Statement容易被注入。
注入:狡猾的分子可以編寫特殊的SQL語句來入侵數(shù)據(jù)庫。
例如:要查詢某個用戶的信息
一般情況:SELECT * FROM user_list where username=xxx and password=xxx;(這里的xxx本應為用戶填寫自己的用戶名和密碼)
注入情況:SELECT * FROM user_list where username='abc' or 1=1 -- password=xxx;
這樣1=1恒等,而且在password前加上了“--”號,后面的內(nèi)容成為了注釋不被執(zhí)行。也就是說,這樣就能不用密碼地查詢所有的用戶信息。
PreparedStatement,因為規(guī)定了SQL語句中的參數(shù),所以可以防止注入。
結(jié)論:建議使用PreparedStatement,因為它更快更安全。
7.使用CallableStatement執(zhí)行存儲過程
使用CallableStatement只是執(zhí)行存儲過程,創(chuàng)建存儲過程我們還是要在數(shù)據(jù)庫內(nèi)創(chuàng)建的。
步驟1
現(xiàn)在數(shù)據(jù)庫建好一個存儲過程:
CREATE PROCEDURE pro_add(IN a INT , IN b VARCHAR(20),OUT c INT)
BEGIN
SELECT * FROM SUBJECT WHERE sjid=a OR sjname=b;
SET c=a+a+a+a;
END $
步驟2
利用java代碼執(zhí)行,并得到輸出參數(shù)
public void calaST() throws Exception {
//獲取連接
connection= sqlUtil.getconnection();
//準備SQL語句
String sql = "CALL pro_add(?,?,?)";
//得到callableStatement對象
CallableStatement cbs = connection.prepareCall(sql);
//設置輸入?yún)?shù),和preparedStatement一樣
cbs.setInt(1, 3);
cbs.setString(2, "數(shù)學");
/*那么如何設置輸出參數(shù)呢?
* 需要注冊輸出參數(shù)!
*/
cbs.registerOutParameter(3, java.sql.Types.INTEGER);//需要使用內(nèi)置對象來設置參數(shù)類型
//執(zhí)行SQL語句
cbs.executeQuery();
//利用getXXX方法得到相應位置的輸出參數(shù)
Integer num= cbs.getInt(3);
System.out.println("a*4 is " + num);
//關(guān)閉資源
sqlUtil.close(cbs, connection);
}
希望本文所述對大家的Java程序設計有所幫助。