访问数据库的主类:package gpsstdserver;
import java.sql.*;
import java.util.Date;
import java.io.PrintWriter;
import java.util.Properties;
import java.util.Enumeration;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
public class AccessDB {
  static AccessDB accessdb=null;
  public static final int MAX_CON_COUNT=200;
  public static final int MIN_CON_COUNT=1;  /**
   * 定义一个连接池,用来存放连接
   */
  private ArrayList conPool=null;
  public  AccessDB() {
    if(conPool==null)
      conPool=new ArrayList(0);
    MyConnection mycon=new MyConnection();
    conPool.add(mycon);  }
  /**
   * 得到一个访问数据库的实例.
   * 此访问数据库的类为单例模式.
   * 如果成功建立了和数据库的连接,则返回它的实例;
   * 否则,返回空
   * @return
   */
  public  synchronized static AccessDB getInstance(){
    if(accessdb==null)
        accessdb = new AccessDB();    return accessdb;  }
  /**
   * 从连接池中得到一个可用的STATEMENT.
   * @return 如果正在访问数据库的客户端的数目大于MAX_CON_COUNT*MAX_STMT_COUNT,返回NULL
   */
  private synchronized Statement getStmt(){
    MyConnection conn=null;
    Statement stmt=null;    /**
     * 如果连接池为空,那么为连接池开辟一个空间,同时创建一个新的连接加入到连接池中.
     */
    if(conPool==null){
      conPool = new ArrayList(0);
      conn=new MyConnection();
      conPool.add(conn);
    }    /**
     * 从连接池中取出一个连接的STATEMENT.如果连接池中的每个连接上的STATEMENT都已经达到
     * 了规定的最大值,那么重新为客户端创建一个新的数据库连接.
     */
    for(int i=0;i<conPool.size();i++){
      conn = (MyConnection) conPool.get(i);
      stmt = conn.getStmt();
      if (stmt != null)
        break;
    }
    if(stmt==null && conPool.size()<MAX_CON_COUNT)
    {
      conn=new MyConnection();
      stmt=conn.getStmt();
      conPool.add(conn);
    }
    return stmt;  }  /**
   * 关闭一个连接上的STATEMENT.如果关闭后,此连接上没有一个STATEMENT的话,并且此时连接池
   * 中的连接数大于MIN_CON_COUNT的话,同时关闭此连接.
   * @param st
   */
  private synchronized void stmtClose(Statement st){
    MyConnection conn=null;
    if(conPool!=null && conPool.size()>0){
      for(int i=0;i<conPool.size();i++){
         conn=(MyConnection )conPool.get(i);
         conn.closeStmt(st);
         if(conn.stmtCount()<=0 && conPool.size()>MIN_CON_COUNT){
           conn.closeConn();
           conPool.remove(i);
           System.out.println("目前的连接数为:\n"+(conPool.size()-1));
           break;
         }      }    }
  }
  /**
   * 查询远程数据库,并且返回一个记录集
   * @param sql 要查询的SQL语句
   * @return 记录集
   */
  public MyResultSet queryDB(String sql){    Statement stmt=null;      /**
       * 查询数据库,并把返回的内容写到标准的容器中,而不是直接返回RESULTSET。
       * 每次查询结束后,并关闭和数据的STATEMENT,在访问数据库之后马上就关闲可以
       * 预防客户端忘记关闭而导致的游标越界。
       */
      if(sql!=null){
      try{
        stmt = getStmt();
        ResultSet rs=null;
        if(stmt!=null){
          rs = stmt.executeQuery(sql);
          MyResultSet myRs = new MyResultSet(rs);
          rs.close();
          stmtClose(stmt);
          return myRs;
        }
      }catch(SQLException e){        String err="无法进行数据库查询!"+
                   "\n"+sql+
                   "\n"+e;
        MyPocket.myPrint(err,true);
        MyPocket.log("\\logs\\error.txt",err);
      }
    }
   return null;
  }  /**
   * 执行诸如:INSERT,UPDATE,DELETE操作
   * @param sql SQL语句
   * @return 如果成功执行SQL语句则返回TRUE
   */
  public int execute(String sql){
    int updateCount=0;
    if(sql!=null){
      try{
        Statement stmt = getStmt();
        if(stmt!=null){
          stmt.execute(sql);
          updateCount = stmt.getUpdateCount();
          stmtClose(stmt);
          return updateCount;
        }
      }catch(SQLException e){
        //printStackTrace();
        String err="更新数据库时出错\n"+
                    "*"+sql+
                    "\n*"+e;
        MyPocket.myPrint(err,true);
        MyPocket.log("\\logs\\error.txt",err);
      }
    }
    return 0;
  }
}

