1、連接數據庫時的配置文件
##數據庫驅動
driver=com.mysql.jdbc.Driver
##MySQL連接信息
url=jdbc:mysql://127.0.0.1:3306/RUNOOB?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=GMT
##用戶名
username=root
##密碼
password=admin0001112
2、JDBC原生方式連接數據庫工具類
package com.wind.ssm.utils;
import org.springframework.stereotype.Component;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
/**
* @Date: 2020/7/3 17:10
* @Description: 原生JDBC連接數據庫
*/
/**
* 事務:一組操作,要麼都成功,要麼都失敗
* ACID原則:
* (1)原子性:一組操作,要麼全都執行完成,要麼都不完成。
* (2)一致性:這裏是說最終一致性,總數保持不變。
* (3)隔離性:多個事務之間相互隔離,互不干擾(多個進程互不干擾)。
* (4)持久性:事務一旦提交之後,就持久化到數據庫了,不能夠再去修改。
* 隔離性的級別會帶來一些問題:
* (1)髒讀:一個事務讀取到了另一個事務還沒有提交的數據
* (2)不可重複度:在同一個事務內,重複讀取表中的某個數據,前後兩次讀取到的數據不一致(數據被更新了,是update操作)。
* (3)幻讀:在一個事務內,讀取到了另一個事務插入的數據,導致前後兩次讀出來的結果不一致(記錄數增加了或者減少了,是insert或delete操作)。
*/
@Component
public class JdbcUtils {
private static String driver = null;
private static String url = null;
private static String username = null;
private static String password = null;
static {
try {
InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
Properties properties = new Properties();
properties.load(inputStream);
driver = properties.getProperty("driver");
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
//1.加載數據庫驅動(只需要加載一次即可)
Class.forName(driver);
} catch (Exception e) {
e.printStackTrace();
}
}
//2.獲取數據庫連接對象
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, username, password);
}
//3.釋放數據庫連接資源
public static void release(ResultSet resultSet, Statement statement, Connection connection) {
try {
if (resultSet != null && !resultSet.isClosed()) {
resultSet.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (statement != null && !statement.isClosed()) {
statement.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (connection != null && !connection.isClosed()) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
3、測試PreparedStatement對象
package com.wind.ssm.java.test;
import com.wind.ssm.entity.StudentEntity;
import com.wind.ssm.utils.JdbcUtils;
import org.springframework.stereotype.Component;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
/**
* @Date: 2020/7/3 18:40
* @Description:
*/
@Component
public class JdbcUtilsTest2 {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null; //是Statement的子類
ResultSet resultSet = null;
try {
//1.獲取數據庫連接
connection = JdbcUtils.getConnection();
//2.【新增SQL】參數使用佔位符而不是直接使用實際值hardcode
String insertSql = "insert into RUN_Student(Name,ClassId,Status,Addtime,UpdateTime) values(?,?,?,?,?)";
//3.預編譯SQL,此時並不執行SQL
preparedStatement = connection.prepareStatement(insertSql);
//4.給SQL中的佔位符設置具體值
preparedStatement.setString(1, "南京市");
preparedStatement.setInt(2, 2);
preparedStatement.setInt(3, 1);
preparedStatement.setDate(4, new java.sql.Date(new Date().getTime()));
preparedStatement.setDate(5, new java.sql.Date(new Date().getTime()));
//4.執行SQL,並且獲取結果。"查"executeQuery(),"增刪改"executeUpdate()。
int insertRes = preparedStatement.executeUpdate();
//5.處理結果
if (insertRes > 0) {
System.out.println("插入DB記錄成功");
}
//【更新SQL】
String updateSql = "update RUN_Student set name = ? where id = ?";
preparedStatement = connection.prepareStatement(updateSql);
preparedStatement.setString(1, "南京市玄武區");
preparedStatement.setInt(2, 19);
int updateRes = preparedStatement.executeUpdate();
if (updateRes > 0) {
System.out.println("更新DB記錄成功");
}
//【刪除SQL】
String deleteSql = "delete from RUN_Student where id = ?";
preparedStatement = connection.prepareStatement(deleteSql);
preparedStatement.setInt(1, 19);
int deleteRes = preparedStatement.executeUpdate();
if (deleteRes > 0) {
System.out.println("刪除DB記錄成功");
}
//【查詢SQL】
String selectSql = "select * from RUN_Student where id in (?,?)";
preparedStatement = connection.prepareStatement(selectSql);
preparedStatement.setInt(1, 1);
preparedStatement.setInt(2, 2);
resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
StudentEntity entity = new StudentEntity();
entity.setId(resultSet.getInt("Id"));
entity.setName(resultSet.getString("Name"));
entity.setClassId(resultSet.getInt("ClassId"));
entity.setStatus(resultSet.getInt("Status"));
entity.setAddTime(resultSet.getDate("AddTime"));
entity.setUpdateTime(resultSet.getDate("UpdateTime"));
System.out.println("查詢DB記錄成功=entity=" + entity.toString());
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.release(resultSet, preparedStatement, connection);
}
}
}
4、PreparedStatement對象與Statement對象的區別
本人的幾點淺見,各位大大不喜勿噴(前面兩篇博客已經做了對應的測試)。
先說下這倆到底是幹啥的吧。其實這倆乾的活兒都一樣,就是創建了一個執行對象,然後通過對象調用executeQuery方法來執行sql語句。說是CreateStatement和PrepareStatement的區別,但其實說的就是Statement和PrepareStatement的區別,相信大家在網上已經看到過不少這方面的資料和博客,我在此處提幾點,大家看到過的,就當是重新記憶,沒看到就當補充~下面開始談談它們的區別。
(1)最明顯的區別,就是執行的sql語句格式不同(是否存在佔位符)。我們放兩段代碼來看看他們的區別:
代碼背景:我們有一個數據庫,裏面有一個user表,有username, userpwd兩列。我們要查出這兩列的數據。
這是使用CreateStatement方法創建了stmt 對象,再通過他查詢的一部分語句片段。
String sql = "select * from users where username= '"+username+"' and userpwd='"+userpwd+"'";
stmt = conn.createStatement();
rs = stmt.executeQuery(sql);
而下面則是使用了PrepareStatement方法創建了pstmt 對象,再通過這個對象查詢的一部分語句片段。
String sql = "select * from users where username=? and userpwd=?";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, username);
pstmt.setString(2, userpwd);
rs = pstmt.executeQuery();
相信寫到這,大家很多人就能看出來了,原來PrepareStatement跟Statement的主要區別:就是把上面sql語句中的變量抽出來了。這就是我要說的第一大優點,PrepareStatement可以提高代碼的可讀性。什麼?你沒覺得這有什麼可以提高可讀性的?那好,咱來看看下面這兩段代碼,看完你再說話。
代碼背景:我們有一個數據庫,裏面有一個book表,有bookid, bookname, bookauthor, booksort, bookprice五列。我們要向這個表中添加一部分數據。
【Statement版】
String sql = "insert into book (bookid,bookname,bookauthor,booksort,bookprice) values ('"+var1+"',
'"+var2+"',"+var3+",'"+var4+","+var5+"')";
stmt = conn.createStatement();
rs = stmt.executeUpdate(sql);
【ParperStatement版】
String sql = "insert into book (bookid,bookname,bookauthor,booksort,bookprice) values (?,?,?,?,?)";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1,var1);
pstmt.setString(2,var2);
pstmt.setString(3,var3);
pstmt.setString(4,var4);
pstmt.setString(5,var5);
pstmt.executeUpdate();
怎麼樣,反正我打這行代碼的時候,整個引號逗號就給我刺激懵了。
(2)下面說說第二點優點,ParperStatement提高了代碼的靈活性和執行效率。
PrepareStatement接口是Statement接口的子接口,它繼承了Statement接口的所有功能。它主要是拿來解決我們使用Statement對象多次執行同一個SQL語句的效率問題的。ParperStatement接口的機制是在數據庫支持預編譯的情況下 預先將SQL語句編譯,當多次執行這條SQL語句時,可以直接執行編譯好的SQL語句,這樣就大大提高了程序的靈活性和執行效率。
(3)最後但也是最重要的一個大大的比Statement好的優點,那就是安全,可以防止SQL注入!
你說啥?這還關安全啥事兒,那我給你一行代碼,你來給我說說這是幹嘛的。
String sql = "select * from user where username= '"+varname+"' and userpwd='"+varpasswd+"'";
stmt = conn.createStatement();
rs = stmt.executeUpdate(sql);
這是驗證用戶名密碼的,對吧。但要是我們把 'or '1' = 1' 當作密碼傳進去,你猜猜會發生啥。
select * from user where username = 'user' and userpwd = '' or '1' = '1';
發現了吧!這是個永真式,因爲1永遠等於1。所以不管怎樣都能獲取到權限。哇。這就壞咯!這還不是最壞的,你再看!
String sql = "select * from user where username= '"+varname+"' and userpwd='"+varpasswd+"'";
stmt = conn.createStatement();
rs = stmt.executeUpdate(sql);
依舊是這行代碼。這次我們把 'or '1' = 1';drop table book; 當成密碼傳進去。哇!又壞了!這次直接把表給刪了。但是,你如果用PrepareStatement的話就不會出現這種問題,因爲它是把入參直接當做字符串來處理的,你傳入的這些數據根本不會跟原來的數據有任何的交集,也不會發生這些問題。文章寫到這就結束了。
轉自:https://blog.csdn.net/u011161786/article/details/48394751