JDBC 學習筆記(四)—— 自定義JDBC框架+Apache—DBUtils框架+事務管理+操做多表

 

 

本文查閱方法:
    一、查閱目錄 —— 查閱本文目錄,肯定想要查閱的目錄標題
    二、快捷「查找」 —— 在當前瀏覽器頁面,按鍵 「Ctrl+F」 按鍵組合,開啓瀏覽器的查找功能,
             在查找搜索框中 輸入須要查閱的 目錄標題,即可以直接到達 標題內容 的位置。
    三、學習小結 —— 文中的學習小結內容,是筆者在學習以後總結出的,開發時可直接參考其進行應用開發的內容, 進一步加快了本文的查閱 速度。(水平有限,僅供參考。)
 java

 


 

 

 

本文目錄sql

 

      學習小結數據庫

 

      一、自定義JDBC框架 ——數據庫元數據:DataBaseMetaDataapache

 

      二、自定義JDBC框架 ——數據庫元數據:DataBaseMetaData數組

 

      三、自定義JDBC框架 ——結果集元數據: ResultSetMetaData瀏覽器

 

      四、使用元數據簡化JDBC代碼app

 

            (1) 萬能更新框架

 

            (2) 萬能查詢工具

 

      五、Apache—DBUtils框架簡介性能

 

      六、DbUtils類 介紹

 

      七、QueryRunner類 介紹

 

      八、QueryRunner類的主要方法

 

      九、ResultSetHandler接口 介紹

 

      十、ResultSetHandler 接口的實現類

 

      十一、JDBC應用的事務管理(ThreadLocal類) 

 

      十二、JDBC應用的事務管理——採用跨層跨層傳遞方法參數

 

      1三、JDBC應用的事務管理—— ThreadLocal 綁定鏈接

 

      1四、使用JDBC操做多個表

 

      1五、使用JDBC操做多個表—— 「一對多」關係 

 

      1六、使用JDBC操做多個表—— 多對多關係

 

      1七、數據庫端——表關係間的級聯操做

 

 

 

相關學習

 

JDBC 學習筆記(一)—— 基礎知識 + 分頁技術

      連接地址:http://even2012.iteye.com/blog/1886946

 

JDBC 學習筆記(二)—— 大數據+存儲過程+批處理+事務

      連接地址:http://even2012.iteye.com/blog/1886950

 

JDBC 學習筆記(三)—— 數據源(數據庫鏈接池):DBCP數據源、C3P0 數據源以及自定義數據源技術

      連接地址:http://even2012.iteye.com/blog/1886953

 

JDBC 學習筆記(四)—— 自定義JDBC框架+Apache—DBUtils框架+事務管理+操做多表  

      連接地址:http://even2012.iteye.com/blog/1886956

 


 

 學習小結

 

 

 

 

 

 

 

 

 


 

 

 

一、自定義JDBC框架 ——數據庫元數據:DataBaseMetaData

 

元數據:數據庫、表、列的定義信息。

 

DataBaseMetaData   connection.getDatabaseMetaData()    

 

得到表明DataBaseMetaData 對象元數據的DataBaseMetaData 對象。

 

    DataBaseMetaData對象中的方法:

 

        (1) getURL():返回一個String類對象,表明數據庫的URL。

 

        (2) getUserName():返回鏈接當前數據庫管理系統的用戶名。

 

        (3) getDatabaseProductName():返回數據庫的產品名稱。

 

        (4) getDatabaseProductVersion():返回數據庫的版本號。

 

        (5) getDriverName():返回驅動驅動程序的名稱。

 

        (6) getDriverVersion():返回驅動程序的版本號。

 

        (7) isReadOnly():返回一個boolean值,指示數據庫是否只容許讀操做。

 

 

 

Demo樣例: 獲取數據庫的元數據 

 

  public void test1() throws SQLException{

 

        Connection conn = JdbcUtils_C3P0.getConnection();

 

        DatabaseMetaData meta = conn.getMetaData();

 

        System.out.println(meta.getDatabaseProductName());

 

        System.out.println(meta.getDatabaseMajorVersion());

 

        System.out.println(meta.getDatabaseMinorVersion());

 

  } 

 

 

 


 

 

 

二、自定義JDBC框架 ——數據庫元數據:DataBaseMetaData

 

ParameterMetaData   PreparedStatement . getParameterMetaData()

 

得到表明PreparedStatement元數據的ParameterMetaData對象。

 

例如:SQL語句 「 Select * from user where name=? And password=?  」 中的兩個「 ?」 問號。

 

