jdbc-dao

来源:互联网 发布:连接tor网络失败 编辑:程序博客网 时间:2024/06/05 00:13

JDBC DAO设计

Connection的含义
    Connection表示了一个和数据库的链接,底层需要有操作系统的Socket支持,所以Connection是一种资源,既然是一种资源,就需要按照建立,打开,使用,关闭的顺序合理的使用。
    Connection是Java数据库操作的基础,是进行一系列操作的基础,所有的派生的操作,例如Statement,PreparedStatement,ResultSet等都由Connection直接或者间接的衍生。

    如何获得Connection呢?
    方法一,使用DriverManager类来获取,前提条件是数据库驱动程序需要在classpath下(即使用数据库链接的程序按照Java的方式可以访问到)。
       Connection conn = DriverManager.getConnection( "jdbc:oracle:thin:@192.168.0.1:1521:ORCL", user, pwd );
    方法二,使用数据库连接池来获取
       什么是数据库连接池呢,数据库连接池是标准JavaEE容器的一种服务,例如Webspher,Weblogic,Tomcat等,容器预先建立一些数据 库链接,以便应用程序使用的时候从中借取,注意有借有还,当应用程序使用完了之后会将数据库链接还回连接池。(数据源配置请参考其他文档)
       使用连接池的好处是,可以预先建立链接,减小在数据库获取上的相对时间。
       使用连接池获取数据库链接的方式为:
           InitialContext ctx = new InitialContext();
           DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/DataSource");
           Connection conn = ds.getConnection();
       由于在配置数据库连接池的时候已经定义了URL,用户名,密码等信息,所以在程序中使用的时候不需要传入这些信息。

ConnectionManager定义
    Connection用来专门管理数据库链接,通常情况下ConnectionManager只有一个方法,调用这个方法将返回一个Connection 的实例。通过ConnectionManager可以封装Connection的获取方式(例如开发的时候使用DriverManager,运用的时候使 用DataSource的方式,但是不需要修改ConnectionManager之外的其他代码)和追加Connection获取之前之后的操作(例如 针对Connection的属性的设置)。
    下面的代码是一个ConnectionManager的代码示例:

Java代码  收藏代码
  1. package com.jpleasure.jdbc.dao;  
  2.   
  3. import java.sql.Connection;  
  4. import java.sql.DriverManager;  
  5. import java.sql.SQLException;  
  6.   
  7. public class ConnectionManager {  
  8.       
  9.     public static Connection getConnection() throws DaoException {  
  10.         Connection conn = null;  
  11.         try {  
  12.             conn = DriverManager.getConnection("""""");  
  13.         } catch (SQLException e) {  
  14.             throw new DaoException("can not get database connection", e);  
  15.         }  
  16.         return conn;  
  17.     }  
  18. }  

 
    如果需要从开发模式变为运用模式,只需要将上述代码修改为:

Java代码  收藏代码
  1. package com.jpleasure.jdbc.dao;  
  2.   
  3. import java.sql.Connection;  
  4. import java.sql.DriverManager;  
  5. import java.sql.SQLException;  
  6.   
  7. public class ConnectionManager {  
  8.       
  9.     public static Connection getConnection() throws DaoException {  
  10.         Connection conn = null;  
  11.         try {  
  12.              Context ctx = new InitialContext();  
  13.              DataSource ds = (DataSource)ctx.lookup("jdbc/dsname");  
  14.              conn = ds.getConnection();  
  15.           } catch(NamingException e) {  
  16.              throw new DaoException("can not find datasource", e);  
  17.           }catch (SQLException e) {  
  18.             throw new DaoException("can not get database connection", e);  
  19.         }   
  20.         return conn;  
  21.     }  
  22. }  

 
如果需要预先设定Connection的一些属性,也可以在上述代码中设定,例如:

Java代码  收藏代码
  1. package com.jpleasure.jdbc.dao;  
  2.   
  3. import java.sql.Connection;  
  4. import java.sql.DriverManager;  
  5. import java.sql.SQLException;  
  6.   
  7. public class ConnectionManager {  
  8.       
  9.     public static Connection getConnection() throws DaoException {  
  10.         Connection conn = null;  
  11.         try {  
  12.               Context ctx = new InitialContext();  
  13.               DataSource ds = (DataSource)ctx.lookup("jdbc/dsname");  
  14.               conn = ds.getConnection();  
  15.               conn.setAutoCommit(false);  
  16.           } catch(NamingException e) {  
  17.              throw new DaoException("can not find datasource", e);  
  18.           }catch (SQLException e) {  
  19.             throw new DaoException("can not get database connection", e);  
  20.         }   
  21.         return conn;  
  22.     }  
  23. }  

 
