JDBC
什么是JDBC
使用java代碼發(fā)送sql語句的技術(shù)绞愚,就是jdbc技術(shù)朱巨。
使用jdbc的前提
- 需要登錄數(shù)據(jù)庫服務(wù)器
- 需要知道數(shù)據(jù)庫的IP地址
- 需要知道數(shù)據(jù)庫服務(wù)器的端口號
- 需要知道數(shù)據(jù)庫的用戶名
- 需要知道數(shù)據(jù)庫密碼
數(shù)據(jù)庫連接
- 先確定連接數(shù)據(jù)庫的URL史翘,
- 然后使用Driver注冊驅(qū)動,
- 注冊驅(qū)動之后然后再去連接數(shù)據(jù)庫
具體實現(xiàn)代碼如下:
第一種連接方式:
//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);
第二種方式:
使用驅(qū)動管理器類連接數(shù)據(jù)庫(注冊了兩次琼讽,沒必要)
Driver driver = new com.mysql.jdbc.Driver();
//Driver driver2 = new com.oracle.jdbc.Driver();
//1.注冊驅(qū)動程序(可以注冊多個驅(qū)動程序)
DriverManager.registerDriver(driver);
//DriverManager.registerDriver(driver2);
//2.連接到具體的數(shù)據(jù)庫
Connection conn = DriverManager.getConnection(url, user, password);
System.out.println(conn);
第三種方式:
推薦使用加載驅(qū)動程序類 來 注冊驅(qū)動程序
//Driver driver = new com.mysql.jdbc.Driver();
//通過得到字節(jié)碼對象的方式加載靜態(tài)代碼塊,從而注冊驅(qū)動程序
Class.forName("com.mysql.jdbc.Driver");
//Driver driver2 = new com.oracle.jdbc.Driver();
//1.注冊驅(qū)動程序(可以注冊多個驅(qū)動程序)
//DriverManager.registerDriver(driver);
//DriverManager.registerDriver(driver2);
//2.連接到具體的數(shù)據(jù)庫
Connection conn = DriverManager.getConnection(url, user, password);
System.out.println(conn);
Statement的執(zhí)行過程
2017010939872Statment.png
JDBC工具類
通常我們在項目中可以創(chuàng)建一個工具類管理JDBC的工具類洪唐,這樣就省去我們寫一些重復(fù)代碼了钻蹬,在這個工具類里面我們可以提供兩個方法,一個獲取連接凭需,一個關(guān)閉问欠。
我們弄一個Properties
肝匆,把數(shù)據(jù)庫的url,用戶名溅潜,密碼都放到這里面术唬,然后通過輸入流把它給讀取出來。讀取的操作我們把它放到static
代碼塊里面滚澜,這樣的話就會只加載一次粗仓,
具體實現(xiàn)如下:
private static String url = null;
private static String user = null;
private static String password = null;
private static String driverClass = null;
/**
* 靜態(tài)代碼塊中(只加載一次)
*/
static{
try {
//讀取db.properties文件
Properties props = new Properties();
/**
* . 代表java命令運行的目錄
* 在java項目下,. java命令的運行目錄從項目的根目錄開始
* 在web項目下设捐, . java命令的而運行目錄從tomcat/bin目錄開始
* 所以不能使用點.
*/
//FileInputStream in = new FileInputStream("./src/db.properties");
/**
* 使用類路徑的讀取方式
* / : 斜杠表示classpath的根目錄
* 在java項目下借浊,classpath的根目錄從bin目錄開始
* 在web項目下,classpath的根目錄從WEB-INF/classes目錄開始
*/
InputStream in = JdbcUtil.class.getResourceAsStream("/db.properties");
//加載文件
props.load(in);
//讀取信息
url = props.getProperty("url");
user = props.getProperty("user");
password = props.getProperty("password");
driverClass = props.getProperty("driverClass");
//注冊驅(qū)動程序
Class.forName(driverClass);
} catch (Exception e) {
e.printStackTrace();
System.out.println("驅(qū)程程序注冊出錯");
}
}
/**
* 抽取獲取連接對象的方法
*/
public static Connection getConnection(){
try {
Connection conn = DriverManager.getConnection(url, user, password);
return conn;
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
/**
* 釋放資源的方法
*/
public static void close(Connection conn,Statement stmt){
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);
}
}
}
public static void close(Connection conn,Statement stmt,ResultSet rs){
if(rs!=null)
try {
rs.close();
} catch (SQLException e1) {
e1.printStackTrace();
throw new RuntimeException(e1);
}
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);
}
}
}
ResulSet
表示數(shù)據(jù)庫結(jié)果集的數(shù)據(jù)表萝招,通常通過執(zhí)行查詢數(shù)據(jù)庫的語句生成蚂斤。
ResultSet 對象具有指向其當(dāng)前數(shù)據(jù)行的光標。最初槐沼,光標被置于第一行之前曙蒸。next 方法將光標移動到下一行;因為該方法在 ResultSet 對象沒有下一行時返回 false岗钩,所以可以在 while 循環(huán)中使用它來迭代結(jié)果集纽窟。
默認的 ResultSet 對象不可更新,僅有一個向前移動的光標兼吓。因此臂港,只能迭代它一次,并且只能按從第一行到最后一行的順序進行视搏∩竽酰可以生成可滾動和/或可更新的 ResultSet 對象。以下代碼片段(其中 con 為有效的 Connection 對象)演示了如何生成可滾動且不受其他更新影響的可更新結(jié)果集浑娜。有關(guān)其他選項佑力,請參見 ResultSet 字段。
Statement stmt = con.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE);
ResultSet rs = stmt.executeQuery("SELECT a, b FROM TABLE2");
// rs will be scrollable, will not show changes made by others,
// and will be updatable
ResultSet 接口提供用于從當(dāng)前行獲取列值的 獲取 方法( getBoolean筋遭、 getLong 等)搓萧。可以使用列的索引編號或列的名稱獲取值宛畦。一般情況下瘸洛,使用列索引較為高效。列從 1 開始編號次和。為了獲得最大的可移植性反肋,應(yīng)該按從左到右的順序讀取每行中的結(jié)果集列,每列只能讀取一次踏施。
對于獲取方法石蔗,JDBC 驅(qū)動程序嘗試將底層數(shù)據(jù)轉(zhuǎn)換為在獲取方法中指定的 Java 類型罕邀,并返回適當(dāng)?shù)?Java 值。JDBC 規(guī)范有一個表养距,顯示允許的從 SQL 類型到 ResultSet 獲取方法所使用的 Java 類型的映射關(guān)系诉探。
用作獲取方法的輸入的列名稱不區(qū)分大小寫。用列名稱調(diào)用獲取方法時棍厌,如果多個列具有這一名稱肾胯,則返回第一個匹配列的值。在生成結(jié)果集的 SQL 查詢中使用列名稱時耘纱,將使用列名稱選項敬肚。對于沒有在查詢中顯式指定的列,最好使用列編號束析。如果使用列名稱艳馒,則程序員應(yīng)該注意保證名稱唯一引用預(yù)期的列,這可以使用 SQL AS 子句確定员寇。
在 JDBC 2.0 API(JavaTM 2 SDK 標準版 1.2 版)中弄慰,此接口添加了一組更新方法。關(guān)于獲取方法參數(shù)的注釋同樣適用于更新方法的參數(shù)蝶锋。
可以用以下兩種方式使用更新方法:
更新當(dāng)前行中的列值曹动。在可滾動的 ResultSet 對象中,可以向前和向后移動光標牲览,將其置于絕對位置或相對于當(dāng)前行的位置。以下代碼片段更新 ResultSet 對象 rs 第五行中的 NAME 列恶守,然后使用方法 updateRow 更新導(dǎo)出 rs 的數(shù)據(jù)源表第献。
rs.absolute(5); // moves the cursor to the fifth row of rs
rs.updateString("NAME", "AINSWORTH"); // updates the
// NAME column of row 5 to be AINSWORTH
rs.updateRow(); // updates the row in the data source
將列值插入到插入行中⊥酶郏可更新的 ResultSet 對象具有一個與其關(guān)聯(lián)的特殊行庸毫,該行用作構(gòu)建要插入的行的暫存區(qū)域 (staging area)。以下代碼片段將光標移動到插入行衫樊,構(gòu)建一個三列的行飒赃,并使用方法 insertRow 將其插入到 rs 和數(shù)據(jù)源表中。
rs.moveToInsertRow(); // moves cursor to the insert row
rs.updateString(1, "AINSWORTH"); // updates the
// first column of the insert row to be AINSWORTH
rs.updateInt(2,35); // updates the second column to be 35
rs.updateBoolean(3, true); // updates the third column to true
rs.insertRow();
rs.moveToCurrentRow();
使用JDBC執(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);
}
}
}
使用JDBC執(zhí)行DML語句
- 增加
@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);
}
}
使用JDBC執(zhí)行DQL語句(查詢操作)
我們一般都是使用executeQuery
來獲取ResultSet
,ResultSet
的話我們可以通過列
或者是字段名稱
來獲取數(shù)據(jù)
代碼如下:
@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);
}
}
使用預(yù)編譯執(zhí)行sql語句
預(yù)編譯執(zhí)行相對于靜態(tài)的安全性比較高臀栈,避免sql注入蔫慧,提高了安全性,
- 增加
@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);
}
}
使用Statement實現(xiàn)sql注入:
//模擬用戶輸入
//private String name = "ericdfdfdfddfd' OR 1=1 -- ";
private String name = "eric";
//private String password = "123456dfdfddfdf";
private String password = "123456";
/**
* Statment存在sql被注入的風(fēng)險
*/
@Test
public void testByStatement(){
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
//獲取連接
conn = JdbcUtil.getConnection();
//創(chuàng)建Statment
stmt = conn.createStatement();
//準備sql
String sql = "SELECT * FROM users WHERE NAME='"+name+"' AND PASSWORD='"+password+"'";
//執(zhí)行sql
rs = stmt.executeQuery(sql);
if(rs.next()){
//登錄成功
System.out.println("登錄成功");
}else{
System.out.println("登錄失敗");
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
} finally {
JdbcUtil.close(conn, stmt ,rs);
}
}
/**
* PreparedStatement可以有效地防止sql被注入
*/
@Test
public void testByPreparedStatement(){
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
//獲取連接
conn = JdbcUtil.getConnection();
String sql = "SELECT * FROM users WHERE NAME=? AND PASSWORD=?";
//預(yù)編譯
stmt = conn.prepareStatement(sql);
//設(shè)置參數(shù)
stmt.setString(1, name);
stmt.setString(2, password);
//執(zhí)行sql
rs = stmt.executeQuery();
if(rs.next()){
//登錄成功
System.out.println("登錄成功");
}else{
System.out.println("登錄失敗");
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
} finally {
JdbcUtil.close(conn, stmt ,rs);
}
}
使用CablleStatement調(diào)用存儲過程
/**
* 調(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í)行!J蛭觥卖怜!
//遍歷結(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);
}
}