ParameterMetaData對象 中的方法:

 

        (1) getParameterCount()    --得到指定參數的個數

 

        (2) getParameterType(int param)    -- 得到指定參數的sql類型(Mysql數據庫不支持該方法,會報異常。)

 

 

 

Demo樣例:參數元數據

 

      public void test2() throws SQLException{

 

            Connection conn = JdbcUtils_C3P0.getConnection();

 

            String sql = "insert into user(id,name) values(?,?)";

 

         

 

            PreparedStatement st = conn.prepareStatement(sql);

 

            ParameterMetaData meta = st.getParameterMetaData();

 

         

 

            System.out.println(meta.getParameterCount());

 

            System.out.println(meta.getParameterType(1)); 

 

      }

 

 

 


 

 

 

三、自定義JDBC框架 ——結果集元數據: ResultSetMetaData

 

ResultSetMetaData   ResultSet. getMetaData()

 

得到表明ResultSet對象元數據的ResultSetMetaData對象。

 

    ResultSetMetaData對象中的方法

 

            (1) getColumnCount()    -- 返回resultset對象的列數

 

            (2) getColumnName(int column)    -- 得到指定列的名稱

 

            (3) getColumnTypeName(int column)    -- 得到指定列的類型

 

 

 

Demo樣例: 結果集元數據

 

      public void test3() throws SQLException{

 

            Connection conn = JdbcUtils_C3P0.getConnection();

 

            String sql = "select * from account";

 

            PreparedStatement st = conn.prepareStatement(sql);

 

            ResultSet rs = st.executeQuery();

 

         

 

            ResultSetMetaData  meta = rs.getMetaData();

 

            System.out.println(meta.getColumnCount());

 

            System.out.println(meta.getColumnName(1));

 

            System.out.println(meta.getColumnName(2));

 

            System.out.println(meta.getColumnName(3));    

 

      }

 

 

 

 


 

  

 

四、使用元數據簡化JDBC代碼

 

業務背景:系統中全部實體對象都涉及到基本的CRUD操做:

 

        (1) 萬能更新

 

        全部實體的CUD操做代碼基本相同,僅僅發送給數據庫的SQL語句不一樣而已,所以能夠把CUD操做的全部相同代碼抽取到工具類的一個update方法中,並定義參數接收變化的SQL語句。

 

 

 

Demo樣例1:萬能更新的方法內容部分

 

    public static void update(String sql,Object params[]) throws SQLException{

 

        Connection conn = null;

 

        PreparedStatement st = null;

 

        ResultSet rs = null;

 

        try{

 

              conn = getConnection();

 

              st = conn.prepareStatement(sql);

 

              for(int i=0;i<params.length;i++){

 

                    st.setObject(i+1,params[i]);

 

              }

 

              st.executeUpdate();

 

        }finally{

 

              release(conn, st, rs);

 

        }

 

  }

 

 

 

Demo樣例2:萬能更新方法的調用代碼

 

    public class CustomerDaoImpl implements CustomerDao { 

 

          public void add(Customer c){

 

                try{

 

                      String sql = "insert into customer(id,name,gender,birthday,cellphone,email,preference,type,description) values(?,?,?,?,?,?,?,?,?)";

 

                      Object params[] = {c.getId(),c.getName(),c.getGender(),c.getBirthday(),c.getCellphone(),c.getEmail(),c.getPreference(),c.getType(),c.getDescription()};

 

                      JdbcUtils.update(sql, params);

 

                }catch (Exception e) {

 

                      throw new DaoException(e);

 

                }

 

          }

 

     

 

          public void update(Customer c){   //id

 

                try{

 

                      String sql = "update customer set name=?,gender=?,birthday=?,cellphone=?,email=?,preference=?,type=?,description=?  where id=?";

 

                      Object params[] = {c.getName(),c.getGender(),c.getBirthday(),c.getCellphone(),c.getEmail(),c.getPreference(),c.getType(),c.getDescription(),c.getId()};

 

                      JdbcUtils.update(sql, params);

 

                }catch (Exception e) {

 

                      throw new DaoException(e);

 

                } 

 

          }

 

     

 

          public void delete(String id){

 

                try{

 

                      String sql = "delete from customer where id=?";

 

                      Object params[] = {id};

 

                      JdbcUtils.update(sql, params);

 

                }catch (Exception e) {

 

                      throw new DaoException(e);

 

                } 

 

          } 

 

    }

 

  

 

        (2) 萬能查詢

 

        實體的R操做,除SQL語句不一樣以外,根據操做的實體不一樣,對ResultSet的映射也各不相同,所以可義一個query方法,除以參數形式接收變化的SQL語句外,可使用策略模式由qurey方法的調用者決定如何把ResultSet中的數據映射到實體對象中。

 

         備註:關於自定義萬能查詢的代碼,涉及到自定義處理器等代碼,上面所述的數據庫元數據的各類知識也都應用到其中,故有些複雜,不便學習。有萬能查詢需求的請學習Apache—DBUtils框架 中的查詢方法部分,相對來講只要會調用便可,學習成本會小一些。

 

 

 

 


 

 

 