CommonDao定义
    属性和构造方法
       通常情况下,CommonDao要有一个Connection的引用。所有一个CommonDao的实例的所有方法的调用都需要依赖于这个 Connection。需要一个Connection的另外一个原因是如果各个方法需要保证在一个事务环境中(上下文中),必须保证所有的操作都在一个 Connection上。
       构造方法通常需要将类型为Connection的属性实例化,例如:

Java代码  收藏代码
  1. package com.jpleasure.jdbc.dao;  
  2.   
  3. import java.sql.Connection;  
  4.   
  5. public class CommonDao {  
  6.       
  7.     private Connection conn;  
  8.       
  9.     public CommonDao() throws DaoException {  
  10.         this.conn = ConnectionManager.getConnection();  
  11.     }      
  12. }  

 
    
    事务方法
        begin()
          开始一个事务,调用CommonDao的begin方法之后,所以的后续操作将会在一个事务环境内,要么全部提交,要么全部回滚。

        commit()
          提交一个事务,必须在begin调用之后调用。且和rollback方法互斥。

        rollback()   
          回滚一个事务,必须在begin方法调用之后调用。且和commit方法互斥。
    
       事务的实现有两种方法,一种是使用基于单一Connection的事务,另外一种方法是使用容器的JTA(Java Transaction API)。需要注意的是第一种方法可以在任何环境下使用,但是只能是针对单一的数据库链接。第二种方法智能在支持JTA的Java EE容器中使用(例如Websphere,Weblogic等,Tomcat默认不支持),但是支持多个Connection实例。
    第一种方法代码为:

Java代码  收藏代码
  1. package com.jpleasure.jdbc.dao;  
  2.   
  3. import java.sql.Connection;  
  4. import java.sql.SQLException;  
  5.   
  6. public class CommonDao {  
  7.       
  8.     private Connection conn;  
  9.       
  10.     public CommonDao() throws DaoException {  
  11.         this.conn = ConnectionManager.getConnection();  
  12.     }      
  13.   
  14.     public void begin() throws DaoException{  
  15.         if(conn != null) {  
  16.             try {  
  17.                 conn.setAutoCommit(false);  
  18.             } catch (SQLException e) {  
  19.                 throw new DaoException("can not begin transaction", e);  
  20.             }  
  21.         } else {  
  22.             throw new DaoException("connection not opened!");  
  23.         }  
  24.     }  
  25.       
  26.     public void commit() throws DaoException {  
  27.         try {  
  28.             if (conn != null && !conn.getAutoCommit()) {  
  29.                 conn.commit();  
  30.                 conn.setAutoCommit(true);  
  31.             } else {  
  32.                 if (conn == null) {  
  33.                     throw new DaoException("connection not opened!");  
  34.                 } else {  
  35.                     throw new DaoException("first begin then commit please!");  
  36.                 }  
  37.             }  
  38.         } catch (SQLException e) {  
  39.             throw new DaoException("can not commit transaction!", e);  
  40.         }  
  41.     }  
  42.       
  43.     public void rollback() throws DaoException {  
  44.         try {  
  45.             if (conn != null && !conn.getAutoCommit()) {  
  46.                 conn.rollback();  
  47.                 conn.setAutoCommit(true);  
  48.             } else {  
  49.                 if (conn == null) {  
  50.                     throw new DaoException("connection not opened!");  
  51.                 } else {  
  52.                     throw new DaoException("first begin then rollback please!");  
  53.                 }  
  54.             }  
  55.         } catch (SQLException e) {  
  56.             throw new DaoException("can not rollback transaction!", e);  
  57.         }  
  58.     }  
  59.       
  60.       
  61. }  

 
    第二种我们在使用DAO的实例中介绍如何使用(@TODO)
       新建两个DAO,做不同的操作,使用JTA保证事务完整。

    查询方法
       查询方法也许是CommonDao最常用的方法,查询方法需要将数据库的结果返回给画面。返回值我们一般不使用ResultSet,因为 ResultSet依赖于Connection,如果Connection关闭,ResultSet将不再有效,所以我们通常将ResultSet转变为 一个List之后返回。
       在说明查询方法之前,我们先说说如何将数据库中的内容放在List中,我们使用一个List表示一个查询结果集合,使用一个Map表示集合中的一行,Map的key表示数据库表的字段名字,Value表示数据库字段的内容。代码为:     