解决方案 »

  1.   


    /**
     * 建立一个和数据库的物理连接,在此连接上可以建立最多250个STATEMENT。
     */
    class MyConnection{
      String dbHost=null;//数据库主机地址
      int dbPort=0;//数据库端口号
      String serviceName=null;//全局数据库名
      String url=null;//连接地址
      String driver=null;//数据库驱动
      String userName=null;//登陆用户名
      String pwd=null;//登陆密码  private Connection conn = null; //数据库连接
      private ArrayList stmtBuffer = null;//存放在此连接上建立的STATEMENT
      private int stmtCount = 0; //每个连接上建立的STATEMENT的数目
      private boolean isUsing = false; //连接是否已经在使用
      public static final int MAX_STMT_COUNT=250;//允许在一个物理连接上建立的STATEMENT的最大数目  public MyConnection() {
        if (stmtBuffer == null)
          stmtBuffer = new ArrayList(0);
        init();
        conn=connectToDB();    System.out.println("Now connection count is:"+(++MyPocket.conCt));
      }
      /**
       * 初始化连接远程数据库的各种配置
       */
      private  void  init(){
        /*
        dbHost="127.0.0.1";
        dbPort=1521;
        serviceName="myOra";
        url="jdbc:oracle:thin:@"+dbHost+":"+dbPort+":"+serviceName;
        driver="oracle.jdbc.driver.OracleDriver";
        userName="sinboy";
        pwd="sinboy";
      */
        Properties props = new Properties();
       try {
         props.load(new FileInputStream("conf\\database.properties"));
         Enumeration propNames = props.propertyNames();
         while (propNames.hasMoreElements()) {
           String name = (String) propNames.nextElement();       //得到数据库的登陆用户名和密码
           if (name.equals("username")) {
              userName=props.getProperty(name);
              continue;
           }
           if(name.equals("password")){
             pwd=props.getProperty(name);
             continue;
           }
           //得到数据库驱动
           if(name.equals("driver")){
             driver=props.getProperty(name);
              continue;
           }
           //得到数据库主机的URL
           if(name.equals("url")){
             url=props.getProperty(name);
             continue;
           }
         } //while
       }
       catch(IOException e){
         String err="从数据库配置文件中读取相关信息时出错:"+
                    "\n"+e+
                    "\n请检查conf\\database.properties文件是存在";
         MyPocket.myPrint(err,true);
         MyPocket.log("\\logs\\error.txt",err);   }  }
      /**
        * 连接远程数据库
        * @return 连接成功返回TRUE
        */
       private Connection connectToDB(){
         Connection conn=null;
         try{
               Class.forName(driver);
               conn = DriverManager.getConnection(url, userName, pwd);
               if(conn!=null)
               {
                 String str="建立和远程数据库的连接!"+
                                   "\n"+driver+
                                   "\n"+url+
                                   "\n"+userName+","+pwd;
                   MyPocket.myPrint(str,false);
                   MyPocket.log("\\logs\\log.txt",str);           }
             }catch(SQLException e){            String err=  "无法建立和远程数据库的连接!"+
                                   "\n"+driver+
                                   "\n"+url+
                                   "\n"+userName+","+pwd+
                                   "\n"+e;
                MyPocket.myPrint(err,true);
                MyPocket.log("\\logs\\error.txt",err);
             }
             catch(ClassNotFoundException e){
              String err="没有发现驱动:\n"+
                          driver+"\n"+e;
               MyPocket.myPrint(err,true);
               MyPocket.log("\\logs\\error.txt",err);
          }
            return conn;
       }  /**
       * 为客户端访问数据库创建一个新的物理连接.
       * @return
       */
      public Connection getConn() {
        if(conn==null)
          {
            init();
            conn=connectToDB();      }
        return conn;
      }  /**
       * 返回一个物理连接上的STATEMENT
       * 为一个新的客户端创建一个新的STATEMENT,如果此时在一个CONNECTION上建立的连接的
       * STATEMENT已经超出规定的最大数目,则返回NULL.
       * @return
       */
      public Statement getStmt() {
        if(conn!=null && stmtCount<MAX_STMT_COUNT)
        {
          try{
            Statement stmt = null;
            stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
                                        ResultSet.CONCUR_UPDATABLE);
            changeStmtCount(1);
            if(stmtBuffer==null)
              stmtBuffer=new ArrayList(0);
            stmtBuffer.add(stmt);
            return stmt;
          }catch(SQLException e){
              String err="在一个物理连接上为客户端创建一个STATEMENT时出错:\n"+e;
              MyPocket.myPrint(err,true);
              MyPocket.log("\\logs\\error.txt",err);
          }
        }
        return null;
      }  public int stmtCount() {
        return stmtCount;
      }  public  synchronized void changeStmtCount(int step) {
        stmtCount += step;
      }  public void setIsUsing(boolean isUsing) {
        this.isUsing = isUsing;
      }  public boolean isUsing() {
        return isUsing;
      }  /**
       * 把指定的已经使用完毕的STATEMENT关闭,并从STMTBUFFER中清除掉.同时把STATEMENT的数目
       * 相应的减一.
       * @param st 已经使用完毕要关闭的STATEMENT
       */
      public synchronized void closeStmt(Statement st){
        Statement stmt=null;
         if(st!=null && stmtBuffer!=null){
           for(int i=0;i<stmtBuffer.size();i++){
               stmt=(Statement)stmtBuffer.get(i);
              if(stmt.equals(st)){
                try{              System.err.println("关闭连接:"+conn.toString()+
                                     "上的STATEMENT"+stmt.toString() );
                  System.out.println("总共关闭了STMT:"+(++MyPocket.myct));
                  stmt.close();
                }catch(SQLException e){
                   String err="在关闭连接池中的一个连接上的STATEMENT时出错:\n"+e;
                   MyPocket.myPrint(err,true);
                   MyPocket.log("\\logs\\error.txt",err);
                }
                System.err.println("stmtBuffer.size:"+stmtBuffer.size()+"   i:"+i);
                stmtBuffer.remove(i);
                changeStmtCount(-1);
                break;
              }
           }
         }
      }  /**
       * 关闭和数据库的连接.
       * 首先要关闭在此连接上的所有的STATEMENT,然后再关闭物理连接。
       */
      public synchronized void closeConn() {
        Statement stmt=null;    try{
          if (conn != null) {
            if (stmtCount > 0) {
              while (stmtBuffer.size() > 0) {
                stmt = (Statement) stmtBuffer.get(0);
                stmt.close();
                stmtBuffer.remove(0);
              }
              conn.close();        }
          }
        }catch(SQLException e){
            String err="在关闭一个和数据库的物理连接时出现异常:\n"+e;
            MyPocket.myPrint(err,true);
            MyPocket.log("\\logs\\error.txt",err);
        }
      }
    }
      

  2.   

    package gpsstdserver;
    import java.util.ArrayList;
    import java.sql.ResultSet;
    import java.sql.ResultSetMetaData;
    import java.sql.SQLException;
    /**
     * <p>Title: GPS小用户服务器</p>
     * <p>Description:把查询数据库返回的标准结果记录集转换成自定义的容器中。
     * 访问的方式和RESULTSET的尽量完全一样。 </p>
     * <p>Copyright: Copyright (c) 2003.12.26</p>
     * <p>Company: YUTONG</p>
     * @author SINBOY
     * @version 1.0
     */public class MyResultSet {
      private ArrayList myRs=null;//存放访问数据库返回的结果记录集
      private ArrayList myRow=null;//存放访问数据库返回的一行结果记录集
      private ArrayList oneResult=null;//存放访问数据库返回的一个结果记录  private int cols=0;//记录集的列数
      private int rows=0;//记录集的行数
      private int currentRow=-1;//当前行  public MyResultSet(ResultSet rs){
        if(rs!=null){
          try{
            /**
             * 得到行数和列数
             */
            ResultSetMetaData rsMeta=rs.getMetaData();
            cols=rsMeta.getColumnCount();
            rs.last();
            rows=rs.getRow();        if (myRs == null)
             myRs = new ArrayList(rows);
             /**
              * 把结果导出到自定义的容器中.
              * colName:列的名称
              * value:对应的值
              */
           String colName=null;
           String value=null;
           rs.beforeFirst();
           while(rs.next()) {          myRow = new ArrayList(cols);
              for (int i = 1; i <=cols; i++){
                oneResult=new ArrayList(1);
                colName=rsMeta.getColumnName(i);
                value=rs.getString(i);
                oneResult.add(0,colName);
                oneResult.add(1,value);            myRow.add(oneResult);
              }
              myRs.add(myRow);        }
          }catch(SQLException e){
            String err="在从标准RESULTSET结果记录集中导出数据到自定义"+
                        "的ARRAYLIST容器中时出错:\n"+e;
            MyPocket.myPrint(err,true);
            MyPocket.log("\\logs\\error.txt",err);
          }
        }
      }  /**
       * 根据列名得到它对应的值
       * @param colName  列名
       * @return  列值
       */
      public String getString(String colName){
         String value=null;
         ArrayList oneResult=null;     if(colName!=null &&   currentRow>=0){
           if(myRs!=null && myRs.size()>currentRow){
             myRow = (ArrayList) myRs.get(currentRow);
             for (int i = 0; i < cols; i++) {
               oneResult = (ArrayList) myRow.get(i);
               if ( ( (String) oneResult.get(0)).toUpperCase().equals(colName.toUpperCase() )) {
                 value = (String) oneResult.get(1);
                 break;
               }
             }
           }
         }
         return value;
      }  /**
       * 根据列序号得到它对应的值
       * @param col 列的序号
       * @return  列值
       */
      public String getString(int col){
         String value=null;
         ArrayList oneResult=null;     if(col>=0){
         myRow=(ArrayList)myRs.get(currentRow);
         oneResult=(ArrayList)myRow.get(col);
         if(oneResult!=null)
             value=(String)oneResult.get(1);     }
         return value;  }  public int cols(){
        return cols;
      }  public int rows(){
        return rows;
      }
      public boolean next(){
        currentRow++;
        if(currentRow<rows)
          return true;
        else
          return false;  }
    }
      

  3.   

    测试代码:
    package gpsstdserver;
    import java.io.*;
    import java.net.*;
    import java.util.Date;
    public class ConPoolTest extends Thread{
    String name=null;
    Socket sck=null;
    public ConPoolTest(String name){
      super(name);
      this.name=name;  start();
    }
    public void run(){
      try{      AccessDB accdb=null;
          MyResultSet rs=null;
          for(int i=0;i<50;i++){
              accdb=AccessDB.getInstance();
              rs=accdb.queryDB("select * from usermsg");
              while(rs.next())
                MyPocket.myPrint(name+"_"+i+"\n"+rs.getString("userName"),false);          sleep(1);
            }
            /*
             BufferedReader in=new BufferedReader(new InputStreamReader(
                               sck.getInputStream()));
             while((msg=in.readLine())!=null)
               System.out.println("msg:"+msg);
              sleep(100000);
            */
             }          catch(InterruptedException e){}
    }
    public static void main(String argv[])  {     for(int i=0;i<200;i++)
         {
           ConPoolTest test=new ConPoolTest("test"+i);
         }}}数据库的配置方件database.properties
    #数据库驱动
    driver=oracle.jdbc.driver.OracleDriver
    #ORACLE数据库URL
    url=jdbc:oracle:thin:@192.168.0.205:1521:myOra
    #ORACLE数据库登陆用户名
    username=sinboy
    #登陆密码
    password=sinboy
    我的想法可能还不太成熟,希望大家多支持,此源程序大家要觉得还能用,可以无尝使用,不过谁要是有了更好的主意,请贴出来供大家分享,共同提高。
      

  4.   

    可以尝试自己写一些数据库访问的模板,但不要试图写一个orm --- 好像是Rod Johnson说的。
    数据库访问的模板你可以参考一下spring framework,如果你需要一个
    orm,看看hibernate吧。
      

  5.   

    jdbc编程
    可以使用工厂模式+单例模式
    或者门面模式+单例模式结合
    客户段无需关心实现细节!!!
      

  6.   

    多谢楼上几位的指点。
    我使用的应该是单例模式,public class AccessDB这里应该改成private class AccessDB{}miwoo(鱼鱼羊)说的东西我还不懂,只不过前几天刚刚发现的BIBERNATE的主页,收藏了下来。抽时间我会好好学习一下。fantasyCoder(牛仔+T恤)
    能不能提供一个比较完整的实例?有一期的程序员杂志上好象发表过这方面的东西,不过我觉得还不够详细。 tanghuan() ( )
    能不能把你修正之后的代码贴上来?
      

  7.   

    数据多了怎么办
    都要保存到list里吗?
    10个人同时查询每人5000条就是50000条
      

  8.   

    如何写sql就是最大的细节问题
      

  9.   

    同意xue_sharp(靠)
    你这个玩意,在大数据量的时候能撑得住吗?其实jdbc2.0已经支持连接池了还有必要写连接池吗?
    大量使用statement,那么preparestatement呢?
    如果和特定的数据库相关的操作呢?
    想法太简单了吧
      

  10.   

    楼上几位的直言批评让我脸红,我知道自己水平很差,当时写完之后犹豫了很长时间才决定放到网上的。但是,你们指点的好,有时光靠自己摸索,进步确实很难,身边又找不到可以帮我的。接触JDBC的编程才一两个月,需要学习的东西很多,请大家不吝赐教!
      

  11.   

    关于异常的处理,我的初衷是把一方面把错误信息打印出来,同时把错误信息记录到日志文件中,以备以后查阅。其中,MyPocket.myprint()是按一定的格式打印出来,包括错误信息以及产生异常的时间。MyPocket.log()是把错误信息记录到指定的文件中,记录的信息跟打印的差不多。随然这样打印出来的错误信息没有使用e.printStackTrace();产生的信息全面、可定位,但是我在使用过程中觉得还可以,如查程序产生异常还是能很快找到错误所在的。关于xiaohaiz(城里的老土,两眼依然通红!) 说的第一点和第三点,还是请你说的详细一点,最好能指出一个地方加以剖析,你只管下刀子,既然放到网上了,俺就不怕被肢解的体无完肤