五、Apache—DBUtils框架簡介

 

        commons-dbutils 是 Apache 組織提供的一個開源 JDBC工具類庫,它是對JDBC的簡單封裝,學習成本極低,而且使用dbutils能極大簡化jdbc編碼的工做量,同時也不會影響程序的性能。所以dbutils成爲不少不喜歡hibernate的公司的首選。

 

        工具類:    org.apache.commons.dbutils.DbUtils。   

 

 

 

        API介紹:

 

           (1)  org.apache.commons.dbutils.QueryRunner 

 

           (2)  org.apache.commons.dbutils.ResultSetHandler

 

         

 


 

 

 

六、DbUtils類 介紹

 

        DbUtils :提供如關閉鏈接、裝載JDBC驅動程序等常規工做的工具類,裏面的全部方法都是靜態的。主要方法以下:

 

        (1) public static void close(…) throws java.sql.SQLException: DbUtils類提供了三個重載的關閉方法。這些方法檢查所提供的參數是否是NULL,若是不是的話,它們就關閉Connection、Statement和ResultSet。

 

        (2) public static void closeQuietly(…): 這一類方法不只能在Connection、Statement和ResultSet爲NULL狀況下避免關閉,還能隱藏一些在程序中拋出的SQLException。

 

        (3) public static void commitAndCloseQuietly(Connection conn): 用來提交鏈接,而後關閉鏈接,而且在關閉鏈接時不拋出SQL異常。 

 

        (4) public static boolean loadDriver(java.lang.String driverClassName):這一方裝載並註冊JDBC驅動程序,若是成功就返回true。使用該方法,你不須要捕捉這個異常ClassNotFoundException。

 

 

 


 

 

 

七、QueryRunner類 介紹

 

        該類簡單化了SQL查詢,它與ResultSetHandler組合在一塊兒使用能夠完成大部分的數據庫操做,可以大大減小編碼量。

 

        QueryRunner類提供了兩個構造方法:

 

            (1) 默認的構造方法:

 

                    QueryRunner() 

 

            (2) 須要一個 javax.sql.DataSource 來做參數的構造方法。

 

                    QueryRunner(DataSource ds) 

 

 

 


 

 

 

八、QueryRunner類的主要方法

 

        (1) public Object query(Connection conn, String sql, Object[] params, ResultSetHandler rsh) throws SQLException:執行一個查詢操做,在這個查詢中,對象數組中的每一個元素值被用來做爲查詢語句的置換參數。該方法會自行處理 PreparedStatement 和 ResultSet 的建立和關閉。

 

        (2) public Object query(String sql, Object[] params, ResultSetHandler rsh) throws SQLException: 幾乎與第一種方法同樣;惟一的不一樣在於它不將數據庫鏈接提供給方法,而且它是從提供給構造方法的數據源(DataSource) 或使用的setDataSource 方法中從新得到 Connection。         

 

        (3) public Object query(Connection conn, String sql, ResultSetHandler rsh) throws SQLException : 執行一個不須要置換參數的查詢操做。         

 

        (4) public int update(Connection conn, String sql, Object[] params) throws SQLException:用來執行一個更新(插入、更新或刪除)操做。        

 

        (5) public int update(Connection conn, String sql) throws SQLException:用來執行一個不須要置換參數的更新操做。

 

 

 

Demo樣例:使用dbutils完成數據庫的crud 

 

public class Demo1 { 

 

  /*

 

   create database day17;

 

   use day17;

 

   create table users(

 

    id int primary key,

 

    name varchar(40),

 

    password varchar(40),

 

    email varchar(60),

 

    birthday date

 

  );

 

   */ 

 

  @Test

 

  public void insert() throws SQLException{

 

    QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());

 

    String sql = "insert into users(id,name,password,email,birthday) values(?,?,?,?,?)";

 

    Object params[] = {2,"bbb","123","aa@sina.com",new Date()};

 

