大数据WEB阶段(七)JDBC、数据库批处理、数据库连接池
JDBC
一、概述
數據庫驅動: 數據庫廠商提供的用來操作數據庫的jar包
- JDBC簡介
- 由于各大數據庫廠商提供的數據庫驅動各不相同, 導致了開發人員的學習成本十分的高. SUN公司為了簡化數據庫的操作, 提供了一套規范, 本質上就是一大堆的接口, 要求各大數據庫廠商在提供驅動時都要實現JDBC這套接口, 實現之后, 只要學會JDBC這套接口, 所有的數據庫驅動就都會使用了!
- 六個步驟實現JDBC程序
在開發之前 , 首先要導入jar包
Connection conn = null; Statement stat = null; ResultSet rs = null; try {//1.注冊數據庫驅動Class.forName("com.mysql.jdbc.Driver");//2.獲取數據庫連接conn = DriverManager.getConnection("jdbc:mysql:///mydb5", "root", "root");//3.獲取傳輸器stat = conn.createStatement();//4.利用傳輸器,發送sql到數據庫執行,返回執行結果rs = stat.executeQuery("select * from account");//5.處理結果while(rs.next()){int id = rs.getInt(1);String name = rs.getString("name");double money = rs.getDouble("money");System.out.println(id+name+money);} } catch (Exception e) {e.printStackTrace();throw new RuntimeException(); }finally{//6.釋放資源if(rs!=null){try {rs.close();} catch (SQLException e) {e.printStackTrace();}finally{rs = null;}}if(stat!=null){try {stat.close();} catch (SQLException e) {e.printStackTrace();}finally{stat = null;}}if(conn!=null){try {conn.close();} catch (SQLException e) {e.printStackTrace();}finally{conn = null;}} }- JDBC API詳解
- 注冊數據庫驅動
- 使用DriverManager.registerDriver(new Driver());注冊數據庫有兩個缺點,首先,通過觀察mysql的中Driver接口的實現類發現在靜態代碼塊中注冊驅動的邏輯,所以這種方式會造成驅動被注冊兩次。另外,這種方式導致了程序和具體的數據庫驅動綁死在了一起,程序的靈活性比較低。
- 所以推薦使用:Class.forName(“com.mysql.jdbc.Driver”);的方式注冊數據庫驅動。
- 獲取數據庫連接 conn = DriverManager.getConnection(url,name,password);
數據庫URL
URL用于標識數據庫的位置,程序員通過URL地址告訴JDBC程序連接哪個數據庫,URL的寫法為:
jdbc:mysql://localhost:3306/test ?參數名=參數值常用數據庫URL地址的寫法:
Oracle寫法:jdbc:oracle:thin:@localhost:1521:sid SqlServer寫法:jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=sid MySql:jdbc:mysql://localhost:3306/sid Mysql的url地址的簡寫形式: jdbc:mysql:///sid
Connection
Jdbc程序中的Connection,它用于代表數據庫的鏈接,Connection是數據庫編程中最重要的一個對象,客戶端與數據庫所有交互都是通過connection對象完成的,這個對象的常用方法:
createStatement():創建向數據庫發送sql的statement對象。 prepareStatement(sql):創建向數據庫發送預編譯sql的PreparedSatement對象。 prepareCall(sql):創建執行存儲過程的callableStatement對象。 setAutoCommit(boolean?autoCommit):設置事務是否自動提交。 commit():在鏈接上提交事務。 rollback():在此鏈接上回滾事務。
Statement
Jdbc程序中的Statement對象用于向數據庫發送SQL語句, Statement對象常用方法:
executeQuery(String?sql) :用于向數據庫發送查詢語句。 executeUpdate(String?sql):用于向數據庫發送insert、update或delete語句 execute(String sql):用于向數據庫發送任意sql語句 addBatch(String?sql):把多條sql語句放到一個批處理中。 executeBatch():向數據庫發送一批sql語句執行。
ResultSet
- Jdbc程序中的ResultSet用于代表Sql語句的執行結果。Resultset封裝執行結果時,采用的類似于表格的方式。ResultSet 對象維護了一個指向表格數據行的游標,初始的時候,游標在第一行之前,調用ResultSet.next() 方法,可以使游標指向具體的數據行,進行調用方法獲取該行的數據。
ResultSet既然用于封裝執行結果的,所以該對象提供的都是用于獲取數據的get方法:
獲取任意類型的數據 getObject(int index) getObject(string columnName) 獲取指定類型的數據,例如: getString(int index) getString(String columnName) getInt(columnIndex) getInt(columnLabel) getDouble(columnIndex) getDouble(columnLabel) ... 操作游標的方法,例如: next():移動到下一行 Previous():移動到前一行 absolute(int row):移動到指定行 beforeFirst():移動resultSet的最前面。 afterLast() :移動到resultSet的最后面。 ...
- 釋放資源
- Jdbc程序運行完后,切記要釋放程序在運行過程中,創建的那些與數據庫進行交互的對象,這些對象通常是ResultSet, Statement和Connection對象。
- 特別是Connection對象,它是非常稀有的資源,用完后必須馬上釋放,如果Connection不能及時、正確的關閉,極易導致系統宕機。Connection的使用原則是盡量晚創建,盡量早的釋放。
- 為確保資源釋放代碼能運行,資源釋放代碼也一定要放在finally語句中。
- JDBC的增刪改查
增
//jdbc 增 public class Demo_01 {public static void main(String[] args) {Connection conn = null;Statement stat = null;ResultSet rs = null;try {//注冊驅動Class.forName("com.mysql.jdbc.Driver");//獲取連接conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/cszj","root","root");//獲取傳輸器stat = conn.createStatement();//通過傳輸器將sql語句傳輸到mysql中int rows = stat.executeUpdate("insert into user values(7 ,'劉備','1234565')");//結果System.out.println("該操作影響了:"+rows+"行");} catch (Exception e) {e.printStackTrace();}finally{if(rs !=null){try {rs.close();} catch (SQLException e) {e.printStackTrace();}finally{rs = null;}}if(stat !=null){try {stat.close();} catch (SQLException e) {e.printStackTrace();}finally{stat = null;}}if(conn !=null){try {conn.close();} catch (SQLException e) {e.printStackTrace();}finally{conn = null;}}}} }刪
//jdbc 刪 public class Demo_01 {public static void main(String[] args) {Connection conn = null;Statement stat = null;ResultSet rs = null;try {//注冊驅動Class.forName("com.mysql.jdbc.Driver");//獲取連接conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/cszj","root","root");//獲取傳輸器stat = conn.createStatement();//通過傳輸器將sql語句傳輸到mysql中int rows = stat.executeUpdate("delete from user where id = 5");//結果System.out.println("該操作影響了:"+rows+"行");} catch (Exception e) {e.printStackTrace();}finally{if(rs !=null){try {rs.close();} catch (SQLException e) {e.printStackTrace();}finally{rs = null;}}if(stat !=null){try {stat.close();} catch (SQLException e) {e.printStackTrace();}finally{stat = null;}}if(conn !=null){try {conn.close();} catch (SQLException e) {e.printStackTrace();}finally{conn = null;}}}} }改
//jdbc 改 public class Demo_01 {public static void main(String[] args) {Connection conn = null;Statement stat = null;ResultSet rs = null;try {//注冊驅動Class.forName("com.mysql.jdbc.Driver");//獲取連接conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/cszj","root","root");//獲取傳輸器stat = conn.createStatement();//通過傳輸器將sql語句傳輸到mysql中int rows = stat.executeUpdate("update user set id = 5 where user='奧特曼'");//結果System.out.println("該操作影響了:"+rows+"行");} catch (Exception e) {e.printStackTrace();}finally{if(rs !=null){try {rs.close();} catch (SQLException e) {e.printStackTrace();}finally{rs = null;}}if(stat !=null){try {stat.close();} catch (SQLException e) {e.printStackTrace();}finally{stat = null;}}if(conn !=null){try {conn.close();} catch (SQLException e) {e.printStackTrace();}finally{conn = null;}}}} }查
//jdbc 查 public class Demo_01 {public static void main(String[] args) {Connection conn = null;Statement stat = null;ResultSet rs = null;try {//注冊驅動Class.forName("com.mysql.jdbc.Driver");//獲取連接conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/cszj","root","root");//獲取傳輸器stat = conn.createStatement();//通過傳輸器將sql語句傳輸到mysql中rs = stat.executeQuery("select * from user");//結果while(rs.next()){int id = rs.getInt("id");String user = rs.getString("user");String password = rs.getString("password");System.out.println(id+":"+user+":"+password);}} catch (Exception e) {e.printStackTrace();}finally{if(rs !=null){try {rs.close();} catch (SQLException e) {e.printStackTrace();}finally{rs = null;}}if(stat !=null){try {stat.close();} catch (SQLException e) {e.printStackTrace();}finally{stat = null;}}if(conn !=null){try {conn.close();} catch (SQLException e) {e.printStackTrace();}finally{conn = null;}}}} }自定義JDBCUtils 工具類
創建配置文件 config.properties
driverClass=com.mysql.jdbc.Driver jdbcUrl=jdbc:mysql://localhost:3306/cszj user=root password=root創建數據庫工具類
/*** 數據庫工具類* */ public class JDBCUtils {//只在類被加載時讀取一次配置文件private static Properties prop = new Properties();static{try {String path = JDBCUtils.class.getClassLoader().getResource("config.properties").getPath();prop.load(new FileInputStream(path));} catch (Exception e) {e.printStackTrace();} }//獲取連接public static Connection getConnection(){String driverClass = prop.getProperty("driverClass");String jdbcUrl = prop.getProperty("jdbcUrl");String user = prop.getProperty("user");String password = prop.getProperty("password");Connection conn = null;try {//注冊驅動Class.forName(driverClass);//獲取連接conn = DriverManager.getConnection(jdbcUrl,user,password);} catch (Exception e) {e.printStackTrace();}return conn;}//釋放資源public static void close(Connection conn , Statement stat, ResultSet rs){if(rs !=null){try {rs.close();} catch (SQLException e) {e.printStackTrace();}finally{rs =null ;}}if(stat !=null){try {stat.close();} catch (SQLException e) {e.printStackTrace();}finally{stat =null ;}}if(conn !=null){try {conn.close();} catch (SQLException e) {e.printStackTrace();}finally{conn =null ;}} } }修改數據路連接代碼
//jdbc 查 public class Demo_01 {public static void main(String[] args) {Connection conn = null;Statement stat = null;ResultSet rs = null;try {//獲取連接conn = JDBCUtils.getConnection();//獲取傳輸器stat = conn.createStatement();//通過傳輸器將sql語句傳輸到mysql中rs = stat.executeQuery("select * from user");//結果while(rs.next()){int id = rs.getInt("id");String user = rs.getString("user");String password = rs.getString("password");System.out.println(id+":"+user+":"+password);}} catch (Exception e) {e.printStackTrace();}finally{JDBCUtils.close(conn, stat, rs);}} }
PreparedStatement
注入攻擊演示
public static void main(String[] args) {Scanner scan = new Scanner(System.in);//提示用戶登陸System.out.println("請登陸");//提示輸入用戶名System.out.println("請輸入用戶名:");String username = scan.nextLine();//提示輸入密碼System.out.println("請輸入密碼:");String password = scan.nextLine();login(username,password);}private static void login(String username, String password) {Connection conn = null;Statement stat = null;ResultSet rs = null;try {conn = JDBCUtils.getConnection();stat = conn.createStatement();String sql = "select * from user where username='"+username+"' and password='"+password+"'";//當輸入“張三'#”后,sql變為://select * from user where username='張三'#' and password=''//當輸入“張三' or '2=2”后,sql變為://select * from user where username='張三' or '2=2' and password=''rs = stat.executeQuery(sql);if(rs.next()){System.out.println("恭喜,登陸成功");}else{System.out.println("用戶名或密碼錯誤");}} catch (Exception e) {e.printStackTrace();throw new RuntimeException();}finally{JDBCUtils.close(conn, stat, rs);}}Preparedment
優點:
可以提高程序運行的效率
PreparedStatement對象發送的sql語句(骨架)到數據庫編譯后會被數據緩存下來, 如果下次執行的sql與緩存中的相匹配, 就不再編譯而是直接使用緩存中的語句, 可以減少sql語句編譯的次數, 提高程序執行的效率!
Statement對象發送的sql語句到數據庫之后也會編譯, 但是Statement對象是先拼接好再發送sql到數據庫, 如果每次參數不同, 整條sql也就不同. 所以每次都需要編譯!
代碼改造
//jdbc 查 public class Demo_01 {public static void main(String[] args) {Connection conn = null;PreparedStatement ps = null;ResultSet rs = null;try {//獲取連接conn = JDBCUtils.getConnection();//獲取傳輸器ps = conn.prepareStatement(" select * from user");//通過傳輸器將sql語句傳輸到mysql中rs = ps.executeQuery();//結果while(rs.next()){int id = rs.getInt("id");String user = rs.getString("user");String password = rs.getString("password");System.out.println(id+":"+user+":"+password);}} catch (Exception e) {e.printStackTrace();}finally{JDBCUtils.close(conn, ps, rs);}} }二、批處理
Statement方式實現批處理
案例:
//批處理 public class Demo_02 {public static void main(String[] args) {Connection conn = null;Statement stat = null;ResultSet rs = null;try{//獲取連接conn = JDBCUtils.getConnection();//獲取傳輸器stat = conn.createStatement();stat.addBatch("use cszj;");stat.addBatch("create if not exists table tb_batch(id int primary key auto_increment, name varchar(20))");stat.addBatch("insert into tb_batch values(null,'a')");stat.addBatch("insert into tb_batch values(null,'b')");stat.addBatch("insert into tb_batch values(null,'c')");stat.addBatch("insert into tb_batch values(null,'d')");stat.executeBatch();System.out.println("執行完成");}catch(Exception e){e.printStackTrace();}finally{JDBCUtils.close(conn, stat, rs);}} }PreparedStatement方式實現批處理
案例:
//批處理 public class Demo_02 {public static void main(String[] args) {Connection conn = null;PreparedStatement ps = null;ResultSet rs = null;try{//獲取連接conn = JDBCUtils.getConnection();//獲取傳輸器ps = conn.prepareStatement("insert into tb_batch values(null ,? )");for( int i = 0;i<10;i++){ps.setString(1, "a"+i);ps.addBatch();}ps.executeBatch();System.out.println("執行完成");}catch(Exception e){e.printStackTrace();}finally{JDBCUtils.close(conn, ps, rs);}} }三、數據庫連接池
自定義一個簡易的數據庫連接池
import java.io.PrintWriter; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.util.LinkedList; import java.util.List; import java.util.logging.Logger; import javax.sql.DataSource;public class MyPool implements DataSource{private static List<Connection> list = new LinkedList<Connection>();static{try {Class.forName("com.mysql.jdbc.Driver");for (int i = 0; i < 5; i++) {Connection conn = DriverManager.getConnection("jdbc:mysql:///mydb5","root","root");list.add(conn);}} catch (Exception e) {e.printStackTrace();throw new RuntimeException();}}@Overridepublic Connection getConnection() throws SQLException {if(list.isEmpty()){for (int i = 0; i < 3; i++) {Connection conn = DriverManager.getConnection("jdbc:mysql:///mydb1");list.add(conn);}}Connection conn = list.remove(0);System.out.println("成功從數據庫中獲取一個連接,連接池中還剩"+list.size()+"個連接..");return conn;}public void returnConn(Connection conn){try {if(conn != null && !conn.isClosed()){list.add(conn);System.out.println("成功向數據庫還回一個連接,連接池中還剩"+list.size()+"個連接..");}} catch (SQLException e) {e.printStackTrace();throw new RuntimeException();}}@Overridepublic PrintWriter getLogWriter() throws SQLException {// TODO Auto-generated method stubreturn null;}@Overridepublic void setLogWriter(PrintWriter out) throws SQLException {// TODO Auto-generated method stub}@Overridepublic void setLoginTimeout(int seconds) throws SQLException {// TODO Auto-generated method stub}@Overridepublic int getLoginTimeout() throws SQLException {// TODO Auto-generated method stubreturn 0;}@Overridepublic Logger getParentLogger() throws SQLFeatureNotSupportedException {// TODO Auto-generated method stubreturn null;}@Overridepublic <T> T unwrap(Class<T> iface) throws SQLException {// TODO Auto-generated method stubreturn null;}@Overridepublic boolean isWrapperFor(Class<?> iface) throws SQLException {// TODO Auto-generated method stubreturn false;}@Overridepublic Connection getConnection(String username, String password)throws SQLException {// TODO Auto-generated method stubreturn null;} }測試自定義連接池import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement;public class TestMyPool {public static void main(String[] args) {MyPool pool = new MyPool();Connection conn= null;Statement stat = null;ResultSet rs = null;try {conn = pool.getConnection();stat = conn.createStatement();rs = stat.executeQuery("select * from account where id=1");if(rs.next()){System.out.println(rs.getInt("id")+":"+rs.getString("name")+":"+rs.getDouble("money"));}System.out.println("執行完畢");} catch (Exception e) {e.printStackTrace();throw new RuntimeException();}finally{if(rs!=null){try {rs.close();} catch (SQLException e) {e.printStackTrace();}finally{rs = null;}}if(stat!=null){try {stat.close();} catch (SQLException e) {e.printStackTrace();}finally{stat = null;}}pool.returnConn(conn);}} }改造close方法
案例:
裝飾者類 public class ConnectionDecorate implements Connection{private Connection conn = null;private MyPool pool = null;public ConnectionDecorate(Connection conn,MyPool pool){this.conn = conn;this.pool = pool;}@Overridepublic void close() throws SQLException {pool.returnConn(conn);}修改MyPool類:在獲取連接方法中,return前加入如下代碼: //先進行包裝Connection connDecorate = new ConnectionDecorate(conn,this);return connDecorate;測試類最后只需關閉連接即可開源數據庫連接池c3p0
c3p0示例
創建測試類
public static void main(String[] args) {Connection conn = null;Statement stat = null;ResultSet rs = null;try {//創建連接池ComboPooledDataSource cpds = new ComboPooledDataSource();//設置連接數據庫的基本信息cpds.setDriverClass("com.mysql.jdbc.Driver");cpds.setJdbcUrl("jdbc:mysql:///mydb5");cpds.setUser("root");cpds.setPassword("root");//從連接池中獲取連接conn = cpds.getConnection();stat = conn.createStatement();rs = stat.executeQuery("select * from account where id=1");if(rs.next()){System.out.println(rs.getInt("id")+" : "+rs.getString("name")+" : "+rs.getDouble("money"));}} catch (Exception e) {e.printStackTrace();throw new RuntimeException();}finally{JDBCUtils.close(conn, stat, rs);} }上面的方法是將數據寫死在代碼中,不利于代碼的維護 , 需要對代碼進行改造
C3p0會默認讀取一個配置文件,為c3p0-config.xml,(注意,名字不能改變,否則c3p0將無法讀取)我們在src或者類似的源碼目錄下,創建一個c3p0-config.xml文件, 配置內容如下:
<?xml version="1.0" encoding="UTF-8"?> <c3p0-config><default-config><property name="driverClass">com.mysql.jdbc.Driver</property><property name="jdbcUrl">jdbc:mysql:///mydb5</property><property name="user">root</property><property name="password">root</property></default-config> </c3p0-config>另一種方式:在src或者類似的源代碼目錄下, 創建一個c3p0.properties文件 , 配置文件內容如下:
c3p0.driverClass=com.mysql.jdbc.Driver c3p0.jdbcUrl=jdbc:mysql:///mydb5 c3p0.user=root c3p0.password=root總結
以上是生活随笔為你收集整理的大数据WEB阶段(七)JDBC、数据库批处理、数据库连接池的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 大数据WEB阶段(六)MySql详解(二
- 下一篇: 大数据WEB阶段(十八)数据库事务