JDBC
1. Jdbc概述
問題:實際開發中,不可能用工具或者命令行操作數據庫,數據庫表中的數據最終要使用Java程序來操作,那么Java中如何操作數據庫中的數據呢?
答 : 在Java語言中,有一個專門連接數據庫的規范(JDBC),專門負責連接數據庫進行數據操作的規范
JDBC只是SUN編寫的一堆接口(規范的體現),SUN公司自己並沒有實現
問題 : 為什么SUN只定義一個JDBC規范,而不實現呢?
答 : 因為市面上的數據庫很多,每個數據庫內部接口不會向外暴露,而且即便是暴露讓SUN去實現,市面上很多數據庫全部要SUN來實現不現實
實際中哪個數據庫需要支持JAVA語言,就需要自己實現Java的JDBC規范,因為實現了JDBC很多接口,那么就會有很多實現類,而很多實現類在java中會使用一個專門的包封裝起來,叫做jar包(在JDBC中叫做驅動包),各大數據庫產商實現JDBC規范以后都會把他們jar包放在官網上以供開發者下載使用
1.1. JDBC
JDBC(Java DataBase Connectivity):
是一種用於執行SQL語句的Java API,可以為多種關系數據庫提供統一訪問,它由一組用Java語言編寫的類和接口組成。JDBC提供了一種基
JDBC規范對應的api包
2.java連接數據庫
以連接mysql為例
2.1. 創建普通java項目
2.2. 在項目下面新建一個lib目錄
2.3. 將MySQL驅動包拷貝到項目中並添加依賴
2.5 獲取數據庫連接對象
准備:
1.拷貝MySQL的驅動包到項目中去:mysql-connector-java-5.1.x-bin.jar
2.build path,告速項目去哪里去找字節碼文件.
--------------------------------------------------------------------------------
操作JDBC的第一步,獲取JDBC的連接對象.:Connection.
步驟:
1.加載注冊驅動.
就是把驅動中的Driver字節碼加載到JVM中.
Class.forName("com.mysql.jdbc.Driver");
為什么這句話就可以加載注冊驅動?
第一步:把com.mysql.jdbc.Driver.class這份字節碼加載到JVM中.
第二步:當一份字節碼被加載進JVM,馬上就會執行該字節碼中的靜態代碼塊.
第三步:該靜態代碼中,就在完成,先創建驅動對象,再注冊.
2.通過DriverManager獲取連接對象.
public static Connection getConnection(String url,String user,String password)
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/dbName","root","admin");
jdbc:mysql://localhost:3306/dbName
jdbc:mysql:// :連接MySQL數據庫的協議,不同數據庫協議不一樣
localhost:3306 :數據庫軟件的主機和端口
dbName : 具體要連接數據庫
若數據庫安裝在本機,並且端口是默認的3306,則可以簡寫:
Connection conn = DriverManager.getConnection("jdbc:mysql:///dbName","root","admin");
驗證已經獲取連接:可以在MySQL控制台,使用命令:show processlist; 查看MySQL運行進程.
1 public class GetConnectionDemo { 2 public static void main(String[] args) throws Exception { 3 4 //1.加載注冊驅動 : 把當前類對應的字節碼加載到JVM中 5 Class.forName("com.mysql.jdbc.Driver"); 6 //2.獲取數據庫連接 7 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); 8 System.out.println(conn); 9 10 } 11 }
3. 創建表-DDL操作
在其他操作之間先要把數據庫表要創建出來
/賈璉欲執事
創建一張t_student表:
id:
name:
age:
1 //DML : 對表數據的增刪改操作 2 public class DMLDemo { 3 4 /* 5 * 向 t_student表中插入一條數據 6 * sql : insert into t_student(name,age) values ('喬峰',30) 7 */ 8 @Test 9 public void testInsert() throws Exception { 10 String sql = "insert into t_student(name,age) values ('喬峰',30)"; 11 12 // 1.加載注冊驅動 13 Class.forName("com.mysql.jdbc.Driver"); 14 15 // 2.獲取數據庫連接對象 16 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); 17 // 3.創建語句對象 18 Statement st = conn.createStatement(); 19 20 // 4.執行SQL語句 21 // int rows = st.executeUpdate(String sql);執行DDL和DML語句,放回的是受影響的行數 22 // ResultSet res = st.executeQuery(String sql);執行DQL查詢語句,返回的結果集對象 23 int rows = st.executeUpdate(sql); 24 System.out.println(rows); 25 //5.釋放資源(先開后關) 26 st.close(); 27 conn.close(); 28 29 } 30 /* 31 * 刪除操作: 刪除t_student表中的某一條數據 32 * SQL :delete from t_student where id = 2 33 */ 34 @Test 35 public void testDelete() throws Exception { 36 String sql = "delete from t_student where id = 2"; 37 38 // 1.加載注冊驅動 39 Class.forName("com.mysql.jdbc.Driver"); 40 41 // 2.獲取數據庫連接對象 42 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); 43 // 3.創建語句對象 44 Statement st = conn.createStatement(); 45 46 // 4.執行SQL語句 47 // int rows = st.executeUpdate(String sql);執行DDL和DML語句,放回的是受影響的行數 48 // ResultSet res = st.executeQuery(String sql);執行DQL查詢語句,返回的結果集對象 49 int rows = st.executeUpdate(sql); 50 System.out.println(rows); 51 //5.釋放資源(先開后關) 52 st.close(); 53 conn.close(); 54 } 55 /* 56 * 修改操作 : 修改t_student表中的某一條數據 57 * SQL : update t_student set name = '虛竹',age = 50 where id = 3 58 */ 59 @Test 60 public void testUpdate() throws Exception { 61 String sql = "update t_student set name = '虛竹',age = 50 where id = 3"; 62 63 // 1.加載注冊驅動 64 Class.forName("com.mysql.jdbc.Driver"); 65 66 // 2.獲取數據庫連接對象 67 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); 68 // 3.創建語句對象 69 Statement st = conn.createStatement(); 70 71 // 4.執行SQL語句 72 // int rows = st.executeUpdate(String sql);執行DDL和DML語句,放回的是受影響的行數 73 // ResultSet res = st.executeQuery(String sql);執行DQL查詢語句,返回的結果集對象 74 int rows = st.executeUpdate(sql); 75 System.out.println(rows); 76 //5.釋放資源(先開后關) 77 st.close(); 78 conn.close(); 79 } 80 }
4.DML操作-表數據的增刪改
1 //DML : 對表數據的增刪改操作 2 public class DMLDemo { 3 4 /* 5 * 向 t_student表中插入一條數據 6 * sql : insert into t_student(name,age) values ('喬峰',30) 7 */ 8 @Test 9 public void testInsert() throws Exception { 10 String sql = "insert into t_student(name,age) values ('喬峰',30)"; 11 12 // 1.加載注冊驅動 13 Class.forName("com.mysql.jdbc.Driver"); 14 15 // 2.獲取數據庫連接對象 16 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); 17 // 3.創建語句對象 18 Statement st = conn.createStatement(); 19 20 // 4.執行SQL語句 21 // int rows = st.executeUpdate(String sql);執行DDL和DML語句,放回的是受影響的行數 22 // ResultSet res = st.executeQuery(String sql);執行DQL查詢語句,返回的結果集對象 23 int rows = st.executeUpdate(sql); 24 System.out.println(rows); 25 //5.釋放資源(先開后關) 26 st.close(); 27 conn.close(); 28 29 } 30 /* 31 * 刪除操作: 刪除t_student表中的某一條數據 32 * SQL :delete from t_student where id = 2 33 */ 34 @Test 35 public void testDelete() throws Exception { 36 String sql = "delete from t_student where id = 2"; 37 38 // 1.加載注冊驅動 39 Class.forName("com.mysql.jdbc.Driver"); 40 41 // 2.獲取數據庫連接對象 42 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); 43 // 3.創建語句對象 44 Statement st = conn.createStatement(); 45 46 // 4.執行SQL語句 47 // int rows = st.executeUpdate(String sql);執行DDL和DML語句,放回的是受影響的行數 48 // ResultSet res = st.executeQuery(String sql);執行DQL查詢語句,返回的結果集對象 49 int rows = st.executeUpdate(sql); 50 System.out.println(rows); 51 //5.釋放資源(先開后關) 52 st.close(); 53 conn.close(); 54 } 55 /* 56 * 修改操作 : 修改t_student表中的某一條數據 57 * SQL : update t_student set name = '虛竹',age = 50 where id = 3 58 */ 59 @Test 60 public void testUpdate() throws Exception { 61 String sql = "update t_student set name = '虛竹',age = 50 where id = 3"; 62 63 // 1.加載注冊驅動 64 Class.forName("com.mysql.jdbc.Driver"); 65 66 // 2.獲取數據庫連接對象 67 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); 68 // 3.創建語句對象 69 Statement st = conn.createStatement(); 70 71 // 4.執行SQL語句 72 // int rows = st.executeUpdate(String sql);執行DDL和DML語句,放回的是受影響的行數 73 // ResultSet res = st.executeQuery(String sql);執行DQL查詢語句,返回的結果集對象 74 int rows = st.executeUpdate(sql); 75 System.out.println(rows); 76 //5.釋放資源(先開后關) 77 st.close(); 78 conn.close(); 79 } 80 }
5. DQL操作-查詢操作
5.1. 查詢操作的分析
5.2 查詢具體操作
結果集的列的位置
1.使用 rs.next() 偏移光標,循環指定具體的某一行
獲取數據的具體方法
getObject(int columnIndex) |
|
getObject(String columnLabel)
|
1 package cn.sxt.jdbc._01connection; 2 3 4 import java.sql.Connection; 5 import java.sql.DriverManager; 6 import java.sql.ResultSet; 7 import java.sql.Statement; 8 import java.util.ArrayList; 9 import java.util.List; 10 11 import org.junit.Test; 12 13 //DQL :查詢操作 14 public class D_DQLDemo { 15 16 /* 17 * 多行查詢 :查詢t_student表中的所有數據 18 * SQL : select * from t_student 19 */ 20 @Test 21 public void testList() throws Exception { 22 String sql = "select * from t_student"; 23 24 // 1.加載注冊驅動 25 Class.forName("com.mysql.jdbc.Driver"); 26 27 // 2.獲取數據庫連接對象 28 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); 29 // 3.創建語句對象 30 Statement st = conn.createStatement(); 31 32 // 4.執行SQL語句 33 // int rows = st.executeUpdate(String sql);執行DDL和DML語句,放回的是受影響的行數 34 // ResultSet res = st.executeQuery(String sql);執行DQL查詢語句,返回的結果集對象 35 ResultSet rs = st.executeQuery(sql); 36 37 38 //創建list集合用於封裝Student對象 39 List<Student> stus = new ArrayList<>(); 40 41 while(rs.next()) { 42 //1.通過結果集的位置獲取對應的數 43 /*Object id = rs.getObject(1); 44 Object name = rs.getObject(2); 45 Object age = rs.getObject(3);*/ 46 47 //2.通過結果集的 列名獲取對應的數據 48 /*Object id = rs.getObject("id"); 49 Object name = rs.getObject("name"); 50 Object age = rs.getObject("age");*/ 51 //3.通過數據庫數據和Java對應的數據類型獲取對應的只 52 int id = rs.getInt("id"); 53 String name = rs.getString("name"); 54 int age = rs.getInt("age"); 55 //System.out.println(id+","+name+","+age); 56 57 //將獲取的數據封裝成對應的Student對象 58 Student stu = new Student(id, name, age); 59 60 //將一個個Student對象添加到list集合中 61 stus.add(stu); 62 } 63 64 for (Student student : stus) { 65 System.out.println(student); 66 } 67 //5.釋放資源(先開后關) 68 rs.close(); 69 st.close(); 70 conn.close(); 71 } 72 73 74 /* 75 * 單行查詢: 查詢出t_student 指定id的信息 76 * SQL : select * from t_student where id = 1; 77 */ 78 @Test 79 public void testGetOne() throws Exception { 80 String sql = "select * from t_student where id = 2"; 81 82 // 1.加載注冊驅動 83 Class.forName("com.mysql.jdbc.Driver"); 84 85 // 2.獲取數據庫連接對象 86 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); 87 // 3.創建語句對象 88 Statement st = conn.createStatement(); 89 90 // 4.執行SQL語句 91 // int rows = st.executeUpdate(String sql);執行DDL和DML語句,放回的是受影響的行數 92 // ResultSet res = st.executeQuery(String sql);執行DQL查詢語句,返回的結果集對象 93 ResultSet rs = st.executeQuery(sql); 94 95 if(rs.next()) { 96 //1.通過結果集的位置獲取對應的數 97 /*Object id = rs.getObject(1); 98 Object name = rs.getObject(2); 99 Object age = rs.getObject(3);*/ 100 101 //2.通過結果集的 列名獲取對應的數據 102 /*Object id = rs.getObject("id"); 103 Object name = rs.getObject("name"); 104 Object age = rs.getObject("age");*/ 105 //3.通過數據庫數據和Java對應的數據類型獲取對應的只 106 int id = rs.getInt("id"); 107 String name = rs.getString("name"); 108 int age = rs.getInt("age"); 109 //System.out.println(id+","+name+","+age); 110 111 //將獲取的數據封裝成對應的Student對象 112 Student stu = new Student(id, name, age); 113 System.out.println(stu); 114 } 115 //5.釋放資源(先開后關) 116 rs.close(); 117 st.close(); 118 conn.close(); 119 } 120 }
6. 預編譯語句對象PreparedStatment
問題 : 我們有了Statment對象可以執行SQL,為什么還要使用PreparedStatment?
優勢
- SQL語句結構清晰,參數的設置和SQL語句分離
- 性能更高
- 防止SQL注入
Statement: 表示靜態SQL語句對象. PreparedStatement:Statement的子接口,表示預編譯SQL語句對象. 通過占位符(?)來拼SQL. |
6.1 創建PreparedStatement
創建語句對象 Statment
createStatement() |
創建預編譯語句對象PreparedStatement
prepareStatement(String sql) |
||
6.2. 執行SQL語句的方法
6.2.1. Statment
在執行SQL語句的時候回帶上SQL語句
executeQuery(String sql) |
|
int |
executeUpdate(String sql) |
6.2.2. PreparedStatement
在執行SQL語句的方法中不需要設置SQL語句
executeQuery() |
|
int |
executeUpdate() |
6.3. 設置站位參數的值
void setXxx(int parameterIndex,Xxx value):用於設置占位符參數, parameterIndex:第幾個問號. 注意:從1開始. value:設置的真實值. Xxx:表示數據類型.String/int/long/Double/Date |
6.4. 代碼
package cn.sxt.jdbc._01connection;
import static org.junit.Assert.*;
import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement;
import org.junit.Test;
//DML : 對表數據的增刪改操作,使用預編譯語句對象 public class E_DMLByPreparedStatmentDemo {
/* * 向 t_student表中插入一條數據 * sql : insert into t_student(name,age) values ('喬峰',30) */ @Test public void testInsert() throws Exception { String sql = "insert into t_student(name,age) values (?,?)";
// 1.加載注冊驅動 Class.forName("com.mysql.jdbc.Driver");
// 2.獲取數據庫連接對象 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); // 3.創建預編譯語句對象 PreparedStatement ps = conn.prepareStatement(sql); //3.1設置占位符參數 ps.setString(1, "東方姑娘"); ps.setInt(2, 18);
// 4.執行SQL語句:注意不要帶SQL參數 ps.executeUpdate(); //5.釋放資源(先開后關) ps.close(); conn.close();
} /* * 刪除操作: 刪除t_student表中的某一條數據 * SQL :delete from t_student where id = 2 */ @Test public void testDelete() throws Exception { String sql = "delete from t_student where id = ?";
// 1.加載注冊驅動 Class.forName("com.mysql.jdbc.Driver");
// 2.獲取數據庫連接對象 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); // 3.創建預編譯語句對象 PreparedStatement ps = conn.prepareStatement(sql); //3.1設置占位符對應的參數值 ps.setInt(1, 1);
// 4.執行SQL語句 int rows = ps.executeUpdate(); System.out.println(rows); //5.釋放資源(先開后關) ps.close(); conn.close(); } /* * 修改操作 : 修改t_student表中的某一條數據 * SQL : update t_student set name = '虛竹',age = 50 where id = 3 */ @Test public void testUpdate() throws Exception { String sql = "update t_student set name = ?,age = ? where id = ?";
// 1.加載注冊驅動 Class.forName("com.mysql.jdbc.Driver");
// 2.獲取數據庫連接對象 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); // 3.創建預編譯語句對象 PreparedStatement ps = conn.prepareStatement(sql); //3.1設置占位符參數對應的值 ps.setString(1, "西方失敗"); ps.setInt(2, 40); ps.setInt(3, 4); // 4.執行SQL語句 int rows = ps.executeUpdate(); System.out.println(rows); //5.釋放資源(先開后關) ps.close(); conn.close(); } }
|
7. JavaWeb開發的分層設計-三層架構
7.1. DAO層設計
實際開發中,JavaWeb開發代碼一般分為三層,分層結構是JavaWeb開發中的一種設計思想,這樣會讓我們開發層次分明,每一層只要完成對應的功能即可,使得項目便於開發和維護
1 . Web層/表現層 : 主要接受前台瀏覽器用戶的參數,給瀏覽器響應數據等等
- Service層/業務成/服務層:主要處理業務功能,日志,權限,事物,等等
- DAO層/持久層 :專門負責和數據庫交互,數據處理相關代碼
DAO : Data Access Object 數據訪問對象
實際開發中 : 用戶請求到-Web層--->Service層-->DAO層
7.2. DAO思想
|
7.3. 使用DAO以后代碼的以及包的設計結構
開發中如果使用的分層,編寫的包和類名接口名等等都是有固定規則,不能隨便瞎寫
7.3.1. DAO層接口包命名
公司域名倒寫+項目名稱/模塊名稱+dao 如 : cn.sxt.crm.dao |
7.3.2. DAO層實現類包命名
公司域名倒寫+項目名稱/模塊名稱+dao+impl 如 : cn.sxt.crm.dao.impl |
7.3.3. DAO層操作對應表的接口命名
對應表的名稱 + Dao/DAO
如 : StudentDao/DAO , TeacherDao/DAO |
7.3.4. DAO層操作對應表的實現類命名
對應表的名稱 + Dao/DAOImpl
如 : StudentDaoImpl/DAOImpl , TeacherDaoImpl/DAOImpl |
7.3.5. 數據表對應的Java類domain/pojo包命名
POJO(Plain Ordinary Java Object)簡單的Java對象
domian : 域對象
公司域名倒寫+項目名稱/模塊名稱+domain/pojo 如 : cn.sxt.crm.domain |
7.3.6. 對應的測試包命名
公司域名倒寫+項目名稱/模塊名稱+test 如 : cn.sxt.crm.test |
7.3.7. 項目的工具類包命名
公司域名倒寫+項目名稱/模塊名稱+util/utils 如 : cn.sxt.crm.util/utils |
7.3.8. DAO代碼設計結構
7.3.9. Dao的實現類代碼
1 package cn.sxt.jdbc.dao.impl; 2 3 import java.sql.Connection; 4 import java.sql.DriverManager; 5 import java.sql.PreparedStatement; 6 import java.sql.ResultSet; 7 import java.sql.SQLException; 8 import java.util.ArrayList; 9 import java.util.List; 10 11 import cn.sxt.jdbc.dao.StudentDao; 12 import cn.sxt.jdbc.domain.Student; 13 14 public class StudentDaoImpl implements StudentDao { 15 16 @Override 17 public int saveStudent(Student stu) { 18 String sql = "insert into t_student(name,age) values (?,?)"; 19 20 Connection conn = null; 21 PreparedStatement ps = null; 22 try { 23 24 // 1.加載注冊驅動 25 Class.forName("com.mysql.jdbc.Driver"); 26 27 // 2.獲取數據庫連接對象 28 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); 29 // 3.創建預編譯語句對象 30 ps = conn.prepareStatement(sql); 31 //3.1設置占位符參數 32 ps.setString(1, stu.getName()); 33 ps.setInt(2, stu.getAge()); 34 35 // 4.執行SQL語句:注意不要帶SQL參數 36 return ps.executeUpdate(); 37 38 39 } catch (Exception e) { 40 e.printStackTrace(); 41 }finally { 42 //5.釋放資源(先開后關) 43 try { 44 if(ps !=null) { 45 ps.close(); 46 } 47 } catch (SQLException e) { 48 e.printStackTrace(); 49 }finally { 50 try { 51 if(conn !=null) { 52 conn.close(); 53 } 54 } catch (SQLException e) { 55 // TODO Auto-generated catch block 56 e.printStackTrace(); 57 } 58 } 59 } 60 return 0; 61 } 62 63 @Override 64 public int deleteById(int id) { 65 66 String sql = "delete from t_student where id = ?"; 67 68 Connection conn = null; 69 PreparedStatement ps = null; 70 try { 71 72 // 1.加載注冊驅動 73 Class.forName("com.mysql.jdbc.Driver"); 74 75 // 2.獲取數據庫連接對象 76 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); 77 // 3.創建預編譯語句對象 78 ps = conn.prepareStatement(sql); 79 //3.1設置占位符參數 80 ps.setInt(1, id); 81 82 // 4.執行SQL語句:注意不要帶SQL參數 83 return ps.executeUpdate(); 84 85 86 } catch (Exception e) { 87 e.printStackTrace(); 88 }finally { 89 //5.釋放資源(先開后關) 90 try { 91 if(ps !=null) { 92 ps.close(); 93 } 94 } catch (SQLException e) { 95 e.printStackTrace(); 96 }finally { 97 try { 98 if(conn !=null) { 99 conn.close(); 100 } 101 } catch (SQLException e) { 102 // TODO Auto-generated catch block 103 e.printStackTrace(); 104 } 105 } 106 } 107 return 0; 108 } 109 110 @Override 111 public int updateStudentById(Student stu) { 112 113 String sql = "update t_student set name = ?,age = ? where id = ?"; 114 115 Connection conn = null; 116 PreparedStatement ps = null; 117 try { 118 119 // 1.加載注冊驅動 120 Class.forName("com.mysql.jdbc.Driver"); 121 122 // 2.獲取數據庫連接對象 123 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); 124 // 3.創建預編譯語句對象 125 ps = conn.prepareStatement(sql); 126 //3.1設置占位符參數 127 ps.setString(1, stu.getName()); 128 ps.setInt(2, stu.getAge()); 129 ps.setInt(3, stu.getId()); 130 // 4.執行SQL語句:注意不要帶SQL參數 131 return ps.executeUpdate(); 132 133 134 } catch (Exception e) { 135 e.printStackTrace(); 136 }finally { 137 //5.釋放資源(先開后關) 138 try { 139 if(ps !=null) { 140 ps.close(); 141 } 142 } catch (SQLException e) { 143 e.printStackTrace(); 144 }finally { 145 try { 146 if(conn !=null) { 147 conn.close(); 148 } 149 } catch (SQLException e) { 150 // TODO Auto-generated catch block 151 e.printStackTrace(); 152 } 153 } 154 } 155 return 0; 156 } 157 158 @Override 159 public Student selectById(int id) { 160 String sql = "select * from t_student where id = ?"; 161 162 Connection conn = null; 163 PreparedStatement ps = null; 164 ResultSet rs = null; 165 166 try { 167 // 1.加載注冊驅動 168 Class.forName("com.mysql.jdbc.Driver"); 169 170 // 2.獲取數據庫連接對象 171 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); 172 // 3.創建語句對象 173 ps = conn.prepareStatement(sql); 174 //3.1設置占位符參數對應的值 175 ps.setInt(1, id); 176 177 // 4.執行SQL語句 178 rs = ps.executeQuery(); 179 if(rs.next()) { 180 //通過數據庫數據和Java對應的數據類型獲取對應的只 181 String name = rs.getString("name"); 182 int age = rs.getInt("age"); 183 //System.out.println(id+","+name+","+age); 184 185 //將獲取的數據封裝成對應的Student對象 186 Student stu = new Student(id, name, age); 187 188 return stu; 189 } 190 191 } catch (Exception e) { 192 // TODO: handle exception 193 }finally { 194 try { 195 if(rs !=null) { 196 rs.close(); 197 } 198 } catch (SQLException e) { 199 e.printStackTrace(); 200 }finally { 201 try { 202 if(ps !=null) { 203 ps.close(); 204 } 205 } catch (SQLException e) { 206 e.printStackTrace(); 207 }finally { 208 try { 209 if(conn !=null) { 210 conn.close(); 211 } 212 } catch (SQLException e) { 213 e.printStackTrace(); 214 } 215 } 216 } 217 } 218 219 return null; 220 } 221 222 @Override 223 public List<Student> selectList() { 224 String sql = "select * from t_student"; 225 //創建list集合用於封裝Student對象 226 List<Student> stus = new ArrayList<>(); 227 228 Connection conn = null; 229 PreparedStatement ps = null; 230 ResultSet rs = null; 231 232 try { 233 234 // 1.加載注冊驅動 235 Class.forName("com.mysql.jdbc.Driver"); 236 237 // 2.獲取數據庫連接對象 238 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "root"); 239 // 3.創建語句對象 240 ps = conn.prepareStatement(sql); 241 242 // 4.執行SQL語句 243 rs = ps.executeQuery(); 244 while(rs.next()) { 245 //通過數據庫數據和Java對應的數據類型獲取對應的只 246 int id = rs.getInt("id"); 247 String name = rs.getString("name"); 248 int age = rs.getInt("age"); 249 //System.out.println(id+","+name+","+age); 250 251 //將獲取的數據封裝成對應的Student對象 252 Student stu = new Student(id, name, age); 253 //將一個個Student對象添加到list集合中 254 stus.add(stu); 255 } 256 257 } catch (Exception e) { 258 // TODO: handle exception 259 }finally { 260 try { 261 if(rs !=null) { 262 rs.close(); 263 } 264 } catch (SQLException e) { 265 e.printStackTrace(); 266 }finally { 267 try { 268 if(ps !=null) { 269 ps.close(); 270 } 271 } catch (SQLException e) { 272 e.printStackTrace(); 273 }finally { 274 try { 275 if(conn !=null) { 276 conn.close(); 277 } 278 } catch (SQLException e) { 279 e.printStackTrace(); 280 } 281 } 282 } 283 } 284 285 return stus; 286 } 287 288 }
7.3.10. 快速生成單元測試類
一個dao層或者service編寫代碼以后,需要為每一個功能都進行單元測試,一個dao中的方法很多。我們快速為這個dao層的類生成單元測試類,(dao的每一個方法都自動生成一個測試方法)
|
|
|
|
7.4. 代碼初步重構
上述的DAO方法中的代碼,存在的問題:
問題1:每個DAO方法中都會寫:驅動名稱/url/賬號/密碼,不利於維護.
解決方案: 聲明為成員變量即可.(在被類中任何地方都可以訪問)
問題2:問題1的解決方案有問題.
每個DAO實現類里都有一模一樣的4行代碼,不利於維護(考慮有100個DAO實現類,就得重復99次).
解決方案: 把驅動名稱/url/賬號/密碼這四行代碼,專門抽取到一個JDBC的工具類中.---->JdbcUtil.
問題3:其實DAO方法,每次操作都只想需要Connection對象即可,而不關心是如何創建的.
解決方案:把創建Connection的代碼,抽取到JdbcUtil中,並提供方法getConn用於向調用者返回Connection對象即可.
問題4:每次調用者調用getConn方法的時候,都會創建一個Connection對象.
但是,每次都會加載注冊驅動一次.--->沒必要的.
解決方案:把加載注冊驅動的代碼放在靜態代碼塊中--->只會在所在類被加載進JVM的時候,執行一次.
問題5:每個DAO方法都要關閉資源.(雞肋代碼).
解決方案:把關閉資源的代碼,抽取到JdbcUtil中.
public static void close(Connection conn, Statement st, ResultSet rs) {}
調用者:
DML: JdbcUtil.close(conn,st,null);
DQL: JdbcUtil.close(conn,st,rs);
問題6 :連接數據庫的賬號密碼寫死在JdbcUtil工具類中了,不利於維護
抽取 db.properties 配置文件,將數據庫對應的賬號密碼寫到配置文件中,然后使用程序讀取配置文件內容即可
7.4.1. JdbcUtil工具類
1 package cn.sxt.jdbc.util; 2 3 import java.io.InputStream; 4 import java.sql.Connection; 5 import java.sql.DriverManager; 6 import java.sql.PreparedStatement; 7 import java.sql.ResultSet; 8 import java.sql.SQLException; 9 import java.util.Properties; 10 11 12 public class JdbcUtil { 13 14 // alt+shif+a 多行修改,修改以后還原 alt+shif+a 15 16 /*private static String driverClassName = "com.mysql.jdbc.Driver"; 17 private static String url = "jdbc:mysql://localhost:3306/jdbcdemo"; 18 private static String username = "root"; 19 private static String password = "root";*/ 20 21 private static Properties p = new Properties(); 22 23 static { 24 try { 25 //1.獲取類加載器 26 ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 27 //2,使用類加載器獲取項目 類路徑下面的文件 28 InputStream inputStream = classLoader.getResourceAsStream("db.properties"); 29 30 //3.使用Priperties加載配置文件對應的輸入流 31 p.load(inputStream); 32 33 Class.forName(p.getProperty("driverClassName")); 34 } catch (Exception e) { 35 e.printStackTrace(); 36 } 37 } 38 39 public static Connection getConnection() { 40 try { 41 42 return DriverManager.getConnection(p.getProperty("url"), p.getProperty("username"), p.getProperty("password")); 43 } catch (Exception e) { 44 e.printStackTrace(); 45 throw new RuntimeException("親,連接數據庫失敗", e); 46 } 47 } 48 49 public static void close(Connection conn,PreparedStatement ps,ResultSet rs) { 50 try { 51 if(rs !=null) { 52 rs.close(); 53 } 54 } catch (SQLException e) { 55 e.printStackTrace(); 56 }finally { 57 try { 58 if(ps !=null) { 59 ps.close(); 60 } 61 } catch (SQLException e) { 62 e.printStackTrace(); 63 }finally { 64 try { 65 if(conn !=null) { 66 conn.close(); 67 } 68 } catch (SQLException e) { 69 e.printStackTrace(); 70 } 71 } 72 } 73 } 74 }
7.4.2. 使用工具類以后的DAO實現類效果
1 package cn.sxt.jdbc.dao.impl; 2 3 import java.sql.Connection; 4 import java.sql.PreparedStatement; 5 import java.sql.ResultSet; 6 import java.sql.SQLException; 7 import java.util.ArrayList; 8 import java.util.List; 9 10 import cn.sxt.jdbc.dao.StudentDao; 11 import cn.sxt.jdbc.domain.Student; 12 import cn.sxt.jdbc.util.JdbcUtil; 13 14 public class StudentDaoImpl implements StudentDao { 15 16 17 18 @Override 19 public int saveStudent(Student stu) { 20 String sql = "insert into t_student(name,age) values (?,?)"; 21 22 Connection conn = null; 23 PreparedStatement ps = null; 24 try { 25 conn = JdbcUtil.getConnection(); 26 27 // 3.創建預編譯語句對象 28 ps = conn.prepareStatement(sql); 29 //3.1設置占位符參數 30 ps.setString(1, stu.getName()); 31 ps.setInt(2, stu.getAge()); 32 33 // 4.執行SQL語句:注意不要帶SQL參數 34 return ps.executeUpdate(); 35 36 } catch (Exception e) { 37 e.printStackTrace(); 38 }finally { 39 JdbcUtil.close(conn, ps, null); 40 } 41 return 0; 42 } 43 44 @Override 45 public int deleteById(int id) { 46 47 String sql = "delete from t_student where id = ?"; 48 49 Connection conn = null; 50 PreparedStatement ps = null; 51 try { 52 53 conn = JdbcUtil.getConnection(); 54 // 3.創建預編譯語句對象 55 ps = conn.prepareStatement(sql); 56 //3.1設置占位符參數 57 ps.setInt(1, id); 58 59 // 4.執行SQL語句:注意不要帶SQL參數 60 return ps.executeUpdate(); 61 62 63 } catch (Exception e) { 64 e.printStackTrace(); 65 }finally { 66 JdbcUtil.close(conn, ps, null); 67 } 68 return 0; 69 } 70 71 @Override 72 public int updateStudentById(Student stu) { 73 74 String sql = "update t_student set name = ?,age = ? where id = ?"; 75 76 Connection conn = null; 77 PreparedStatement ps = null; 78 try { 79 80 conn = JdbcUtil.getConnection(); 81 // 3.創建預編譯語句對象 82 ps = conn.prepareStatement(sql); 83 //3.1設置占位符參數 84 ps.setString(1, stu.getName()); 85 ps.setInt(2, stu.getAge()); 86 ps.setInt(3, stu.getId()); 87 // 4.執行SQL語句:注意不要帶SQL參數 88 return ps.executeUpdate(); 89 90 91 } catch (Exception e) { 92 e.printStackTrace(); 93 }finally { 94 JdbcUtil.close(conn, ps, null); 95 } 96 return 0; 97 } 98 99 @Override 100 public Student selectById(int id) { 101 String sql = "select * from t_student where id = ?"; 102 103 Connection conn = null; 104 PreparedStatement ps = null; 105 ResultSet rs = null; 106 107 try { 108 conn = JdbcUtil.getConnection(); 109 // 3.創建語句對象 110 ps = conn.prepareStatement(sql); 111 //3.1設置占位符參數對應的值 112 ps.setInt(1, id); 113 114 // 4.執行SQL語句 115 rs = ps.executeQuery(); 116 if(rs.next()) { 117 //通過數據庫數據和Java對應的數據類型獲取對應的只 118 String name = rs.getString("name"); 119 int age = rs.getInt("age"); 120 //System.out.println(id+","+name+","+age); 121 //將獲取的數據封裝成對應的Student對象 122 Student stu = new Student(id, name, age); 123 return stu; 124 } 125 126 } catch (Exception e) { 127 // TODO: handle exception 128 }finally { 129 try { 130 if(rs !=null) { 131 rs.close(); 132 } 133 } catch (SQLException e) { 134 e.printStackTrace(); 135 }finally { 136 JdbcUtil.close(conn, ps, rs); 137 } 138 } 139 140 return null; 141 } 142 143 @Override 144 public List<Student> selectList() { 145 String sql = "select * from t_student"; 146 //創建list集合用於封裝Student對象 147 List<Student> stus = new ArrayList<>(); 148 149 Connection conn = null; 150 PreparedStatement ps = null; 151 ResultSet rs = null; 152 153 try { 154 155 conn = JdbcUtil.getConnection(); 156 // 3.創建語句對象 157 ps = conn.prepareStatement(sql); 158 159 // 4.執行SQL語句 160 rs = ps.executeQuery(); 161 while(rs.next()) { 162 //通過數據庫數據和Java對應的數據類型獲取對應的只 163 int id = rs.getInt("id"); 164 String name = rs.getString("name"); 165 int age = rs.getInt("age"); 166 //System.out.println(id+","+name+","+age); 167 168 //將獲取的數據封裝成對應的Student對象 169 Student stu = new Student(id, name, age); 170 //將一個個Student對象添加到list集合中 171 stus.add(stu); 172 } 173 174 } catch (Exception e) { 175 // TODO: handle exception 176 }finally { 177 JdbcUtil.close(conn, ps, rs); 178 } 179 180 return stus; 181 } 182 183 }
7.5. 知識點補充,類加載器
在項目的 類路徑(src)下面創建一個 db.properties配置文件,專門配置連接數據庫的賬號密碼
如何使用類加載器加載配置文件
7.5.1. 配置文件
|
7.5.1.1. 配置文件創建的位置
配置文件一般都放在項目的src 源目錄下面
|
7.5.2. 加載代碼
1 package cn.sxt.jdbc.test; 2 3 import static org.junit.Assert.*; 4 5 import java.io.InputStream; 6 import java.util.Properties; 7 8 import org.junit.Test; 9 10 public class PropertiesTest { 11 12 @Test 13 public void testName() throws Exception { 14 15 /* 16 * ClassLoader 類加載器 17 * ClassLoader :可以從項目的類路徑下面讀取對應的配置文件返回一個輸入流 18 * ClassLoader 在程序運行的時候JVM已經為每一個項目都創建了一個,我們開發者只需要獲取即可 19 * 獲取類加載器方式 20 * 1、使用當前線程 21 * ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 22 * 2、通過某一類的字節碼實例也可以獲取 23 * ClassLoader classLoader = PropertiesTest.class.getClassLoader(); 24 */ 25 ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 26 //使用類加載器獲取項目 類路徑下面的文件 27 InputStream inputStream = classLoader.getResourceAsStream("db.properties"); 28 29 30 /* 31 * Properties 是Map集合下面的一個 專門用於讀取配置文件的對象 32 * 可以讀取當前類路徑下面的 xxx.properites類型的配置文件 33 * 34 * xxx.properites的內容必須是key=value 鍵值對的數據 35 */ 36 37 //1.創建Properties對象 38 Properties p = new Properties(); 39 40 //2.加載配置文件 41 p.load(inputStream); 42 43 System.out.println(p); 44 45 //獲取具體某一個key對應的值 46 String driverClassName = p.getProperty("driverClassName"); 47 System.out.println(driverClassName); 48 } 49 }
7.5.3. 效果
8. 連接池
8.1. 遇到的問題-引出連接池
8.2. 連接池思想
8.3. 連接池的概述
在Java中,連接池使用javax.sql.DataSource接口來表示連接池.
注意:DataSource僅僅只是一個接口,由各大服務器廠商來實現(Tomcat.JBoss,阿里巴巴).
常用的DataSource的實現:
DBCP: Spring推薦的
C3P0: Hibernate推薦的
Druid : (德魯伊)阿里巴巴開源的,性能最好,速度最快
DataSource(數據源)和連接池(Connection Pool)是同一個.
8.4. 使用連接池和不使用連接池的區別在哪里
8.5. Druid連接池的使用
8.5.1. 准備druid 連接池jar包到項目
從代碼上:
不使用連接池: Conenction對象由DriverManager獲取.
Connection conn = DriverManager.getConnection(url,username,password);
使用連接池:
如何創建DataSource對象,如何在DataSource中設置url,賬號,密碼.
Connection conn = DataSource對象.getConnection();
--------------------------------------------------------------------
使用連接池的時候:
釋放資源: Connection對象.close():
是把Connection放回給連接池,而不是和數據庫斷開.
1 package cn.sxt.jdbc.test; 2 3 import static org.junit.Assert.*; 4 5 import java.io.InputStream; 6 import java.io.Reader; 7 import java.sql.Connection; 8 import java.util.Properties; 9 10 import javax.sql.DataSource; 11 12 import org.junit.Test; 13 14 import com.alibaba.druid.pool.DruidDataSource; 15 import com.alibaba.druid.pool.DruidDataSourceFactory; 16 import com.alibaba.druid.pool.DruidPooledConnection; 17 18 public class DataSourceTest { 19 // 直接創建連接池對象 20 @Test 21 public void testName() throws Exception { 22 // 1.創建連接池對象 23 DruidDataSource ds = new DruidDataSource(); 24 // 2.設置連接數據庫的賬號密碼 25 ds.setDriverClassName("com.mysql.jdbc.Driver"); 26 ds.setUrl("jdbc:mysql://localhost:3306/jdbcdemo"); 27 ds.setUsername("root"); 28 ds.setPassword("root"); 29 ds.setMaxActive(10);// 最大連接數 30 // 3.獲取連接對象 31 Connection conn = ds.getConnection(); 32 System.out.println(conn); 33 } 34 35 // 使用工廠對象創建連接池對象,工廠對象的好處,不需要直接設置賬號密碼等等,只需要將 36 // 連接數據庫的賬號密碼等等以指定的 key的名稱配置到 xxx.properties文件中即可,工廠對象底層自動讀取 37 @Test 38 public void testDataSourceByFactory() throws Exception { 39 40 // 1.獲取類加載器用於加載clsspath下面的 配置文件 41 ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 42 // 2.讀取druid.properties配置文件 43 InputStream inputStream = classLoader.getResourceAsStream("druid.properties"); 44 // 3.創建Properties對象,並讀取配置文件對應的輸入流 45 Properties p = new Properties(); 46 p.load(inputStream); 47 48 // 4.創建連接池對象 49 DataSource ds = DruidDataSourceFactory.createDataSource(p); 50 // 5.獲取連接對象 51 Connection conn = ds.getConnection(); 52 System.out.println(conn); 53 } 54 }
|
8.5.2. db.propperties
|
8.5.3. 使用Druid抽取的工具類
9. 事務
案例:銀行轉賬:從張無忌賬戶上給趙敏轉1000塊.
准備:account(賬戶表):
---------------------------------------------------------------
id name(賬號,唯一) balance(余額)
1 張無忌 20000
2 趙敏 0
---------------------------------------------------------------
轉賬的思路:
1.檢查張無忌的賬號余額是否大於等於1000.
SQL: SELECT balance FROM account WHERE name = '張無忌' AND balance >=1000
余額>=1000:GOTO 2:
余額 <1000:提示:親,你的余額不足.
2.在張無忌的賬號余額上減少1000.
SQL: UPDATE account SET balance = balance-1000 WHERE name = '張無忌'
3.在趙敏的賬戶余額尚增加1000.
SQL: UPDATE account SET balance = balance+1000 WHERE name = '趙敏'
-------------------------------------------------------------------------------------------
注意:在第二步和第三步之間,停電了.
使用異常模擬停電:System.out.println(1/0);
9.1. 事務概述
事務(Transaction,簡寫為tx):
在數據庫中,所謂事務是指一組邏輯操作單元,使數據從一種狀態變換到另一種狀態。
為確保數據庫中數據的一致性,數據的操縱應當是離散的成組的邏輯單元:
當每個邏輯操作單元全部完成時,數據的一致性可以保持,
而當這個單元中的一部分操作失敗,整個事務應全部視為錯誤,所有從起始點以后的操作應全部回退到開始狀態。
事務的操作:先定義開始一個事務,然后對數據作修改操作,這時如果提交(commit),這些修改就永久地保存下來,如果回退(rollback),數據庫管理系統將放棄您所作的所有修改而回到開始事務時的狀態。
--------------------------------------------------
事務的ACID屬性:
1. 原子性(Atomicity)
原子性是指事務是一個不可分割的工作單位,事務中的操作要么都發生,要么都不發生。
2. 一致性(Consistency)
事務必須使數據庫從一個一致性狀態變換到另外一個一致性狀態。(數據不被破壞)
3. 隔離性(Isolation)
事務的隔離性是指一個事務的執行不能被其他事務干擾,即一個事務內部的操作及使用的數據對並發的其他事務是隔離的,並發執行的各個事務之間不能互相干擾。
4. 持久性(Durability)
持久性是指一個事務一旦被提交,它對數據庫中數據的改變就是永久性的,接下來的其他操作和數據庫故障不應該對其有任何影響
--------------------------------------------------
事務:指構成單個邏輯工作單元的操作集合
事務處理:保證所有事務都作為一個工作單元來執行,即使出現了故障,都不能改變這種執行方式。當在一個事務中執行多個操作時,要么所有的事務都被提交(commit),要么整個事務回滾(rollback)到最初狀態
處理事務的兩個動作:
提交:commit: 當整個事務中,所有的邏輯單元都正常執行成功. ---->提交事務.---數據已經提交,不能更改.
回滾:rollback: 當整個事務中,有一個邏輯單元執行失敗, ---->回滾事務.
撤銷該事務中的所有操作--->恢復到最初的狀態.
---------------------------------------------------------------------------------------------------
如何在代碼中去處理事務:
1.在JDBC中,事務是默認自動提交的. 必須先設置事務為手動提交.
connection對象.setAutoCommit(false);//設置事務為手動提交.
2.手動的提交事務.
connection對象.commit();
3.若出現異常必須回滾事務:
不回滾事務,總余額依然是正確的. 若不回滾事務,不會釋放數據庫資源.
connection對象.rollback();
-----------------------------------------------------------------------------------
1.在JDBC在事務是默認提交的,那是在什么時候提交的.
在執行一個DML/DDL操作的時候,就已經提交事務了.
2.針對於CRUD操作. 只有DML操作才有事務,查詢操作沒有事務.
但是,我們一般會把查詢也放在事務里面.
- 以后,凡是發現自己編寫的代碼是正確的,測試也通過,但是就是數據庫表中的數據不變----->事務沒提交的問題.
4.MySQL中,InnoDB支持外鍵.支持事務,MyISAM不支持外鍵,不支持事務.
InnoDB存儲引擎: 支持事務,支持外鍵,但是查詢效率略低,(金融,理財,p2p)
MyISAM存儲引擎:不支持事務和外鍵,但是查詢效率較高(新聞網站)
Oracle 不存在存儲引擎,都有事務
9.2. 事務處理代碼
1 public class TransactionTest { 2 @Test 3 public void testName() throws Exception { 4 Connection conn = null; 5 Statement st = null; 6 ResultSet rs = null; 7 try { 8 conn = DruidUtil.getConnection(); 9 //將事務設置為手動提交 10 conn.setAutoCommit(false); 11 12 st = conn.createStatement(); 13 // 1.檢查張無忌的賬號余額是否大於等於1000. 14 rs = st.executeQuery("SELECT balance FROM account WHERE name = '張無忌' AND balance >=1000"); 15 if(!rs.next()) { 16 throw new RuntimeException("親,您的賬戶余額不夠"); 17 } 18 // 余額>=1000:GOTO 2: 19 // 余額 <1000:提示:親,你的余額不足. 20 // 2.在張無忌的賬號余額上減少1000. 21 st.executeUpdate("UPDATE account SET balance = balance-1000 WHERE name = '張無忌'"); 22 23 System.out.println(1/0); 24 25 // 3.在趙敏的賬戶余額尚增加1000. 26 st.executeUpdate("UPDATE account SET balance = balance+1000 WHERE name = '趙敏'"); 27 28 //提交事務 29 conn.commit(); 30 31 32 } catch (Exception e) { 33 e.printStackTrace(); 34 //回滾事務 35 conn.rollback(); 36 37 }finally { 38 DruidUtil.close(conn, st, rs); 39 } 40 41 } 42 }