    runner.update(sql, params);

 

  }

 

 

 

  @Test

 

  public void update() throws SQLException{

 

    QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());

 

    String sql = "update users set email=? where id=?";

 

    Object params[] = {"aaaaaa@sina.com",1};

 

    runner.update(sql, params);

 

  }

 

 

 

  @Test

 

  public void delete() throws SQLException{

 

    QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());

 

    String sql = "delete from users where id=?";

 

    runner.update(sql, 1);

 

  }

 

 

 

  @Test

 

  public void find() throws SQLException{

 

    QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());

 

    String sql = "select * from users where id=?";

 

    User user = (User) runner.query(sql, 1, new BeanHandler(User.class));

 

    System.out.println(user.getEmail());

 

  }

 

 

 

 

 

  @Test

 

  public void getAll() throws Exception{

 

    QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());

 

    String sql = "select * from users";

 

    List list = (List) runner.query(sql, new BeanListHandler(User.class));

 

    System.out.println(list);

 

  }

 

 

 

  @Test

 

  public void batch() throws SQLException{

 

    QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());

 

    String sql =  "insert into users(id,name,password,email,birthday) values(?,?,?,?,?)";

 

    Object params[][] = new Object[3][5];

 

    for(int i=0;i<params.length;i++){  //3

 

      params[i] = new Object[]{i+1,"aa"+i,"123",i + "@sina.com",new Date()};

 

    }

 

    runner.batch(sql, params);

 

  }

 

}

 

 

 

 


 

  

 

九、ResultSetHandler接口 介紹

 

        該接口用於處理 java.sql.ResultSet,將數據按要求轉換爲另外一種形式。

 

        ResultSetHandler 接口提供了一個單獨的方法:Object handle (java.sql.ResultSet .rs)。

 

        

 


 

 

 

十、ResultSetHandler 接口的實現類

 

        (1) ArrayHandler( ):把結果集中的第一行數據轉成對象數組。

 

        (2) ArrayListHandler( ):把結果集中的每一行數據都轉成一個數組,再存放到List中。

 

        (3) BeanHandler(Class type) :將結果集中的第一行數據封裝到一個對應的JavaBean實例中。

 

        (4) BeanListHandler(Class type) :將結果集中的每一行數據都封裝到一個對應的JavaBean實例中,存放到List裏。

 

        (5) ColumnListHandler(int columnIndex / String columnName):將結果集中某一列的數據存放到List中。

 

        (6) KeyedHandler( int columnIndex / String columnName ):將結果集中的每一行數據都封裝到一個Map裏,再把這些map再存到一個map裏,並將其columnName的值做爲指定的key。

 

        (7) MapHandler( ):將結果集中的第一行數據封裝到一個Map裏,key是列名,value就是對應的值。

 

        (8) MapListHandler( ):將結果集中的每一行數據都封裝到一個Map裏,而後再存放到List

 

        (9)  ScalarHandler( ):將結果集中的某一列 裝入到一個對象中。

 

 

 

Demo樣例:測試dbutils的各個結果集處理器

 

public class Demo2 {   

 

  @Test

 

  public void test1() throws SQLException{

 

    QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());

 

    String sql = "select * from users where id=?";

 

    Object result[] = (Object[]) runner.query(sql,1, new ArrayHandler());

 

    System.out.println(result[0]);

 

    System.out.println(result[1]);

 

  }  

 

 

 

  @Test

 

  public void test2() throws SQLException{

 

    QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());

 

    String sql = "select * from users";

 

    List list = (List) runner.query(sql, new ArrayListHandler());

 

    System.out.println(list);

 

  }

 

 

 

  @Test

 

  public void test3() throws SQLException{

 

    QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());

 

    String sql = "select * from users";

 

    List list = (List) runner.query(sql, new ColumnListHandler1("name"));

 

    System.out.println(list);

 

  }  

 

 

 

  @Test

 

  public void test4() throws SQLException{

 

    QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());

 

    String sql = "select * from users";

 

    Map<Integer,Map<String,Object>> map = (Map) runner.query(sql, new KeyedHandler("id"));

 

    for(Map.Entry<Integer,Map<String,Object>> me : map.entrySet()){

 

      int id = me.getKey();

 

      for(Map.Entry<String, Object> entry : me.getValue().entrySet()){

 

        String name = entry.getKey();

 

        Object value = entry.getValue();

 

        System.out.println(name + "=" + value);

 

      }

 

    }

 

  }

 

 

 

  @Test  //獲取總記錄數。

 

  public void test5() throws SQLException{

 

    QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());

 

    String sql = "select count(*) from users";

 

    /* 方式一:

 

    Object result[] = (Object[]) runner.query(sql, new ArrayHandler());

 

    long totalrecord = (Long)result[0];

 

    int num = (int)totalrecord;

 

    System.out.println(num);

 

    int totalrecord = ((Long)result[0]).intValue();

 

    */

 

 

 

    //方式二:

 

    int totalrecord = ((Long)runner.query(sql, new ScalarHandler(1))).intValue();

 

    System.out.println(totalrecord);

 

  }

 

}

 

 

 

