1. 程式人生 > >第7章WEB07- JDBC篇

第7章WEB07- JDBC篇

javaweb JDBC篇

今日任務
? 使用JDBC完成對分類表的CRUD的操作(JDBC的回顧)
? 使用DBCP,C3P0連接池完成基本數據庫的操作(抽取工具類)
? 使用元數據抽取一個JDBC的框架.
? 能夠使用DBUtils完成CRUD的操作
教學導航
教學目標
掌握DBCP和C3P0連接池的使用並掌握連接池的原理.
掌握使用元數據抽取基本的JDBC通用方法的思想.
能夠使用DBUtils完成基本的CRUD的操作
教學方法
案例驅動法
1.1 上次課內容回顧:
MYSQL:

  • 數據庫的概述:指的是一個文件系統,這個文件系統通過SQL訪問.
  • 關系型數據庫:數據庫中存放的是實體之間的關系.
  • 常見的關系型數據庫:MYSQL,Oracle,DB2,SQLServer,SQLite,SyBase
    MYSQL的使用:
  • 安裝和卸載:
  • 存儲結構:
    • 一臺電腦安裝了MYSQL的服務器軟件,那麽這臺電腦稱為是MYSQL數據庫服務器.一個軟件創建一個數據庫,有一個實體類創建一個表與之對應,實體的實例對象通常使用表中的記錄與之對應.
      SQL:結構化查詢語言:
  • SQL的分類:
    • DDL:數據定義語言.create,alter,drop...
    • DML:數據操縱語言.update,insert,delete
    • DCL:數據控制語言.grant,if,...
    • DQL:數據查詢語言.select
  • SQL對數據庫CRUD:
  • SQL對數據庫中表CRUD的操作
  • SQL對數據庫中表的記錄的操作:
    • update,insert,delete
    • Select ... From ... Where ... Group by ... Having ... Order by ...;
  • SQL的多表設計:
    • 一對多:在多的一方創建外鍵執向一的一方的主鍵.
    • 多對多:創建中間表,至少兩個字段作為外鍵執向多對多雙方的主鍵.
    • 一對一:唯一外鍵對應,主鍵對應.
  • SQL的多表的查詢:
    • 連接查詢:
      • 交叉連接:
      • 內連接:
        • 顯式內連接:inner join
        • 隱式內連接:
      • 外連接
        • 左外連接:left outer join
        • 右外連接:right outer join
    • 子查詢:
      1.2 案例一:使用JDBC完成CRUD的操作:1.2.1 需求:
      對分類管理使用JDBC進行CRUD的操作.
      1.2.2 分析:
      1.2.2.1 技術分析:
      【JDBC的概述】
      ? JDBC:Java DataBase Connectivity Java數據庫的連接.
  • 是SUN公司統一提供的一套接口規範(JDBC).各個數據庫生產商提供實現.
    ? 驅動:兩個硬件設備之間通信的橋梁.
    【JDBC的開發步驟】
    ? 註冊驅動:
    ? 獲得連接:
    ? 獲得執行SQL語句對象:
    ? 釋放資源:
    1.2.2.2 步驟分析:
    【步驟一】:創建一個Java項目.
    【步驟二】:引入mysql的驅動包.
    【步驟三】:編寫代碼.
    【步驟四】:完成CRUD的操作:
    1.2.3 代碼實現:
工具類的抽取:
public class JDBCUtils {

/**
 * 註冊驅動的方法
 */
public static void loadDriver(){
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
 * 獲得連接的方法
 */
public static Connection getConnection(){
Connection conn = null;
try {
loadDriver();
conn = DriverManager.getConnection("jdbc:mysql:///web_07", "root", "123");
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
/**
 * 釋放資源的方法
 */
public static void release(ResultSet rs,Statement stmt,Connection conn){
if (rs != null) {
        try {
            rs.close();
        } catch (SQLException e) {
                e.printStackTrace();
        }
        // 垃圾回收盡快回收對象.
        rs = null;
    }
if (stmt != null) {
        try {
                stmt.close();
        } catch (SQLException e) {
                e.printStackTrace();
        }
        // 垃圾回收盡快回收對象.
        stmt = null;
    }
if (conn != null) {
        try {
                conn.close();
        } catch (SQLException e) {
                e.printStackTrace();
        }
        // 垃圾回收盡快回收對象.
        conn = null;
    }
}
public static void release(Statement stmt,Connection conn){
if (stmt != null) {
        try {
                stmt.close();
        } catch (SQLException e) {
                e.printStackTrace();
        }
        // 垃圾回收盡快回收對象.
        stmt = null;
    }
if (conn != null) {
        try {
                conn.close();
        } catch (SQLException e) {
                e.printStackTrace();
        }
        // 垃圾回收盡快回收對象.
        conn = null;
    }
}
}

帶有屬性文件的工具類的抽取:
定義了一個屬性文件:
public class JDBCUtils {
private static final String driverClass;
private static final String url;
private static final String username;
private static final String password;

static {
Properties properties = null;
// 讀取屬性文件:使用Java中Properties的對象.
try{
InputStream is = new FileInputStream("src/jdbc.properties");
properties = new Properties();
properties.load(is);
}catch(Exception e){
e.printStackTrace();
}
driverClass = properties.getProperty("driverClass");
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
}
/**
 * 註冊驅動的方法
 */
public static void loadDriver(){
try {
Class.forName(driverClass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
 * 獲得連接的方法
 */
public static Connection getConnection(){
Connection conn = null;
try {
loadDriver();
conn = DriverManager.getConnection(url, username, password);
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
/**
 * 釋放資源的方法
 */
public static void release(ResultSet rs,Statement stmt,Connection conn){
if (rs != null) {
        try {
            rs.close();
        } catch (SQLException e) {
                e.printStackTrace();
        }
        // 垃圾回收盡快回收對象.
        rs = null;
    }
if (stmt != null) {
        try {
                stmt.close();
        } catch (SQLException e) {
                e.printStackTrace();
        }
        // 垃圾回收盡快回收對象.
        stmt = null;
    }
if (conn != null) {
        try {
                conn.close();
        } catch (SQLException e) {
                e.printStackTrace();
        }
        // 垃圾回收盡快回收對象.
        conn = null;
    }
}
public static void release(Statement stmt,Connection conn){
if (stmt != null) {
        try {
                stmt.close();
        } catch (SQLException e) {
                e.printStackTrace();
        }
        // 垃圾回收盡快回收對象.
        stmt = null;
    }
if (conn != null) {
        try {
                conn.close();
        } catch (SQLException e) {
                e.printStackTrace();
        }
        // 垃圾回收盡快回收對象.
        conn = null;
    }
}
}

參見JDBCDemo2類:
1.2.4 總結:
1.2.4.1 JDBC的API:
【Connection】
? 創建執行SQL的對象:
技術分享圖片
技術分享圖片
技術分享圖片

? 進行事務管理:
技術分享圖片
技術分享圖片
技術分享圖片

【Statement】
? 執行SQL語句:
技術分享圖片
技術分享圖片
技術分享圖片

? 執行批處理:
技術分享圖片
技術分享圖片
技術分享圖片

【ResultSet】
? 獲得結果集中的數據:

  • getXXX(int idx);
    • select cname,cid from category;
  • getXXX(String name);
    ? 默認情況下:next();
  • 正常的情況下結果集只能向下的.
    1.3 案例二:使用連接池改造JDBC的工具類:
    1.3.1 需求:
    傳統JDBC的操作,對連接的對象銷毀不是特別好.每次創建和銷毀連接都是需要花費時間.可以使用連接池優化的程序.
  • 在程序開始的時候,可以創建幾個連接,將連接放入到連接池中.用戶使用連接的時候,可以從連接池中進行獲取.用完之後,可以將連接歸還連接池.
    1.3.2 分析:
    1.3.2.1 技術分析:
    【自定義連接池】(了解)
  • SUN公司提供了一個連接池的接口.(javax.sql.DataSource).
  • 定義一個連接池:實現這個接口.
  • 使用List集合存放多個連接的對象.
    【自定義連接池的代碼】
public class MyDataSource implements DataSource{
// 創建一個List集合用於存放多個連接對象.
private List<Connection> list = new ArrayList<Connection>();
// 在程序開始的時候,初始化幾個連接,將連接存放到list中.
public MyDataSource() {
// 初始化3個連接:
for(int i=1;i<=3;i++){
Connection conn = JDBCUtils.getConnection();
list.add(conn);
}
}
@Override
// 獲得連接的方法:
public Connection getConnection() throws SQLException {
if(list.size() <= 0){
for(int i=1;i<=3;i++){
Connection conn = JDBCUtils.getConnection();
list.add(conn);
}
}
Connection conn = list.remove(0);
return conn;
}
// 歸還連接的方法:
public void addBack(Connection conn){
list.add(conn);
}
...
}

【自定義連接池中問題及如何解決】
? 問題?
1.如果使用自定義連接池,那麽需要額外記住自定義連接池中的API.
2.能不能使用面向接口的編程方式.
? 解決:
不額外提供API方法,就可以解決上述兩個問題!!!
能不能還調用Connection的close方法.能不能增強Connection的close方法,原有的銷毀變為歸還!!!
? 如何增強Connection的close方法:

  • 增強一個Java類中的某個方法有幾種方式???
    • 一種方式:繼承的方式.
      • 能夠控制這個類的構造的時候,才可以使用繼承.
    • 二種方式:裝飾者模式方式.
      • 包裝對象和被包裝的對象都要實現相同的接口.
      • 包裝的對象中需要獲得到被包裝對象的引用.
        ***** 缺點:如果接口的方法比較多,增強其中的某個方法.其他的功能的方法需要原有調用.
    • 三種方式:動態代理的方式.
      • 被增強的對象實現接口就可以.
        【繼承和裝飾者的案例】
        /**
  • 繼承的方式增強一個類中某個方法:
    */
    class Man{
    public void run(){
    System.out.println("跑....");
    }
    }
    class SuperMan extends Man{
    public void run(){
    // super.run();
    System.out.println("飛....");
    }
    }
    /**
  • 使用裝飾者的方式完成類的方法的增強
    */
    interface Waiter{
    public void server();
    }
    class Waiteress implements Waiter{@Override
    br/>@Override
    System.out.println("服務...");
    }

}
class WaiteressWrapper implements Waiter{
private Waiter waiter;
public WaiteressWrapper(Waiter waiter) {
this.waiter = waiter;
}

    @Override
    public void server() {
            System.out.println("微笑...");
            // this.waiter.server();

    }

}
【使用裝飾者模式增強Connection的close方法】
public class MyConnection implements Connection{
private Connection conn;
private List<Connection> list;
public MyConnection(Connection conn,List<Connection> list) {
this.conn = conn;
this.list = list;}
@Override
br/>}
@Override
}
...
}
連接池的getConnection方法:
@Override
br/>list.add(conn);
}
...
}
連接池的getConnection方法:
@Override
public Connection getConnection() throws SQLException {
if(list.size() <= 0){
for(int i=1;i<=3;i++){
Connection conn = JDBCUtils.getConnection();
list.add(conn);
}
}
Connection conn = list.remove(0);
MyConnection myConn = new MyConnection(conn, list);
return myConn;
}
【常見的開源的數據庫連接池】:
? DBCP:
DBCP(DataBase connection pool),數據庫連接池。是 apache 上的一個 java 連接池項目,也是 tomcat 使用的連接池組件。單獨使用dbcp需要2個包:commons-dbcp.jar,commons-pool.jar由於建立數據庫連接是一個非常耗時耗資源的行為,所以通過連接池預先同數據庫建立一些連接,放在內存中,應用程序需要建立數據庫連接時直接到連接池中申請一個就行,用完後再放回去。
? C3P0:
C3P0是一個開源的JDBC連接池,它實現了數據源和JNDI綁定,支持JDBC3規範和JDBC2的標準擴展。目前使用它的開源項目有Hibernate,Spring等。
? Tomcat內置連接池:
【DBCP連接池的使用】
第一步:引入DBCP連接池的jar包.
第二步:編寫DBCP代碼:

  • 手動設置參數:
  • 配置文件設置參數:【DBCP連接池的使用】
    @Test
    br/>【DBCP連接池的使用】
    @Test

    • 手動方式:
      /
      public void demo1(){
      Connection conn = null;
      PreparedStatement stmt = null;
      ResultSet rs = null;
      BasicDataSource dataSource = new BasicDataSource();
      dataSource.setDriverClassName("com.mysql.jdbc.Driver");
      dataSource.setUrl("jdbc:mysql:///web_07");
      dataSource.setUsername("root");
      dataSource.setPassword("123");
      try{
      // 獲得連接:
      conn = dataSource.getConnection();
      // 編寫SQL:
      String sql = "select
      from category";
      // 預編譯SQL:
      stmt = conn.prepareStatement(sql);
      // 執行SQL:
      rs = stmt.executeQuery();
      while(rs.next()){
      System.out.println(rs.getInt("cid")+" "+rs.getString("cname"));
      }
      }catch(Exception e){
      e.printStackTrace();
      }finally{
      JDBCUtils.release(rs,stmt, conn);
      }
      }

      @Test
      /**

    • 配置文件方式:
      */
      public void demo2(){
      Connection conn = null;
      PreparedStatement stmt = null;
      ResultSet rs = null;
      Properties properties = new Properties();

          try{
                  properties.load(new FileInputStream("src/dbcpconfig.properties"));
                  DataSource dataSource = BasicDataSourceFactory.createDataSource(properties);
                  // 獲得連接:
                  conn = dataSource.getConnection();
                  // 編寫SQL:
                  String sql = "select * from category";
                  // 預編譯SQL:
                  stmt = conn.prepareStatement(sql);
                  // 執行SQL:
                  rs = stmt.executeQuery();
                  while(rs.next()){
                          System.out.println(rs.getInt("cid")+"   "+rs.getString("cname"));
                  }
          }catch(Exception e){
                  e.printStackTrace();
          }finally{
                  JDBCUtils.release(rs,stmt, conn);
          }

      }
      【C3P0連接池的使用】
      第一步:引入C3P0連接池的jar包.
      第二步:編寫代碼:

      • 手動設置參數:
      • 配置文件設置參數:
        【C3P0改造工具類】
        public class JDBCUtils2 {
        private static final ComboPooledDataSource DATA_SOURCE =new ComboPooledDataSource();
        /**
    • 獲得連接的方法
      */
      public static Connection getConnection(){
      Connection conn = null;
      try {
      conn = DATA_SOURCE.getConnection();
      } catch (SQLException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
      }
      return conn;
      }
      ...
      }
      1.4 案例三:手動抽取一個DBUtils的工具類:
      1.4.1 需求:
      每次進行JDBC的CURD的操作的時候,有很多的代碼都是相似的.可以不可以抽取工具類.完成一些通用性的代碼?
      1.4.2 分析:
      1.4.2.1 技術分析:
      【JDBC的元數據MataData】(了解)-- 編寫通用性較高的代碼.
      ? DatabaseMetaData:獲得數據庫連接的信息,獲得數據庫的表的信息.
      • 獲得數據庫元數據:Connection中getMetaData();
        ? ParameterMetaData:獲得SQL中的參數的個數及類型.
      • 獲得參數元數據:PreparedStatement中getParameterMetaData()
        ? ResultSetMetaData:獲得結果集中的列名及列的類型.
      • 獲得結果集元數據:ResultSet中getMeta()【元數據的使用】
        @Test
        br/>【元數據的使用】
        @Test
    • 數據庫元數據
      */
      public void demo1(){
      Connection conn = null;
      conn = JDBCUtils2.getConnection();
      // 獲得數據庫元數據:
      try {
      DatabaseMetaData metaData = conn.getMetaData();
      System.out.println("獲得驅動名稱:"+metaData.getDriverName());
      System.out.println("獲得驅動URL:"+metaData.getURL());
      System.out.println("獲得用戶名:"+metaData.getUserName());

                  // 獲得表中的主鍵:
                  ResultSet rs = metaData.getPrimaryKeys(null, null, "category");
                  if(rs.next()){
                          String name = rs.getString("COLUMN_NAME");
                          System.out.println(name);
                  }
          } catch (SQLException e) {
                  e.printStackTrace();
          }

      }

      @Test
      /**

    • 參數元數據:
      */
      public void demo2(){
      Connection conn = null;
      PreparedStatement stmt = null;
      try{
      conn = JDBCUtils2.getConnection();
      String sql = "update category set cname = ? where cid = ?";
      stmt = conn.prepareStatement(sql);
      ParameterMetaData metaData = stmt.getParameterMetaData();
      int count = metaData.getParameterCount();
      System.out.println(count);
      }catch(Exception e){

          }

      }

      @Test
      /**

    • 結果集元數據:
      /
      public void demo3(){
      Connection conn = null;
      PreparedStatement stmt = null;
      ResultSet rs = null;
      try{
      conn = JDBCUtils2.getConnection();
      String sql = "select
      from category";
      stmt = conn.prepareStatement(sql);
      rs = stmt.executeQuery();

                  ResultSetMetaData metaData = rs.getMetaData();
                  int count = metaData.getColumnCount();
                  for(int i = 1;i<=count ;i++){
                          String name = metaData.getColumnName(i);
      
                          String type = metaData.getColumnTypeName(i);
                          System.out.println(name+type);
                  }
          }catch(Exception e){
      
          }

      }

第7章WEB07- JDBC篇