前言
將最近學(xué)習(xí)的JDBC編程整理成筆記,做次記錄。
mysql 安裝
首先安裝mysql 8.0.19 版本數(shù)據(jù)庫(kù)。
下載地址 https://downloads.mysql.com/archives/community/
下載完成后點(diǎn)擊默認(rèn)安裝即可。


默認(rèn)安裝到了C:\Program Files\MySQL\MySQL Server 8.0\bin目錄下。
關(guān)于mysql的一些命令:
mysqld.exe --initialize-insecure 初始化數(shù)據(jù)庫(kù),執(zhí)行后會(huì)生成data文件夾
mysqld.exe --install 安裝mysql服務(wù)
net start mysql 啟動(dòng)mysql服務(wù)
set password for root@localhost = password('password'); 修改密碼
flush privileges 刷新權(quán)限
JDBC 簡(jiǎn)介
JDBC全稱是Java DataBase Connectivity的縮寫,是Java程序訪問(wèn)數(shù)據(jù)庫(kù)的標(biāo)準(zhǔn)接口。
使用java里面提供的一些類和方法,利用程序鏈接數(shù)據(jù)庫(kù),進(jìn)行增刪改查,這個(gè)過(guò)程叫JDBC編程。
要實(shí)現(xiàn)JDBC編程,除了java.sql標(biāo)準(zhǔn)庫(kù),還需要找一個(gè)MySQL的JDBC驅(qū)動(dòng)。其實(shí)就是一個(gè)第三方j(luò)ar包。
這里下載mysql-connector-java-8.0.23 版本的jar包。 https://dev.mysql.com/downloads/connector/j/
下載后在eclipse 引入jar包。

并添加到項(xiàng)目庫(kù)。

JDBC 編程
JDBC編程主要分為六步操作:
- 加載驅(qū)動(dòng)程序
- 連接數(shù)據(jù)庫(kù)
- 獲取數(shù)據(jù)庫(kù)操作對(duì)象
- 執(zhí)行sql語(yǔ)句
- 處理查詢結(jié)果集
- 釋放連接
舉個(gè)栗子:
實(shí)現(xiàn)最簡(jiǎn)單的查詢語(yǔ)句
package jdbc_test;
import java.sql.*;
public class jdbcdemo01 {
/**
*
* @param username
* @param password
* @throws SQLException
*/
public static void main(String[] args) throws SQLException {
Connection conn=null;
Statement stmt=null;
ResultSet rs=null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");//加載驅(qū)動(dòng)
String url = "jdbc:mysql://localhost:3306/web?useUnicode=true&characterEncoding=UTF8&userSSL=true";
String user = "root";
String pass = "";
conn = DriverManager.getConnection(url,user,pass);//獲取連接
stmt = conn.createStatement();//獲取數(shù)據(jù)庫(kù)操作對(duì)象
String sql = "select * from user";
rs = stmt.executeQuery(sql);//執(zhí)行sql語(yǔ)句
while(rs.next()) {
System.out.print(rs.getInt(1)+","+rs.getString("username")+","+rs.getString(3)+","+rs.getString(4));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//釋放資源
rs.close();
stmt.close();
conn.close();
}
}
}
注意:url地址的完整度,執(zhí)行查詢語(yǔ)句用的是executeQuery方法,以及驅(qū)動(dòng)連接用的是Class.forName()
還有一種方式也可以實(shí)現(xiàn)。
DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());// 第二種
但new com.mysql.cj.jdbc.Driver()創(chuàng)建對(duì)象的時(shí)候本質(zhì)上也執(zhí)行了DriverManager.registerDriver(driver),這樣一來(lái)就重復(fù)了,所以我們更習(xí)慣使用Class.forName()。
第二個(gè)例子,更新語(yǔ)句
package jdbc_test;
import java.sql.*;
public class jdbcdemo01 {
/**
*
* @param username
* @param password
* @throws SQLException
*/
public static void main(String[] args) throws SQLException {
Connection conn=null;
Statement stmt=null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");//加載驅(qū)動(dòng)
String url = "jdbc:mysql://localhost:3306/web?useUnicode=true&characterEncoding=UTF8&userSSL=true";
String user = "root";
String pass = "";
conn = DriverManager.getConnection(url,user,pass);//獲取連接
stmt = conn.createStatement();//獲取數(shù)據(jù)庫(kù)操作對(duì)象
String sql = "update user set loginpwd=123456789 where id=1";
int count = stmt.executeUpdate(sql);//執(zhí)行sql語(yǔ)句
System.out.println(count == 1 ? "更新成功" : "更新失敗");
} catch (Exception e) {
e.printStackTrace();
} finally {
//釋放資源
stmt.close();
conn.close();
}
}
}
注意:更新方法用的是executeUpdate,包括插入、刪除、更新,并返回一個(gè)int值,所以自然也沒(méi)有查詢記過(guò)集
區(qū)分一下這兩個(gè)方法。
int executeUpdate(insert/delete/update)
Resultset executeQuery(select)
PreparedStatement 對(duì)象
在了解PreparedStatement對(duì)象之前,先設(shè)計(jì)一個(gè)登錄的demo,并嘗試一種工具類提取的方式編寫代碼。
首先新建db.properties文件,提取出連接數(shù)據(jù)庫(kù)的信息。
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/web?useUnicode=true&characterEncoding=UTF8&userSSL=true
username=root
password=
再提取出jdbc連接工具類JdbcUtils.java
package jdbc_test;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.*;
// 提取工具類
public class JdbcUtils {
private static String driver = null;
private static String url = null;
private static String username = null;
private static String password = null;
static {
InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
Properties properties = new Properties();
try {
properties.load(in);
driver = properties.getProperty("driver");
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
// 加載驅(qū)動(dòng)
Class.forName(driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
// 獲取連接
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url,username,password);
}
// 釋放連接資源
public static void release(Connection conn,Statement st,ResultSet rs) {
if(rs!=null) {try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}}
if(st!=null) {try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}}
if(conn!=null) {try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}}
}
}
最后再編寫jdbcDemo3.java,來(lái)實(shí)現(xiàn)用戶登錄。
jdbcDemo3.java
package jdbc_test;
import java.sql.*;
import java.util.*;
public class jdbcdemo3 {
public static void main(String[] args) {
Map userlogininfo = loginit();
String loginName = (String) userlogininfo.get("username");
String loginPwd = (String) userlogininfo.get("password");
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
stmt = conn.createStatement();
String sql = "select * from user where loginName='"+loginName+"' and loginPwd='"+loginPwd+"' ";
rs = stmt.executeQuery(sql);
if(rs.next()) {
System.out.print("login success");
}else {
System.out.print("login false");
}
}catch (Exception e) {
e.printStackTrace();
}finally {
JdbcUtils.release(conn, stmt, rs);
}
}
private static Map loginit() {
Scanner s = new Scanner(System.in);
System.out.print("username:");
String username = s.nextLine();
System.out.print("password:");
String password = s.nextLine();
Map userlogininfo = new HashMap();
userlogininfo.put("username",username);
userlogininfo.put("password",password);
return userlogininfo;
}
}
嘗試登錄。