//自定義

 

class ColumnListHandler1 implements ResultSetHandler{ 

 

  private String columnName;

 

  public ColumnListHandler1(String columnName){

 

    this.columnName = columnName;

 

  }

 

  public Object handle(ResultSet rs) throws SQLException {

 

    List list = new ArrayList();

 

    while(rs.next()){

 

      list.add(rs.getObject(columnName));

 

    }

 

    return list;

 

  }

 

}

 

 

 


 

  

 

十一、JDBC應用的事務管理(ThreadLocal類) 

 

JDBC 應用的事務管理——Service層和Dao層事務的傳遞。

 

    方式一:跨層傳遞方法參數——在Service層建立開啓事務的鏈接,並傳遞到Dao層,最後在Service層提交事務;

 

    方式二:ThreadLocal 綁定鏈接——使用ThreadLocal進行事務管理——ThreadLocal能夠實如今線程範圍內實現數據共享。

 

     方式三:使用Spring進行事務管理;(在Spring 博文中講解。)

 

     

 

                    

 

 

 


 

 

 

十二、JDBC應用的事務管理——採用跨層跨層傳遞方法參數

 

思想:在Service層建立開啓事務的鏈接,並傳遞到Dao層,最後在Service層提交事務;

 

 

 

Demo樣例1:Service層(Dao層中只要在方法中參數中接收該 鏈接參數 就行了)

 

public class BusinessService {  

 

  /*  

 

  create table account(

 

    id int primary key auto_increment,

 

    name varchar(40),

 

    money float

 

  )character set utf8 collate utf8_general_ci;

 

 

 

  insert into account(name,money) values('aaa',1000);

 

  insert into account(name,money) values('bbb',1000);

 

  insert into account(name,money) values('ccc',1000); 

 

  */  

 

 

 

  public void transfer1(int sourceid,int targetid,double money) throws SQLException{

 

 

 

    Connection conn = null;

 

    try{

 

      // 獲取鏈接並開啓事務。

 

      conn = JdbcUtils.getConnection();

 

      conn.setAutoCommit(false);

 

 

 

      // 將開啓事務的鏈接傳遞到各層。

 

      AccountDao dao = new AccountDao(conn);

 

 

 

      Account a = dao.find(sourceid);   //select

 

      Account b = dao.find(targetid);   //select

 

 

 

      a.setMoney(a.getMoney()-money);  

 

      b.setMoney(b.getMoney()+money);   

 

 

 

      dao.update(a); //update      

 

      dao.update(b);//update

 

 

 

      // 提交事務。

 

      conn.commit();

 

    }finally{

 

      // 關閉鏈接。

 

      if(conn!=null) conn.close();

 

    }

 

  }

 

 

 

 

 

 

 


 

 

 

 

 

1三、JDBC應用的事務管理—— ThreadLocal 綁定鏈接

 

思想:在Service層將開啓事務的鏈接綁定到ThreadLocal中,在當前線程所途徑的其餘各層從ThreadLocal中獲取鏈接並進行操做,最後線程返回至Service層時,再提交事務,移除綁定的連接。

 

 

 

 

Demo樣例1:將 使用ThreadLocal 綁定鏈接 的代碼封裝成工具類。

 

public class JdbcUtils {

 

  private static DataSource ds;

 

 

 

  // 爲保證各層的類所使用的ThreadLocal是同一個,建議將其設定成靜態的,可是必定要記得使用後要移出綁定在上面的對象。

 

  private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();  // 其實就是一個Map集合

 

 

 

  static{

 

    try{

 

      Properties prop = new Properties();

 

      InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");

 

      prop.load(in);

 

      BasicDataSourceFactory factory = new BasicDataSourceFactory();

 

      ds = factory.createDataSource(prop);

 

    }catch (Exception e) {

 

      throw new ExceptionInInitializerError(e);

 

    }

 

  }

 

 

 

  public static DataSource getDataSource(){

 

    return ds;

 

  }

 

 

 

  // 備註:該獲取鏈接的方法,僅當使用ThreadLocal來管理事務鏈接的狀況,由於向靜態對象ThreadLocal中綁定了對象,因此當咱們不須要管理事務的普通獲取鏈接的方法,就不要用此方法。應該用普通的獲取鏈接的方法。

 

  public static Connection getConnection() throws SQLException{

 

    try{

 

      //獲得當前線程上綁定的鏈接

 

      Connection conn = tl.get();

 

      if(conn==null){  //表明線程上沒有綁定鏈接

 

        conn = ds.getConnection();

 

        tl.set(conn);

 

      }

 

      return conn;

 

    }catch (Exception e) {

 

      throw new RuntimeException(e);

 

    }

 

  } 

 

 

 

