今天對JDBC的內(nèi)容又進(jìn)行了一下復(fù)習(xí),并學(xué)了一些新的知識如數(shù)據(jù)庫連接池,Spring JDBC等。
1. JDBC基本概念2. 快速入門3. 對JDBC中各個接口和類詳解 4. 數(shù)據(jù)庫連接池5. Spring JDBC : JDBC Template
1. 概念:Java DataBase Connectivity Java 數(shù)據(jù)庫連接, Java語言操作數(shù)據(jù)庫* JDBC本質(zhì):其實是官方(sun公司)定義的一套操作所有關(guān)系型數(shù)據(jù)庫的規(guī)則,即接口。各個數(shù)據(jù)庫廠商去實現(xiàn)這套接口,提供數(shù)據(jù)庫驅(qū)動jar包。我們可以使用這套接口(JDBC)編程,真正執(zhí)行的代碼是驅(qū)動jar包中的實現(xiàn)類。2. 快速入門:* 步驟:1. 導(dǎo)入驅(qū)動jar包 mysql-connector-java-5.1.37-bin.jar1.復(fù)制mysql-connector-java-5.1.37-bin.jar到項目的libs目錄下2.右鍵-->Add As Library2. 注冊驅(qū)動3. 獲取數(shù)據(jù)庫連接對象 Connection4. 定義sql5. 獲取執(zhí)行sql語句的對象 Statement6. 執(zhí)行sql,接受返回結(jié)果7. 處理結(jié)果8. 釋放資源* 代碼實現(xiàn): //1. 導(dǎo)入驅(qū)動jar包 //2.注冊驅(qū)動 Class.forName("com.mysql.jdbc.Driver"); //3.獲取數(shù)據(jù)庫連接對象 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3", "root", "root"); //4.定義sql語句 String sql = "update account set balance = 500 where id = 1"; //5.獲取執(zhí)行sql的對象 Statement Statement stmt = conn.createStatement(); //6.執(zhí)行sql int count = stmt.executeUpdate(sql); //7.處理結(jié)果 System.out.println(count); //8.釋放資源 stmt.close(); conn.close();3. 詳解各個對象:1. DriverManager:驅(qū)動管理對象* 功能:1. 注冊驅(qū)動:告訴程序該使用哪一個數(shù)據(jù)庫驅(qū)動jarstatic void registerDriver(Driver driver) :注冊與給定的驅(qū)動程序 DriverManager 。 寫代碼使用: Class.forName("com.mysql.jdbc.Driver");通過查看源碼發(fā)現(xiàn):在com.mysql.jdbc.Driver類中存在靜態(tài)代碼塊 static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); }}注意:mysql5之后的驅(qū)動jar包可以省略注冊驅(qū)動的步驟。2. 獲取數(shù)據(jù)庫連接:* 方法:static Connection getConnection(String url, String user, String password) * 參數(shù):* url:指定連接的路徑* 語法:jdbc:mysql://ip地址(域名):端口號/數(shù)據(jù)庫名稱* 例子:jdbc:mysql://localhost:3306/db3* 細(xì)節(jié):如果連接的是本機mysql服務(wù)器,并且mysql服務(wù)默認(rèn)端口是3306,則url可以簡寫為:jdbc:mysql:///數(shù)據(jù)庫名稱* user:用戶名* password:密碼 2. Connection:數(shù)據(jù)庫連接對象1. 功能:1. 獲取執(zhí)行sql 的對象* Statement createStatement()* PreparedStatement prepareStatement(String sql) 2. 管理事務(wù):* 開啟事務(wù):setAutoCommit(boolean autoCommit) :調(diào)用該方法設(shè)置參數(shù)為false,即開啟事務(wù)* 提交事務(wù):commit() * 回滾事務(wù):rollback() 3. Statement:執(zhí)行sql的對象1. 執(zhí)行sql1. boolean execute(String sql) :可以執(zhí)行任意的sql 了解 2. int executeUpdate(String sql) :執(zhí)行DML(insert、update、delete)語句、DDL(create、alter、drop)語句* 返回值:影響的行數(shù),可以通過這個影響的行數(shù)判斷DML語句是否執(zhí)行成功 返回值>0的則執(zhí)行成功,反之,則失敗。3. ResultSet executeQuery(String sql) :執(zhí)行DQL(select)語句2. 練習(xí):1. account表 添加一條記錄2. account表 修改記錄3. account表 刪除一條記錄代碼:Statement stmt = null; Connection conn = null; try { //1. 注冊驅(qū)動 Class.forName("com.mysql.jdbc.Driver"); //2. 定義sql String sql = "insert into account values(null,'王五',3000)"; //3.獲取Connection對象 conn = DriverManager.getConnection("jdbc:mysql:///db3", "root", "root"); //4.獲取執(zhí)行sql的對象 Statement stmt = conn.createStatement(); //5.執(zhí)行sql int count = stmt.executeUpdate(sql);//影響的行數(shù) //6.處理結(jié)果 System.out.println(count); if(count > 0){ System.out.println("添加成功!"); }else{ System.out.println("添加失??!"); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); }finally { //stmt.close(); //7. 釋放資源 //避免空指針異常 if(stmt != null){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if(conn != null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }修改刪除與添加操作流程一樣,只需更改sql語句即可。4. ResultSet:結(jié)果集對象,封裝查詢結(jié)果* boolean next(): 游標(biāo)向下移動一行,判斷當(dāng)前行是否是最后一行末尾(是否有數(shù)據(jù)),如果是,則返回false,如果不是則返回true* getXxx(參數(shù)):獲取數(shù)據(jù)* Xxx:代表數(shù)據(jù)類型 如: int getInt() ,String getString()* 參數(shù):1. int:代表列的編號,從1開始 如: getString(1)2. String:代表列名稱。 如: getDouble("balance")* 注意:* 使用步驟:1. 游標(biāo)向下移動一行2. 判斷是否有數(shù)據(jù)3. 獲取數(shù)據(jù) //循環(huán)判斷游標(biāo)是否是最后一行末尾。 while(rs.next()){ //獲取數(shù)據(jù) int id = rs.getInt(1); String name = rs.getString("name"); double balance = rs.getDouble(3); System.out.println(id + "---" + name + "---" + balance); }5. PreparedStatement:執(zhí)行sql的對象1. SQL注入問題:在拼接sql時,有一些sql的特殊關(guān)鍵字參與字符串的拼接。會造成安全性問題1. 輸入用戶隨便,輸入密碼:a' or 'a' = 'a2. sql:select * from user where username = 'fhdsjkf' and password = 'a' or 'a' = 'a' 2. 解決sql注入問題:使用PreparedStatement對象來解決3. 預(yù)編譯的SQL:參數(shù)使用?作為占位符4. 步驟:1. 導(dǎo)入驅(qū)動jar包 mysql-connector-java-5.1.37-bin.jar2. 注冊驅(qū)動3. 獲取數(shù)據(jù)庫連接對象 Connection4. 定義sql* 注意:sql的參數(shù)使用?作為占位符。 如:select * from user where username = ? and password = ?;5. 獲取執(zhí)行sql語句的對象 PreparedStatement Connection.prepareStatement(String sql) 6. 給?賦值:* 方法: setXxx(參數(shù)1,參數(shù)2)* 參數(shù)1:?的位置編號 從1 開始* 參數(shù)2:?的值7. 執(zhí)行sql,接受返回結(jié)果,不需要傳遞sql語句8. 處理結(jié)果9. 釋放資源例子:String sql = "select * from user where username = ? and password = ?"; pstmt = conn.prepareStatement(sql); pstmt.setString(1, username); pstmt.setString(2, password); rs = pstmt.executeQuery();5. 注意:后期都會使用PreparedStatement來完成增刪改查的所有操作1. 可以防止SQL注入2. 效率更高
* 目的:簡化書寫* 分析:1. 注冊驅(qū)動也抽取2. 抽取一個方法獲取連接對象* 需求:不想傳遞參數(shù)(麻煩),還得保證工具類的通用性。* 解決:配置文件jdbc.propertiesurl=user=password= 例如:url=jdbc:mysql:///db3user=rootpassword=rootdriver=com.mysql.jdbc.Driver 注意:等號兩邊沒有空格等號右邊不用加引號3. 抽取一個方法釋放資源* 代碼實現(xiàn):public class JDBCUtils { private static String url; private static String user; private static String password; private static String driver; /** * 文件的讀取,只需要讀取一次即可拿到這些值。使用靜態(tài)代碼塊 */ static{ //讀取資源文件,獲取值。 try { //1. 創(chuàng)建Properties集合類。 Properties pro = new Properties(); //獲取src路徑下的文件的方式--->ClassLoader 類加載器 ClassLoader classLoader = JDBCUtils.class.getClassLoader(); URL res = classLoader.getResource("jdbc.properties"); String path = res.getPath();//得到絕對路徑 System.out.println(path);///D:/IdeaProjects/itcast/out/production/day04_jdbc/jdbc.properties //2. 加載文件 // pro.load(new FileReader("D:\\IdeaProjects\\itcast\\day04_jdbc\\src\\jdbc.properties")); pro.load(new FileReader(path)); //3. 獲取數(shù)據(jù),賦值 url = pro.getProperty("url"); user = pro.getProperty("user"); password = pro.getProperty("password"); driver = pro.getProperty("driver"); //4. 注冊驅(qū)動 Class.forName(driver); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } /** * 獲取連接 * @return 連接對象 */ public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url, user, password); } /** * 釋放資源 * @param stmt * @param conn */ public static void close(Statement stmt,Connection conn){ if( stmt != null){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if( conn != null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } /** * 釋放資源 * @param rs * @param stmt * @param conn */ public static void close(ResultSet rs,Statement stmt, Connection conn){ if( rs != null){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if( stmt != null){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if( conn != null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }}* 練習(xí):* 需求:1. 通過鍵盤錄入用戶名和密碼2. 判斷用戶是否登錄成功* select * from user where username = "" and password = "";* 如果這個sql有查詢結(jié)果,則成功,反之,則失敗* 步驟:1. 創(chuàng)建數(shù)據(jù)庫表 userCREATE TABLE USER(id INT PRIMARY KEY AUTO_INCREMENT,username VARCHAR(32),PASSWORD VARCHAR(32));INSERT INTO USER VALUES(NULL,'zhangsan','123');INSERT INTO USER VALUES(NULL,'lisi','234');2. 代碼實現(xiàn):public class JDBC_Demo { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("請輸入用戶名:"); String username = sc.nextLine(); System.out.println("請輸入密碼:"); String password = sc.nextLine(); boolean flag = new JDBC_Demo().login(username, password); if (flag) { System.out.println("登陸成功!"); } else { System.out.println("用戶名或密碼錯誤!"); } } public boolean login(String username,String password) { if (username == null || password == null) { return false; } Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; try { conn = JDBCUtils.getConnection(); String sql = "select * from user where username = ? and password = ?"; pstmt = conn.prepareStatement(sql); pstmt.setString(1, username); pstmt.setString(2, password); rs = pstmt.executeQuery(); return rs.next(); } catch (SQLException e) { e.printStackTrace(); } finally { JDBCUtils.close(rs, pstmt, conn); } return false; }}
ps:實現(xiàn)這個樣例時遇到了一個問題,運行時會報錯找不到properties文件,檢查了代碼沒有bug,認(rèn)為問題可能是因為項目所在路徑文件夾名稱中有空格,將項目移到另外一個目錄即可正常運行。
1. 事務(wù):一個包含多個步驟的業(yè)務(wù)操作。如果這個業(yè)務(wù)操作被事務(wù)管理,則這多個步驟要么同時成功,要么同時失敗。2. 操作:1. 開啟事務(wù)2. 提交事務(wù)3. 回滾事務(wù)3. 使用Connection對象來管理事務(wù)* 開啟事務(wù):setAutoCommit(boolean autoCommit) :調(diào)用該方法設(shè)置參數(shù)為false,即開啟事務(wù)* 在執(zhí)行sql之前開啟事務(wù)* 提交事務(wù):commit() * 當(dāng)所有sql都執(zhí)行完提交事務(wù)* 回滾事務(wù):rollback() * 在catch中回滾事務(wù)4. 代碼:public class JDBCDemo10 { public static void main(String[] args) { Connection conn = null; PreparedStatement pstmt1 = null; PreparedStatement pstmt2 = null; try { //1.獲取連接 conn = JDBCUtils.getConnection(); //開啟事務(wù) conn.setAutoCommit(false); //2.定義sql //2.1 張三 - 500 String sql1 = "update account set balance = balance - ? where id = ?"; //2.2 李四 + 500 String sql2 = "update account set balance = balance + ? where id = ?"; //3.獲取執(zhí)行sql對象 pstmt1 = conn.prepareStatement(sql1); pstmt2 = conn.prepareStatement(sql2); //4. 設(shè)置參數(shù) pstmt1.setDouble(1,500); pstmt1.setInt(2,1); pstmt2.setDouble(1,500); pstmt2.setInt(2,2); //5.執(zhí)行sql pstmt1.executeUpdate(); // 手動制造異常 int i = 3/0; pstmt2.executeUpdate(); //提交事務(wù) conn.commit(); } catch (Exception e) { //事務(wù)回滾 try { if(conn != null) { conn.rollback(); } } catch (SQLException e1) { e1.printStackTrace(); } e.printStackTrace(); }finally { JDBCUtils.close(pstmt1,conn); JDBCUtils.close(pstmt2,null); } }}
1. 概念:其實就是一個容器(集合),存放數(shù)據(jù)庫連接的容器。 當(dāng)系統(tǒng)初始化好后,容器被創(chuàng)建,容器中會申請一些連接對象,當(dāng)用戶來訪問數(shù)據(jù)庫時,從容器中獲取連接對象,用戶訪問完之后,會將連接對象歸還給容器。2. 好處:1. 節(jié)約資源2. 用戶訪問高效3. 實現(xiàn):1. 標(biāo)準(zhǔn)接口:DataSource javax.sql包下的1. 方法:* 獲取連接:getConnection()* 歸還連接:Connection.close()。如果連接對象Connection是從連接池中獲取的,那么調(diào)用Connection.close()方法,則不會再關(guān)閉連接了,而是歸還連接。2. 一般我們不去實現(xiàn)它,有數(shù)據(jù)庫廠商來實現(xiàn)1. C3P0:數(shù)據(jù)庫連接池技術(shù)2. Druid:數(shù)據(jù)庫連接池實現(xiàn)技術(shù),由阿里巴巴提供的4. C3P0:數(shù)據(jù)庫連接池技術(shù)* 使用步驟:1. 導(dǎo)入jar包 (兩個) c3p0-0.9.5.2.jar mchange-commons-java-0.2.12.jar ,* 不要忘記導(dǎo)入數(shù)據(jù)庫驅(qū)動jar包2. 定義配置文件:* 名稱: c3p0.properties 或者 c3p0-config.xml* 路徑:直接將文件放在src目錄下即可。* 參數(shù):* 連接參數(shù): driverClass 例如:com.mysql.jdbc.DriverjdbcUrl 例如:jdbc:mysql://localhost:3306/db3user 例如:rootpassword 例如: root* 連接池參數(shù): initialPoolSize 初始連接池大小(也就是可以獲取連接的Connection數(shù)量) maxPoolSize 連接池最大連接數(shù)量 checkoutTimeout 報錯的等待時間3. 創(chuàng)建核心對象 數(shù)據(jù)庫連接池對象 ComboPooledDataSource4. 獲取連接: getConnection* 代碼: //1.創(chuàng)建數(shù)據(jù)庫連接池對象 DataSource ds = new ComboPooledDataSource(); //2. 獲取連接對象 Connection conn = ds.getConnection();5. Druid:數(shù)據(jù)庫連接池實現(xiàn)技術(shù),由阿里巴巴提供的* 使用步驟:1. 導(dǎo)入jar包 druid-1.0.9.jar2. 定義配置文件:* 是properties形式的* 可以叫任意名稱,可以放在任意目錄下3. 加載配置文件。Properties4. 獲取數(shù)據(jù)庫連接池對象:通過工廠類來獲取 DruidDataSourceFactory5. 獲取連接:getConnection* 代碼://3.加載配置文件 Properties pro = new Properties(); InputStream is = DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties"); pro.load(is); //4.獲取連接池對象 DataSource ds = DruidDataSourceFactory.createDataSource(pro); //5.獲取連接 Connection conn = ds.getConnection();* 定義工具類1. 定義一個類 JDBCUtils2. 提供靜態(tài)代碼塊加載配置文件,初始化連接池對象3. 提供方法1. 獲取連接方法:通過數(shù)據(jù)庫連接池獲取連接2. 釋放資源3. 獲取連接池的方法* 代碼:public class JDBCUtils { //1.定義成員變量 DataSource private static DataSource ds ; static{ try { //1.加載配置文件 Properties pro = new Properties(); pro.load(JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties")); //2.獲取DataSource ds = DruidDataSourceFactory.createDataSource(pro); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } /** * 獲取連接 */ public static Connection getConnection() throws SQLException { return ds.getConnection(); } /** * 釋放資源 */ public static void close(Statement stmt,Connection conn){ /* if(stmt != null){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if(conn != null){ try { conn.close();//歸還連接池 } catch (SQLException e) { e.printStackTrace(); } }*/ close(null,stmt,conn); } public static void close(ResultSet rs , Statement stmt, Connection conn){ if(rs != null){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if(stmt != null){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if(conn != null){ try { conn.close();//歸還連接池 } catch (SQLException e) { e.printStackTrace(); } } } /** * 獲取連接池方法 */ public static DataSource getDataSource(){ return ds; }}
* Spring框架對JDBC的簡單封裝。提供了一個JDBCTemplate對象簡化JDBC的開發(fā)* 步驟:1. 導(dǎo)入jar包2. 創(chuàng)建JdbcTemplate對象。依賴于數(shù)據(jù)源DataSource* JdbcTemplate template = new JdbcTemplate(ds);3. 調(diào)用JdbcTemplate的方法來完成CRUD的操作* update():執(zhí)行DML語句。增、刪、改語句* queryForMap():查詢結(jié)果將結(jié)果集封裝為map集合,將列名作為key,將值作為value 將這條記錄封裝為一個map集合* 注意:這個方法查詢的結(jié)果集長度只能是1* queryForList():查詢結(jié)果將結(jié)果集封裝為list集合* 注意:將每一條記錄封裝為一個Map集合,再將Map集合裝載到List集合中* query():查詢結(jié)果,將結(jié)果封裝為JavaBean對象* query的參數(shù):RowMapper* 一般我們使用BeanPropertyRowMapper實現(xiàn)類??梢酝瓿蓴?shù)據(jù)到JavaBean的自動封裝* new BeanPropertyRowMapper<類型>(類型.class)* queryForObject:查詢結(jié)果,將結(jié)果封裝為對象* 一般用于聚合函數(shù)的查詢4. 練習(xí):* 需求:1. 修改1號數(shù)據(jù)的 salary 為 100002. 添加一條記錄3. 刪除剛才添加的記錄4. 查詢id為1001的記錄,將其封裝為Map集合5. 查詢所有記錄,將其封裝為List6. 查詢所有記錄,將其封裝為Emp對象的List集合7. 查詢總記錄數(shù)* 代碼:import cn.itcast.domain.Emp;import cn.itcast.utils.JDBCUtils;import org.junit.Test;import org.springframework.jdbc.core.BeanPropertyRowMapper;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.jdbc.core.RowMapper;import java.sql.Date;import java.sql.ResultSet;import java.sql.SQLException;import java.util.List;import java.util.Map;public class JdbcTemplateDemo2 { //Junit單元測試,可以讓方法獨立執(zhí)行 //1. 獲取JDBCTemplate對象 private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource()); /** * 1. 修改1號數(shù)據(jù)的 salary 為 10000 */ @Test public void test1(){ //2. 定義sql String sql = "update emp set salary = 10000 where id = 1001"; //3. 執(zhí)行sql int count = template.update(sql); System.out.println(count); } /** * 2. 添加一條記錄 */ @Test public void test2(){ String sql = "insert into emp(id,ename,dept_id) values(?,?,?)"; int count = template.update(sql, 1015, "郭靖", 10); System.out.println(count); } /** * 3.刪除剛才添加的記錄 */ @Test public void test3(){ String sql = "delete from emp where id = ?"; int count = template.update(sql, 1015); System.out.println(count); } /** * 4.查詢id為1001的記錄,將其封裝為Map集合 * 注意:這個方法查詢的結(jié)果集長度只能是1,若查詢長度大于1則會報錯 */ @Test public void test4(){ String sql = "select * from emp where id = ?"; Map<String, Object> map = template.queryForMap(sql, 1001); System.out.println(map); //{id=1001, ename=孫悟空, job_id=4, mgr=1004, joindate=2000-12-17, salary=10000.00, bonus=null, dept_id=20} } /** * 5. 查詢所有記錄,將其封裝為List */ @Test public void test5(){ String sql = "select * from emp"; List<Map<String, Object>> list = template.queryForList(sql); for (Map<String, Object> stringObjectMap : list) { System.out.println(stringObjectMap); } } /** * 6. 查詢所有記錄,將其封裝為Emp對象的List集合 */ @Test public void test6(){ String sql = "select * from emp"; List<Emp> list = template.query(sql, new RowMapper<Emp>() { @Override public Emp mapRow(ResultSet rs, int i) throws SQLException { Emp emp = new Emp(); int id = rs.getInt("id"); String ename = rs.getString("ename"); int job_id = rs.getInt("job_id"); int mgr = rs.getInt("mgr"); Date joindate = rs.getDate("joindate"); double salary = rs.getDouble("salary"); double bonus = rs.getDouble("bonus"); int dept_id = rs.getInt("dept_id"); emp.setId(id); emp.setEname(ename); emp.setJob_id(job_id); emp.setMgr(mgr); emp.setJoindate(joindate); emp.setSalary(salary); emp.setBonus(bonus); emp.setDept_id(dept_id); return emp; } }); for (Emp emp : list) { System.out.println(emp); } } /** * 6. 查詢所有記錄,將其封裝為Emp對象的List集合 *///此方法需要類中定義的屬性改為引用數(shù)據(jù)類型,否則會報錯 @Test public void test6_2(){ String sql = "select * from emp"; List<Emp> list = template.query(sql, new BeanPropertyRowMapper<Emp>(Emp.class)); for (Emp emp : list) { System.out.println(emp); } } /** * 7. 查詢總記錄數(shù) */ @Test public void test7(){ String sql = "select count(id) from emp"; Long total = template.queryForObject(sql, Long.class); System.out.println(total); }}
聯(lián)系客服