但是該編寫方式存在sql注入問(wèn)題,當(dāng)用戶輸入特殊的用戶名、密碼時(shí),也可以登錄成功。
如a' or 1='1

這是因?yàn)檎嬷?or 真值 and 假值 or 真值,結(jié)果即為真值。會(huì)查詢出所有結(jié)果。

那么該如何解決sql注入的問(wèn)題呢?這就有了PreparedStatement 對(duì)象。
該對(duì)象用來(lái)防止sql注入,是Statement的子類,可對(duì)sql進(jìn)行預(yù)編譯,從而提高數(shù)據(jù)庫(kù)的執(zhí)行效率。
修改jdbcDemo3.java
package jdbc_test;
import java.sql.*;
import java.util.*;
public class jdbcdemo3 {
public static void main(String[] args) {
Map userlogininfo = loginit();
String loginName = (String) userlogininfo.get("username");
String loginPwd = (String) userlogininfo.get("password");
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
String sql = "select * from user where loginName = ? and loginPwd= ? ";
//?代表占位符,不能使用單引號(hào)括起來(lái)
stmt = conn.prepareStatement(sql);//sql語(yǔ)句的預(yù)先編譯
//再給占位符傳值,即使再注入,sql語(yǔ)句也沒(méi)有編譯
stmt.setString(1, loginName);
stmt.setString(2, loginPwd);
rs = stmt.executeQuery();//執(zhí)行sql語(yǔ)句
if(rs.next()) {
System.out.print("login success");
}else {
System.out.print("login false");
}
}catch (Exception e) {
e.printStackTrace();
}finally {
JdbcUtils.release(conn, stmt, rs);
}
}
private static Map loginit() {
Scanner s = new Scanner(System.in);
System.out.print("username:");
String username = s.nextLine();
System.out.print("password:");
String password = s.nextLine();
Map userlogininfo = new HashMap();
userlogininfo.put("username",username);
userlogininfo.put("password",password);
return userlogininfo;
}
}
注意:無(wú)法sql注入就是因?yàn)镻reparedStatement 對(duì)象在執(zhí)行sql語(yǔ)句時(shí)先進(jìn)行了預(yù)編譯

總結(jié)
學(xué)習(xí)了JDBC的基本過(guò)程,復(fù)習(xí)了db.properties文件、代碼層的sql注入,蠻有收獲,繼續(xù)加油。