一、JDBC定義
JDBC(Java DataBase Connectivity)是Java和數據庫之間的一個橋樑,是一個規範而不是一個實現,能夠執行SQL語句。它由一組用 Java 語言編寫的類和接口組成。各種不同類型的數據庫都有相應的實現。
二、JDBC編程步驟
2.1 裝載相應數據庫的JDBC驅動並進行初始化
2.1.1 導入專用的jar包(不同的數據庫需要的jar包不同)
訪問MySQL數據庫需要用到第三方的類,這些第三方的類,都被壓縮在一個 .jar 的文件裏。mysql-connector-java-5.0.8-bin.jar 包可以在網上下載,或者在MySQL的安裝目錄下找到。通常下載到該 jar 包之後將其放到在項目的 lib 目錄下,在本例就會放在 E:\project\j2se\lib 這個位置,然後在 eclipse 中導入這個 jar 包。
導包步驟: 右鍵 project -> property -> java build path -> libaries -> add external jars。
如果沒有完成上述步驟的導包操作,後面會拋出 ClassNotFoundException。
2.1.2 初始化驅動
通過初始化驅動類 com.mysql.jdbc.Driver,該類就在 mysql-connector-java-5.0.8-bin.jar 中。如果你使用的是 oracle 數據庫那麼該驅動類將不同。
Class.forName() 是把這個類加載到JVM中,加載的時候,就會執行其中的靜態初始化塊,完成驅動的初始化的相關工作。
注意:Class.forName() 需要捕獲 ClassNotFoundException。
Class.forName() 的具體用法及作用請見 https://blog.csdn.net/fengyuzhengfan/article/details/38086743
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
2.2 建立JDBC和數據庫之間的Connection連接
這裏需要提供:
- 數據庫服務端的IP地址:127.0.0.1或 localhost (這是本機,如果連接其他電腦上的數據庫,需填寫相應的IP地址)
- 數據庫的端口號: 3306 (mysql專用端口號)
- 數據庫名稱 exam(根據你自己數據庫中的名稱填寫)
- 編碼方式 UTF-8
- 賬號 root
- 密碼 admin(如果你在創建數據庫的時候沒有使用默認的賬號和密碼,請填寫自己設置的賬號和密碼)
Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/exam?characterEncoding=UTF-8", "root", "admin");
Connection 是與特定數據庫連接回話的接口,使用的時候需要導包,而且必須在程序結束的時候將其關閉。getConnection 方法也需要捕獲 SQLException 異常。
因爲在進行數據庫的增刪改查的時候都需要與數據庫建立連接,所以可以在項目中將建立連接寫成一個工具方法,用的時候直接調用即可:
/**
* 取得數據庫的連接
* @return 一個數據庫的連接
*/
public static Connection getConnection(){
Connection conn = null;
try {
//初始化驅動類com.mysql.jdbc.Driver
//該類就在 mysql-connector-java-5.0.8-bin.jar中,如果忘記了第一個步驟的導包,就會拋出ClassNotFoundException
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/exam?characterEncoding=UTF-8","root", "admin");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
2.3 創建Statement或者PreparedStatement接口,執行SQL語句
2.3.1 使用Statement接口
Statement 接口創建之後,可以執行SQL語句,完成對數據庫的增刪改查。其中 ,增刪改只需要改變SQL語句的內容就能完成,然而查詢略顯複雜。在 Statement 中使用字符串拼接的方式,該方式存在句法複雜,容易犯錯等缺點,具體在下文中的對比中介紹。所以 Statement 在實際過程中使用的非常的少。
字符串拼接方式的SQL語句是非常繁瑣的,中間有很多的單引號和雙引號的混用,極易出錯。
Statement s = conn.createStatement();
// 準備sql語句
// 注意: 字符串要用單引號'
String sql = "insert into t_courses values(null,"+"'數學')";
//在statement中使用字符串拼接的方式,這種方式存在諸多問題
s.execute(sql);
System.out.println("執行插入語句成功");
2.3.2 使用PreparedStatement接口
與 Statement 一樣,PreparedStatement也是用來執行sql語句的與創建 Statement 不同的是,需要根據sql語句創建 PreparedStatement。除此之外,還能夠通過設置參數,指定相應的值,而不是 Statement 那樣使用字符串拼接。
使用PreparedStatement時,他的SQL語句不再採用字符串拼接的方式,而是採用佔位符的方式。“?”在這裏就起到佔位符的作用。這種方式除了避免了statement拼接字符串的繁瑣之外,還能夠提高性能。每次SQL語句都是一樣的,java類就不會再次編譯,這樣能夠顯著提高性能。
後面需要用到PreparedStatement接口創建的pstmt的set方法給佔位符進行賦值。注意一點,這裏的參數索引是從1開始的。
2.3.3 Statement和PreparedStatement的異同及優缺點
同:兩者都是用來執SQL語句的
異:PreparedStatement需要根據SQL語句來創建,它能夠通過設置參數,指定相應的值,不是像Statement那樣使用字符串拼接的方式。
PreparedStatement的優點:
- 其使用參數設置,可讀性好,不易記錯。在statement中使用字符串拼接,可讀性和維護性比較差。
- 其具有預編譯機制,性能比statement更快。
- 其能夠有效防止SQL注入攻擊。
2.3.4 execute和executeUpdate的區別
同:二者都能夠執行增加、刪除、修改等操作。
異:
-
execute 可以執行查詢語句,然後通過 getResult 把結果取出來。executeUpdate 不能執行查詢語句。
-
execute 返回 Boolean 類型,true 表示執行的是查詢語句,false 表示執行的 insert、delete、update 等。executeUpdate 的返回值是 int,表示有多少條數據受到了影響。
2.3.5 舉例
舉例1:給數據庫中添加課程:(以下代碼中最後關閉資源的兩個方法 DbUtil.close(pstmt); DbUtil.close(conn); 和上面的建立連接的方法是一樣的,是在工具類中定義了的關閉方法,下文會給出其代碼)
/**
* 添加課程
* @param courseName 課程名稱
*/
public void addCourse(String courseName){
String sql = "insert into t_course(course_name) values(?)";
//該語句爲每個 IN 參數保留一個問號(“?”)作爲佔位符
Connection conn = null; //和數據庫取得連接
PreparedStatement pstmt = null; //創建statement
try{
conn = DbUtil.getConnection();
pstmt = (PreparedStatement) conn.prepareStatement(sql);
pstmt.setString(1, courseName); //給佔位符賦值
pstmt.executeUpdate(); //執行
}catch(SQLException e){
e.printStackTrace();
}
finally{
DbUtil.close(pstmt);
DbUtil.close(conn); //必須關閉
}
}
舉例2:對數據庫中的課程進行刪除:
/**
* 刪除課程
* @param courseId
*/
public void delCourse(int courseId){
String sql = "delete from t_course where course_id = ?";
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = DbUtil.getConnection();
pstmt = (PreparedStatement) conn.prepareStatement(sql);
pstmt.setInt(1, courseId);
pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}finally{
DbUtil.close(pstmt);
DbUtil.close(conn); //必須關閉
}
}
舉例3:對數據庫中的課程進行修改:
/**
* 修改課程
* @param courseId
* @param courseName
*/
public void modifyCourse(int courseId,String courseName){
String sql = "update t_course set course_name =? where course_id=?";
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = DbUtil.getConnection();
pstmt = (PreparedStatement) conn.prepareStatement(sql);
pstmt.setString(1, courseName); //利用Preparedstatement的set方法給佔位符賦值
pstmt.setInt(2, courseId);
pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}finally{
DbUtil.close(pstmt);
DbUtil.close(conn); //必須關閉
}
}
舉例4:對數據庫中的課程進行查詢:
/**
* 查詢課程
* @return
*/
public List<Course> findCourseList(){
String sql = "select * from t_course order by course_id";
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
//創建一個集合對象用來存放查詢到的數據
List<Course> courseList = new ArrayList<>();
try {
conn = DbUtil.getConnection();
pstmt = (PreparedStatement) conn.prepareStatement(sql);
rs = (ResultSet) pstmt.executeQuery();
while (rs.next()){
int courseId = rs.getInt("course_id");
String courseName = rs.getString("course_name");
//每個記錄對應一個對象
Course course = new Course();
course.setCourseId(courseId);
course.setCourseName(courseName);
//將對象放到集合中
courseList.add(course);
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
DbUtil.close(pstmt);
DbUtil.close(conn); //必須關閉
}
return courseList;
}
2.3.3 批量更新SQL操作
在添加的過程的,如果添加的數據量比較大的話,可以用批量添加。 PreparedStatement 接口提供了相應的批量操作的方法。
示例:
for(int i=1; i<100; i++){
pstmt.setInt(1, 8000 + i);
pstmt.setString(2, "趙_" + i);
pstmt.addBatch();
//批量更新
if(i%10 == 0){
pstmt.executeBatch();
}
}
具體操作及其優勢見 https://www.cnblogs.com/husam/p/3830225.html
2.4 處理和顯示結果
執行查詢語句,並把結果集返回給集合 ResultSet。
ResultSet rs = s.executeQuery();
利用 While(ResultSet.next()){…} 循環將集合ResultSet中的結果遍歷出來。
ResultSet.getXX(); 這裏的get方法的括號裏面可以填屬性值,如下圖代碼中的course_id,還可以填該屬性在數據表中的列號,從1開始編碼,例如:course_id 在我的查詢結果數據表中位於第一列,所以執行 get 方法的時候,我除了代碼段中寫法外,還可以這樣寫 int courseId = rs.getInt(1); 但是不推薦使用列號的這種方式,因爲一段數據表中個屬性值得順序發生變化,就會導致這裏出錯,而使用屬性名則不會出現這樣的問題。
while (rs.next()){
int courseId = rs.getInt("course_id");
String courseName = rs.getString("course_name");
//每個記錄對應一個對象
Course course = new Course();
//在我的項目中創建了course類,其中定義了set方法,所以這裏將查詢到的值傳給了course,也可以直接打印到控制檯
course.setCourseId(courseId);
course.setCourseName(courseName);
//將對象放到集合中
courseList.add(course);
}
也可以直接用打印語句將 CourseId 和 CourseName 打印到控制檯。
2.5 釋放資源
在JDBC編碼的過程中我們創建了 Connection、ResultSet 等資源,這些資源在使用完畢之後是一定要進行關閉的。關閉的過程中遵循從裏到外的原則。因爲在增刪改查的操作中都要用到這樣的關閉操作,爲了使代碼簡單,增加其複用性,這裏我將這些關閉的操作寫成一個方法和建立連接的方法一起放到一份工具類中。
/**
* 封裝三個關閉方法
* @param pstmt
*/
public static void close(PreparedStatement pstmt){
if(pstmt != null){ //避免出現空指針異常
try{
pstmt.close();
}catch(SQLException e){
e.printStackTrace();
}
}
}
public static void close(Connection conn){
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(ResultSet rs){
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
JDBC編程的內容就這些了,如果你已經全部掌握,還有事務、獲取自增、獲取元數據、ORM、DAO、數據連接池等內容可以自行了解一下。
本文大部分爲CSDN博主「Jungle_Rao」的原創文章,原文鏈接:https://blog.csdn.net/jungle_rao/article/details/81274720