  public static void startTransaction(){

 

    try{

 

      //獲得當前線程上綁定鏈接開啓事務

 

      Connection conn = tl.get();

 

      if(conn==null){  //表明線程上沒有綁定鏈接

 

        conn = ds.getConnection();

 

        tl.set(conn);

 

      }

 

      conn.setAutoCommit(false);

 

    }catch (Exception e) {

 

      throw new RuntimeException(e);

 

    }

 

  } 

 

 

 

  public static void commitTransaction(){

 

    try{

 

      Connection conn = tl.get();

 

      if(conn!=null){

 

        conn.commit();

 

      }

 

    }catch (Exception e) {

 

      throw new RuntimeException(e);

 

    }

 

  }

 

 

 

  public static void closeConnection(){

 

    try{

 

      Connection conn = tl.get();

 

      if(conn!=null){

 

        conn.close();

 

      }

 

    }catch (Exception e) {

 

      throw new RuntimeException(e);

 

    }finally{

 

      tl.remove();   //千萬注意,解除當前線程上綁定的連接(從threadlocal容器中移除對應當前線程的連接)

 

    }

 

  }

 

}

 

 

 

Demo樣例2: 採用 ThreadLocal 綁定鏈接 來管理事務的 Service層的代碼。

 

public class BusinessService {  

 

  /*  

 

  create table account(

 

    id int primary key auto_increment,

 

    name varchar(40),

 

    money float

 

  )character set utf8 collate utf8_general_ci;

 

 

 

  insert into account(name,money) values('aaa',1000);

 

  insert into account(name,money) values('bbb',1000);

 

  insert into account(name,money) values('ccc',1000); 

 

  */  

 

  //用上ThreadLocal的事務管理

 

  public void transfer2(int sourceid,int targetid,double money) throws SQLException{

 

 

 

    try{

 

      JdbcUtils.startTransaction();

 

      AccountDao dao = new AccountDao();

 

      Account a = dao.find(sourceid);   //select

 

      Account b = dao.find(targetid);   //select

 

      a.setMoney(a.getMoney()-money);  

 

      b.setMoney(b.getMoney()+money);   

 

      dao.update(a); //update

 

      dao.update(b);//update

 

      JdbcUtils.commitTransaction();

 

    }finally{

 

      JdbcUtils.closeConnection();

 

    }

 

  }

 

}

 

 


 

  

 

1四、使用JDBC操做多個表

 

        (1) 使用JDBC操做多表的步驟

 

                (a)  明確對象的屬性,及之間的關聯關係。

 

                (b)  明確表關係, 建立數據庫及表;

 

                (c)  編碼Dao層的代碼(重點是增刪改查時涉及到的級聯操做。)

 

                (d)  編碼Service層的代碼(重點是 複雜對象  的級聯操做。)

 

        

 

        (2)  O-R Mapping 映射的注意事項

 

                (a) 無論java的對象存在何種關係,反映到關係型數據庫中,都是使用外鍵表示紀錄(即對象)的關聯關係。

 

                (b) 設計java對象如涉及到多個對象相互引用,要儘可能避免使用一對多,或多對多關係,而應使用多對一描述對象之間的關係(或使用延遲加載的方式)。以免查詢出了全部「一對多」中 「多」的數據,容易形成內存溢出

 

                (c) 特殊狀況下(好比訂單--訂單項)必須設計成「一對多」關係時,當「多」的一方數據較少時,可使用級聯查詢,但如果「多」的一方數據量較大時,則建議使用 「分頁方式」查詢。 

 

        

 

         (3)  「一對多」多表關聯關係的設計方法:

 

                (a) 先將每張表各自的基本屬性信息列好;

 

                (b) 再將「一對多」 多的一方中設定外鍵列(並添加外鍵約束,以維護兩表之間的關係)。

 

         

 

        (4) 經常使用O-R Mapping映射工具

 

                (a) Hibernate

 

                (b) Ibatis

 

                (c) Commons DbUtils(只是對JDBC簡單封裝)

 

         

 

 


 

 

 

 

 

1五、使用JDBC操做多個表—— 一對多關係 (例如:部門和員工)

 



 

 

 

 

 

 

Demo樣例1:Dao層的代碼

 

public class DepartmentDao { 

 