Java代码  收藏代码
  1. private List convert(ResultSet rs) throws DaoException {  
  2.   
  3.        // record list  
  4.        List retList = new ArrayList();  
  5.   
  6.        try {  
  7.            ResultSetMetaData meta = rs.getMetaData();  
  8.   
  9.            // column count  
  10.            int colCount = meta.getColumnCount();  
  11.   
  12.            // each record  
  13.            while (rs.next()) {  
  14.   
  15.                Map recordMap = new HashMap();  
  16.   
  17.                // each column  
  18.                for (int i = 1; i <= colCount; i++) {  
  19.                    // column name  
  20.                    String name = meta.getColumnName(i);  
  21.                    // column value  
  22.                    Object value = rs.getObject(i);  
  23.                    // add column to record  
  24.                    recordMap.put(name, value);  
  25.                }  
  26.                // ad record to list  
  27.                retList.add(recordMap);  
  28.            }  
  29.        } catch (SQLException ex) {  
  30.            throw new DaoException("can not convert result set to list of map", ex);  
  31.        }  
  32.        return retList;  
  33.    }  

 
    为了避免Sql注入的安全问题,我们通常使用PreparedStatement,在使用PreparedStatement的时候涉及到如何将传入参数设置到PreparedStatement上面,参看以下的共通方法:

Java代码  收藏代码
  1. private void apply(PreparedStatement pstmt, List params) throws DaoException {  
  2.     try {  
  3.         // if params exist  
  4.         if (params != null && params.size() > 0) {  
  5.             // parameters iterator  
  6.             Iterator it = params.iterator();  
  7.               
  8.             // parameter index  
  9.             int index = 1;  
  10.             while(it.hasNext()) {  
  11.                   
  12.                 Object obj = it.next();  
  13.                 // if null set ""  
  14.                 if (obj == null) {  
  15.                     pstmt.setObject(index, "");  
  16.                 } else {  
  17.                     // else set object  
  18.                     pstmt.setObject(index, obj);  
  19.                 }  
  20.                   
  21.                 //next index  
  22.                 index++;  
  23.             }  
  24.         }  
  25.     } catch (SQLException ex) {  
  26.         throw new DaoException("can not apply parameter", ex);  
  27.     }  
  28. }  

 
    接着我们继续说我们的查询方法,有了上述两个方法,我们的查询方法就非常简单了:   

Java代码  收藏代码
  1. public List query(String sql, List params) throws DaoException {  
  2.         List result = null;  
  3.         PreparedStatement pstmt = null;  
  4.         ResultSet rs = null;  
  5.         try {  
  6.             pstmt = conn.prepareStatement(sql);  
  7.             this.apply(pstmt, params);  
  8.             rs = pstmt.executeQuery();  
  9.             result = this.convert(rs);  
  10.         } catch (SQLException ex) {  
  11.             throw new DaoException("can not execute query", ex);  
  12.         } finally {  
  13.             if (rs != null) {  
  14.                 try {  
  15.                     rs.close();  
  16.                 } catch (SQLException e) {  
  17.                     // nothing  
  18.                 }  
  19.             }  
  20.             if (pstmt != null) {  
  21.                 try {  
  22.                     pstmt.close();  
  23.                 } catch (SQLException e) {  
  24.                     // nothing  
  25.                 }  
  26.             }  
  27.         }  
  28.   
  29.         return result;  
  30.     }  
  31.       

 
    特殊的查询方法(返回单值)
    有时候为了方便使用,我们需要返回单值的产寻方法,例如 select max(id) from table_a, select count(id) from table_b等。以下的代码使用了上述通用的查询方法,代码为:

Java代码  收藏代码
  1. public Object queryOne(String sql, List params) throws DaoException {  
  2.       List list = this.query(sql, params);  
  3.         
  4.       if(list == null || list.size() == 0) {  
  5.           throw new DaoException("data not exist");  
  6.       } else {  
  7.           Map record = (Map)list.get(0);  
  8.           if(record == null || record.size() == 0 ) {  
  9.               throw new DaoException("data not exist");  
  10.           } else {  
  11.               return record.values().toArray()[0];  
  12.           }  
  13.       }  
  14.   }  
  15.     

 
    更新,删除,插入方法
    由于在JDBC中这三个方法都是用了一个execute完成,所以这里我们也使用一个方法来完成这些功能。代码为:    

Java代码  收藏代码
  1. public int execute(String sql, List params) throws DaoException {  
  2.         int ret = 0;  
  3.         PreparedStatement pstmt = null;  
  4.         try {  
  5.             pstmt = conn.prepareStatement(sql);  
  6.             this.apply(pstmt, params);  
  7.             ret = pstmt.executeUpdate();  
  8.         }catch(SQLException ex) {  
  9.             throw new DaoException("", ex);  
  10.         } finally {  
  11.             if (pstmt != null) {  
  12.                 try {  
  13.                     pstmt.close();  
  14.                 } catch (SQLException e) {  
  15.                     // nothing.  
  16.                 }  
  17.             }  
  18.         }  
  19.           
  20.         return ret;  
  21.     }  
  22.       

 
    批处理方法(查询)
    有些时候为了便于操作,需要一次查询多条SQL语句,我们称之为批处理,实现参看以下方法,其中为了和query方法做区分,将参数和返回值都改为了数组形式。

Java代码  收藏代码
  1. public List[] queryBatch(String[] sqlArray, List[] paramArray) throws DaoException {  
  2.        List rets = new ArrayList();  
  3.        if(sqlArray.length != paramArray.length) {  
  4.            throw new DaoException("sql size not equal parameter size");  
  5.        } else {  
  6.            for(int i = 0; i < sqlArray.length; i++) {  
  7.                String sql = sqlArray[i];  
  8.                List param = paramArray[i];  
  9.                List ret = this.query(sql, param);  
  10.                rets.add(ret);  
  11.            }  
  12.            return (List[])rets.toArray();  
  13.        }  
  14.    }  

 

    批处理方法(更新)
    有些时候需要一次更新多条Sql语句,为了便于操作,添加了批处理更新操作,参看以下代码,为了和更新方法区分,将参数和返回值都改为了数组形式。    

Java代码  收藏代码
  1. public int[] executeBatch(String[] sqlArray, List[] paramArray) throws DaoException {  
  2.         List rets = new ArrayList();  
  3.         if(sqlArray.length != paramArray.length) {  
  4.             throw new DaoException("sql size not equal parameter size");  
  5.         } else {  
  6.             for(int i = 0; i < sqlArray.length; i++) {  
  7.                 int ret = this.execute(sqlArray[i], paramArray[i]);  
  8.                 rets.add(new Integer(ret));  
  9.             }  
  10.               
  11.             int[] retArray = new int[rets.size()];  
  12.             for(int i = 0; i < retArray.length; i++) {  
  13.                 retArray[i] = ((Integer)rets.get(i)).intValue();  
  14.             }  
  15.               
  16.             return retArray;  
  17.         }  
  18.     }  

 

    资源释放
    由于CommonDao有一个Connection的属性,且Connection属于稀缺资源,所以在CommonDao不需要在使用的时候需要显示的关闭Connection。代码如下:
   

Java代码  收藏代码
  1. public void close() throws DaoException{  
  2.        try {  
  3.            if (conn != null && conn.getAutoCommit()) {  
  4.                conn.close();  
  5.            } else {  
  6.                if(conn == null) {  
  7.                    throw new DaoException("can not close null connection, first new then close");  
  8.                } else {  
  9.                    throw new DaoException("transaction is running, rollbakc or commit befor close please.");  
  10.                }  
  11.            }  
  12.        } catch (SQLException ex) {  
  13.            throw new DaoException("Can not close common dao");  
  14.        }  
  15.    }  

        
JDBC工具类(JDBCUtil Class)
    在上述的代码中我们看到有很多的无用的处理,例如:

Java代码  收藏代码
  1. if (pstmt != null) {  
  2.     try {  
  3.         pstmt.close();  
  4.     } catch (SQLException e) {  
  5.         // nothing.  
  6.     }  
  7. }  

 
    为什么要有这些处理呢?说先这些处理发生的位置都是在正常处理完成之后,这些处理(例如pstmt.close())即使失败也没有影响,这个时候我们需 要做上述的无用处理,这正是JDBC API的一个小小的瑕疵。我们通常使用一个特殊的静态工具来来做补充,例如:

Java代码  收藏代码
  1. package com.jpleasure.jdbc.dao;  
  2.   
  3. import java.sql.Connection;  
  4. import java.sql.PreparedStatement;  
  5. import java.sql.ResultSet;  
  6. import java.sql.SQLException;  
  7.   
  8. public class JDBCUtil {  
  9.     public void safelyClose(Connection conn) {  
  10.         if(conn != null) {  
  11.             try {  
  12.                 conn.close();  
  13.             } catch (SQLException e) {  
  14.                 //   
  15.             }  
  16.         }  
  17.     }  
  18.     public void safelyClose(PreparedStatement pstmt) {  
  19.         if(pstmt != null) {  
  20.             try {  
  21.                 pstmt.close();  
  22.             } catch (SQLException e) {  
  23.                 //   
  24.             }  
  25.         }  
  26.     }  
  27.     public void safelyClose(ResultSet rs) {  
  28.         if(rs != null) {  
  29.             try {  
  30.                 rs.close();  
  31.             } catch (SQLException e) {  
  32.                 //   
  33.             }  
  34.         }  
  35.     }  
  36. }  
  37.    

 
异常处理
    也许细心的你已经发现了一个问题,为什么所有抛出异常的地方我们都是将SQLException包装在了DaoException之内抛出呢,为什么不直 接抛出SQLException呢?有两个原因,第一,可以细化,分类Exception抛出合适的异常,添加合适的消息,第二,隔离和Dao和业务逻辑 的耦合,可以方便的修改Dao层而不会影响到业务逻辑层。另外需要注意,DaoExcetion中可以包含SQLException,这个时候可以为客户 提供更详细的错误信息,例如ORA-12524等内容,但是很少见到。

Java代码  收藏代码
  1. package com.jpleasure.jdbc.dao;  
  2.   
  3. public class DaoException extends Exception {  
  4.   
  5.     public DaoException() {  
  6.         super();  
  7.     }  
  8.   
  9.     public DaoException(String message, Throwable cause) {  
  10.         super(message, cause);  
  11.     }  
  12.   
  13.     public DaoException(String message) {  
  14.         super(message);  
  15.     }  
  16.   
  17.     public DaoException(Throwable cause) {  
  18.         super(cause);  
  19.     }  
  20.       
  21. }  
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 官换机过保坏了怎么办 官换机在保坏了怎么办 留学生美国东西寄回国怎么办便宜 淘宝东西没到收货了怎么办 在淘宝上买东西被骗怎么办 网上把钱骗走了怎么办 在淘宝上卖号被骗了怎么办 如果淘宝商家保证金不够退款怎么办 天猫账号忘记了怎么办 姨妈来了10天了怎么办 微店商品无资质被下架怎么办 微店商品无资质怎么办 淘宝买东西提前确认收货了怎么办 苹果手机王者荣耀充值充多了怎么办 买家淘宝账号出现虚拟交易怎么办 贵州通登录不上怎么办 华为手机媒体声音小怎么办 苹果七内存满了怎么办 华为p9玩王者卡怎么办 鞋放健身房丢了怎么办 京东商家搞虚假活动怎么办 淘宝买的商品出现质量问题怎么办 买了重复的东西怎么办 借记卡在atm用不了怎么办 淘宝评论视频审核不通过怎么办 京东票据丢了怎么办 开拼多多找不到低价货源怎么办 要是校花追你该怎么办 苏宁自提逾期怎么办 天猫超市有问题怎么办 买的东西质量有问题怎么办 淘宝优惠额度大做不了怎么办 淘宝天猫拒绝退货退款怎么办 u盘变成活动分区怎么办 学生上课纪律差家长老师怎么办 老师管纪律学生打老师怎么办? 淘宝店铺层级排名下降怎么办 网购不支持该地区销售怎么办 分销公司佣金不给业务员怎么办 淘宝买家账号违规了怎么办 王者荣耀打野被队友抢野怎么办