目錄
5)reperPreparedStatement:也是執行sql對象,但比Statement功能強大
1.JDBC的概念
JDBC=Java DataBase Connectivity,使用同樣的Java代碼操作不同的數據庫。
本質:是官方定義的一套操作關係型數據庫的規則,即接口。各個數據庫廠商去實現這套接口,提供數據庫驅動的jar包。我們可以使用這套接口(JDBC)編程,真正執行的代碼是驅動jar包中的實現類。
2.JDBC編碼步驟
- 導入驅動jar包 (此處相關jar包見https://blog.csdn.net/weixin_44187963/article/details/104864821)
- 編寫代碼註冊驅動
- 獲取數據庫的連接對象 connection
- 定義SQL語句
- 獲取SQL語句對象 statement
- 執行SQL,接收返回結果
- 處理返回結果
- 釋放資源
public class JdbcDome01 {
public static void main(String[] args) throws Exception {
//1.導入JAR包
//2.註冊驅動,在mysql5版本之後可以省略這個步驟,因爲jar包裏配置,系統回會自動註冊驅動
Class.forName("com.mysql.cj.jdbc.Driver");
//3.獲取數據庫的連接對象
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/TestDB?serverTimezone=UTC","root","123456");
//4.定義sql語句
String sql = "update person set id = 161004 where id = 4";
//5.獲取執行sql的對象 statement
Statement stat = conn.createStatement();
//6.執行sql
int info = stat.executeUpdate(sql);
//7.處理結果
System.out.println(info);
//8.釋放資源
conn.close();
stat.close();
}
}
當然,這塊只是梳理流程,並沒有去抓取異常。而且關閉資源的代碼理應在異常處理的finally塊中,纔會不論前面是否異常最終都會執行到finally,這樣以免產生內存的泄露。所以代碼可以改爲:
public class JdbcDome03 {
public static void main(String[] args) {
Connection con = null;
Statement sta = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
con = DriverManager.getConnection("jdbc:mysql://localhost:3306/TestDB?serverTimezone=UTC","root","123456");
String sql = "update person set id = 161005 where id = 5";//修改表sql
sta = con.createStatement();
int count = sta.executeUpdate(sql);
if(count > 1){
System.out.println("修改成功");
}else{
System.out.println("修改失敗");
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if(sta != null){
try {
sta.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(con != null){
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
這樣一看,很明顯每一個操作都是在重複獲取連接、關閉資源等基礎操作,所以這塊我們可以在utils包封裝一個工具類出來,至於工具類的使用放在後面實現登錄的代碼裏:
/**
* JDBC工具類要有兩個方法,一是註冊驅動獲取鏈接,二是釋放資源
* 注意:工具類的方法一般都是靜態方法
*/
public class JdbcUtils {
private static String url;
private static String user;
private static String password;
private static String driver;
//讀取配置文件:期望文件的讀取只讀取一次,而靜態代碼塊隨着類的加載而加載,所以用靜態代碼塊讀取配置文件中的資源
static{
Properties properties = new Properties();
try {
//獲取src下文件路徑---->Class.loader 類加載器
ClassLoader classLoader = JdbcUtils.class.getClassLoader();
URL res = classLoader.getResource("Jdbc.properties");
String path = res.getPath();
//加載文件
properties.load(new FileReader(path));
//獲取屬性,賦值
url = properties.getProperty("url");
user = properties.getProperty("user");
password = properties.getProperty("password");
driver = properties.getProperty("Driver");
//加載驅動
Class.forName(driver);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//獲取連接
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url,user,password);
}
//釋放資源,因爲有兩種情況,所以寫兩個方法實現方法的重載
public static void release(Statement state,Connection conn){
if(state != null){
try {
state.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void release(ResultSet res,Statement state,Connection conn){
if(res != null){
try {
res.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(state != null){
try {
state.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
3.詳解各個對象
1)DriverManager:驅動管理對象
1. 註冊驅動 static void registerDriver(Driver driver)
註冊與給定的驅動程序DriverManager
但我們代碼中用Class.forName("com.mysql.cj.jdbc.Driver");
有什麼關係呢?
通過源碼發現在com.mysql.cj.jdbc.Driver下有個靜態代碼塊,代碼塊裏使用了registerDriver
2. 獲取數據庫連接 static Connection getConnection(String url,Stirng user,String password)
這裏url = jdbc:mysql://IP:端口號/數據庫名
2)Connection:數據庫連接對象
1.獲取執行sql的對象 Statement createStatement();
PrepareStatement prepareStatement(String sql);
2.管理事務
開啓事務:setAutoCommit(boolean autuCommit);參數設置爲false
提交事務:commit();
回滾事務:rollbacl();
3)Statement:執行sql的對象
1. boolean execute(String sql); 可以執行任意的sql
2. int executeUpdate(String sql);
執行DML(insert,update,delete)語句、DLL(表的create、drop、alter)語句,返回值是影響的行數。
3. ResultSet executeQuery(String sql); 執行DQL(select)語句
這塊代碼就不貼出來了,就是注意不同的sql語句後面執行的時候調用不同的方法就行了。
4)ResultSet:結果集對象,封裝返回結果的
next(); 遊標向下移動一行,最開始默認指向的是標題欄,所以就有下面代碼中的res.next()
getXxx(參數); 獲取返回值,比如int getInt()
get方法的參數有兩種情況:
int代表列的編號,從1開始,比如getString(1) 獲取第一列的值;
String代表列的名稱,比如getString("name")
使用步驟:
1.遊標向下移動一行
2.判斷是否有數據
3.獲取數據
此時熟悉前面這部分的使用,我們寫一個實現登錄邏輯的代碼,其中數據庫的連接及關閉資源都使用了工具類:
/**
* 需求:通過鍵盤輸入用戶名和密碼
* 判斷用戶是否登錄成功
*/
public class JdbcLogin {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("請輸入用戶名:");
String name = sc.next();
System.out.println("請輸入密碼:");
String password = sc.next();
boolean flag = new JdbcLogin().logIn2(name, password);
if(flag){
System.out.println("登陸成功!");
}else{
System.out.println("登陸失敗...");
}
}
//登錄方法
public boolean logIn(String usename,String password){
//連接數據庫判斷是否登陸成功
Connection connection = null;
Statement statement = null;
ResultSet res = null;
if(usename == null || password == null){
return false;
}
try {
connection = JdbcUtils.getConnection();
String sql = "select * from user where username = '"+usename+"' and password = '"+password+"'";
statement = connection.createStatement();
res = statement.executeQuery(sql);
return res.next();//返回的就是一個Boolean類型值,代替了if語句
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(res,statement,connection);
}
return false;
}
}
5)reperPreparedStatement:也是執行sql對象,但比Statement功能強大
1.SQL注入問題:在拼接sql時會有sql的特殊關鍵字參與字符串的拼接,會造成安全性問題
2.解決SQL注入問題:使用PreparedStatement對象來解決
3.預編譯SQL:參數使用?作爲佔位符
4.使用步驟在sql語句時:
1)用?作爲佔位符寫sql語句
2)在PreparedStatement(sql)方法中要傳入sql作爲參數
3)給佔位符?賦值:setXxx(參數1,參數2);
參數1---?的位置編號(從1開始)
參數2---?的值
4)在下一步執行sql接收返回結果時就不需要傳遞sql參數了
之前寫的sql語句是靜態sql,用reperPreparedStatement獲取執行對象後是這樣的:對比上一個登錄實現代碼來理解:
public class JdbcLogin {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("請輸入用戶名:");
String name = sc.next();
System.out.println("請輸入密碼:");
String password = sc.next();
boolean flag = new JdbcLogin().logIn2(name, password);
if(flag){
System.out.println("登陸成功!");
}else{
System.out.println("登陸失敗...");
}
}
/**
* 使用preparedstatement的登錄方法的實現
*/
public boolean logIn2(String username,String password){
//連接數據庫判斷是否登陸成功
Connection connection = null;
PreparedStatement statement = null;
ResultSet res = null;
if(username == null || password == null){
return false;
}
try {
connection = JdbcUtils.getConnection();
String sql = "select * from user where username = ? and password = ?";
statement = connection.prepareStatement(sql);//此處傳參
//注意給?賦值
statement.setString(1,"username");
statement.setString(2,"password");
res = statement.executeQuery();//此處不需要傳參
return res.next();
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(res,statement,connection);
}
return false;
}
}