  /*

 

    多表設計原則

 

    一、現將各表各自的基本屬性信息列好;

 

    二、再將「一對多」 多的一方中設定外鍵列(並添加外鍵約束,以維護兩表之間的關係)。

 

 

 

    create table department

 

    (

 

      id varchar(40) primary key,

 

      name varchar(40)

 

    ); 

 

    create table employee

 

    (

 

      id varchar(40) primary key,

 

      name varchar(40),

 

      salary double,

 

      department_id varchar(40),

 

      constraint department_id_FK foreign key(department_id) references department(id)

 

    );

 

 

 

    alter table employee drop foreign key department_id_FK;

 

    alter table employee add constraint department_id_FK foreign key(department_id) references department(id) on delete set null;

 

 

 

    alter table employee drop foreign key department_id_FK;

 

    alter table employee add constraint department_id_FK foreign key(department_id) references department(id) on delete cascade; 

 

   */

 

 

 

  public void add(Department d) throws SQLException{ 

 

    QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());

 

 

 

    //1.把department對象的數據插入到department表

 

    String sql = "insert into department(id,name) values(?,?)";

 

    Object params[] = {d.getId(),d.getName()};

 

    runner.update(sql, params);

 

 

 

    //2.把department對象中維護的全部員工插入到員工表

 

    Set<Employee> set = d.getEmployees();

 

    for(Employee e : set){

 

      sql = "insert into employee(id,name,salary,department_id) values(?,?,?,?)";

 

      params = new Object[]{e.getId(),e.getName(),e.getSalary(),d.getId()};

 

      runner.update(sql, params);

 

    }

 

 

 

    //3.更新員工表的外鍵列,說明員工的部門(本例中的ID能夠實現給定,固就不須要進行更新外鍵列操做;但如果涉及到獲取 自動生成主鍵 的案例時,則須要 涉及到更新外鍵操做)。

 

    

 

  }

 

 

 

 

 

  //該方法查詢出了全部「一對多」中 「多」的數據,容易形成內存溢出。當「多」的一方數據較少時,可使用該級聯查詢,但如果「多」的一方數據量較大時,則建議使用 「分頁方式」查詢。

 

  public Department find(String id) throws SQLException{

 

 

 

    QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());

 

 

 

    //1.找部門表,查出部門的基本信息

 

    String sql = "select * from department where id=?";

 

    Department d = (Department) runner.query(sql, id, new BeanHandler(Department.class));

 

 

 

    //2.找員工表,找出部門下面全部員工

 

    sql = "select * from employee where department_id=?";

 

    List list = (List) runner.query(sql, id, new BeanListHandler(Employee.class));

 

 

 

    d.getEmployees().addAll(list);    // 注:set集合的addAll() 是將全部的值逐個取出來,再逐一存入到Set集合中;而set集合的add()方法則是替換一Set集合的引用。

 

 

 

    return d;

 

  }

 

  //111

 

  public void delete(String id) throws SQLException{

 

    QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());

 

    String sql= "delete from department where id=?";

 

    runner.update(sql, id);

 

  }

 

}

 

 

 

Demo樣例2:Service層的代碼 

 

public class BService { 

 

@Test

 

  public void add() throws SQLException{ 

 

    Department d = new Department();

 

    d.setId("111");

 

    d.setName("開發部");

 

 

 

    Employee e1 = new Employee();

 

    e1.setId("1");

 

    e1.setName("aa");

 

    e1.setSalary(10000); 

 

 

 

    Employee e2 = new Employee();

 

    e2.setId("2");

 

    e2.setName("bb");

 

    e2.setSalary(10000);

 

  

 

    d.getEmployees().add(e1);

 

    d.getEmployees().add(e2); 

 

 

 

    DepartmentDao dao = new DepartmentDao();

 

    dao.add(d);

 

  }

 

 

 

  @Test

 

  public void find() throws SQLException{

 

    DepartmentDao dao = new DepartmentDao();

 

    Department d = dao.find("111");

 

    System.out.println(d);

 

  }

 

 

 

  @Test

 

  public void delete() throws SQLException{

 

    DepartmentDao dao = new DepartmentDao();

 

    dao.delete("111");

 

  }

 

 }

 

 

 


 

 

 

1六、使用JDBC操做多個表—— 多對多關係 (老師和學生)

 



 

 

 

 

Demo樣例1:Dao層的代碼

 

public class TeacherDao { 

 

  /*

 

  create table teacher

 

  (

 

    id varchar(40) primary key,

 

    name varchar(40),

 

    salary double

 

  ) ;

 

 

 

  create table student

 

  (

 

    id varchar(40) primary key,

 

    name varchar(40)

 

  );

 

 

 

   create table teacher_student

 

   (

 

     teacher_id varchar(40),

 

     student_id varchar(40),

 

     primary key(teacher_id,student_id),

 

     constraint teacher_id_FK foreign key(teacher_id) references teacher(id), 

 

     constraint student_id_FK foreign key(student_id) references student(id)

 

   );

 

 

 

   alter table teacher_student drop foreign key teacher_id_FK;

 

   alter table teacher_student add constraint teacher_id_FK foreign key(teacher_id) references teacher(id) on delete cascade;  

 

 

 

   alter table teacher_student drop foreign key student_id_FK;

 

   alter table teacher_student add constraint student_id_FK foreign key(student_id) references student(id) on delete cascade;

 

  */

 

 

 

  public void add(Teacher t) throws SQLException {

 

 

 

    QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());

 

 

 

    //1`.取出老師存老師表

 

    String sql = "insert into teacher(id,name,salary) values(?,?,?)";

 

    Object params[] = {t.getId(),t.getName(),t.getSalary()};

 

    runner.update(sql, params);

 

 

 

 

 

    //2.取出老師全部學生的數據,存學生表

 

    Set<Student> set = t.getStudents();

 

    for(Student s : set){

 

      sql = "insert into student(id,name) values(?,?)";

 

      params = new Object[]{s.getId(),s.getName()};

 

      runner.update(sql, params);

 

 

 

      //3.更新中間表,說明老師和學生的關係

 

      sql = "insert into teacher_student(teacher_id,student_id) values(?,?)";

 

      params = new Object[]{t.getId(),s.getId()};

 

      runner.update(sql, params);

 

    }

 

  }

 

 

 

  public Teacher find(String id) throws SQLException{

 

 

 

    QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());

 

 

 

    //1.找老師表,找出老師的基本信息

 

    String sql = "select * from teacher where id=?";

 

    Teacher t = (Teacher) runner.query(sql, id, new BeanHandler(Teacher.class));

 

 

 

    //2.找出老師的全部學生    ()

 

    //sql = "select s.* from teacher_student ts,student s where ts.teacher_id=? and ts.student_id=s.id";

 

    sql = "select s.* from teacher_student ts,student s where ts.teacher_id=? and ts.student_id=s.id";

 

    List list = (List) runner.query(sql, id, new BeanListHandler(Student.class));

 

 

 

 

 

    t.getStudents().addAll(list);

 

    return t;

 

  }

 

 

 

  public void delete(String id){

 

    QueryRunner runner = new QueryRunner(JdbcUtils.getDataSource());

 

    String sql = "delete from teacher where id=?";

 

 

 

  }

 

}

 

 

 

Demo樣例2:Service層的代碼 

 

public class BService {

 

@Test

 

 public void addTeacher() throws SQLException{

 

 

 

  Teacher t = new Teacher();

 

  t.setId("1");

 

  t.setName("老張");

 

  t.setSalary(100000);

 

 

 

  Student s1 = new Student();

 

  s1.setId("1");

 

  s1.setName("aa");

 

 

 

  Student s2 = new Student();

 

  s2.setId("2");

 

  s2.setName("bb");

 

 

 

  t.getStudents().add(s1);

 

  t.getStudents().add(s2); 

 

 

 

  TeacherDao dao = new TeacherDao();

 

  dao.add(t);

 

 }

 

 

 

 @Test

 

 public void findTeacher() throws SQLException{

 

  TeacherDao dao = new TeacherDao();

 

  Teacher t = dao.find("1");

 

  System.out.println(t);

 

 }

 

 

 

}

 

 

 

 


 

  

 

1七、數據庫端——表關係間的級聯操做

 

表關係間的級聯操做:

 

    REFERENCES tbl_name [(index_col_name,...)]

 

               [MATCH FULL | MATCH PARTIAL | MATCH SIMPLE]

 

               [ON DELETE reference_option]  (級聯刪除)  

 

               [ON UPDATE reference_option]  (級聯修改)

 

 

 

reference_option的可選值:

 

    RESTRICT | CASCADE(刪除) | SET NULL(置空) | NO ACTION 

 

 

 

Demo:給表添加外鍵約束——包含級聯刪除(置空)的關係

 

   alter table employee add constraint department_id_FK foreign key(department_id) references department(id) on delete set null ; 

 

   alter table employee add constraint department_id_FK foreign key(department_id) references department(id) on delete set null;

 

    alter table employee add constraint department_id_FK foreign key(department_id) references department(id) on delete cascade; 

 

 

 

 

 


 

 

 

 敬請評論(1)若您以爲本文 有用處  —— 請留言評論,以堅決其餘 IT童鞋 閱讀本文的信心。(2)若您以爲本文 沒用處  —— 請留言評論,筆者將會改進不足,以便爲你們整理更加好用的筆記。 

相關文章
相關標籤/搜索