java记录-JDBC连接池的设计

设计一个数据库连接池的几个步骤和要点

一:步骤

  1. 首先要创建一个工具类,然后在类中创建静态代码块中加载驱动。
  2. // 静态代码块中直接加载数据库驱动
    static {
        try {
            String driver = ConfigurationManager.getProperty(Constants.JDBC_DRIVER);
            Class.forName(driver);
        }catch (Exception e){
            e.getStackTrace();
        }
    }
2.实现工具类的单例化,保证这个类在系统中只能有一个实例,数据库连接池也只有一份
/**
 * 实现JDBCHelper的单例化
 */
private static JDBCHelper instance = null;

//获取单例
public static JDBCHelper getInstance(){
    if(instance == null){
        synchronized (JDBCHelper.class){
            if(instance == null){
                instance = new JDBCHelper();
            }
        }
    }
    return instance;
}

  3.创建数据库连接池

//创建数据库连接池
private LinkedList datasource = new LinkedList();

  4.在构造函数当中创建连接池

* JDBCHelper在整个程序运行申明周期中,只会创建一个实例
 * 在这一次创建实例的时候,会调用自己的构造方法
 * 在构造方法中去创建自己唯一的一个数据库连接池
 */
private JDBCHelper(){
    /**
     * 第一步:获取数据库连接池的大小,就是数据库连接池中要放多少个数据库连接
     * 这个可以通过在配置文件中配置的方式,来灵活设定
     */
    int dataSourceSize = ConfigurationManager.getInteger(
            Constants.JDBC_DATASOURCE_SIZE);

    //然后创建制动数量的数据库连接,并放入数据库连接池中
    for(int i = 0; i < dataSourceSize; i++){
        String url = null;
        String userName = null;
        String passWord = null;
        url = ConfigurationManager.getProperty(Constants.JDBC_URL);
        userName = ConfigurationManager.getProperty(Constants.JDBC_USERNAME);
        passWord = ConfigurationManager.getProperty(Constants.JDBC_PASSWORD);
        try {
            Connection connection = DriverManager.getConnection(url,userName,passWord);
            datasource.push(connection);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

 5.创建一个获取连接的方法

/**
 *  提供获取数据库连接的方法
 *  有可能现在连接池中所的连接都用完了,暂时获取不到连接
 *  所以在我们的代码中要有一个简单的等待机制,去等待获取到数据库连接
 */
public synchronized Connection getConnection(){
    while (datasource.size() == 0){
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    return datasource.poll();
}

 6.创建执行增删改的sql语句的方法

public int executeUpdate(String sql, Object[] param){
    int rtn = 0;
    Connection conn = null;
    PreparedStatement pStat = null;

    try {
        conn = getConnection();
        conn.setAutoCommit(false);

        pStat = conn.prepareStatement(sql);
        if (param != null && param.length > 0) {
            for (int i = 0; i < param.length; i++) {
                pStat.setObject(i + 1, param[i]);
            }
        }
        rtn = pStat.executeUpdate();
        conn.commit();
    } catch(SQLException e){
            e.printStackTrace();
    }finally {
        datasource.push(conn);
    }
    return rtn;
}

 7. 创建执行查询sql语句的方法

public void executeQuery(String sql, Object[] param, QueryCallback callback){
    Connection conn = null;
    PreparedStatement pStat = null;
    ResultSet rs = null;

    try{
        conn = getConnection();
        pStat = conn.prepareStatement(sql);
        for(int i=0; ilength;i++){
            pStat.setObject(i+1,param[i]);
        }
        rs = pStat.executeQuery();
        callback.process(rs);
    }catch (Exception e){
        e.getStackTrace();
    }finally {
        if(conn != null) {
            datasource.push(conn);
        }
    }
}

 8. 创建执行批量执行sql语句的方法

public int[] executeBatch(String sql, List paramList){
    //第一步获取连接
    int[] rtn = null;
    Connection conn = null;
    PreparedStatement pStat = null;
    try{
        conn = getConnection();
        //第二步取消自动提交
        conn.setAutoCommit(false);
        pStat = conn.prepareStatement(sql);
        if(paramList != null && paramList.size() > 0){
            for(Object[] param : paramList){
                for(int i=0; ilength; i++){
                    pStat.setObject(i+1,param[i]);
                }
                //使用addBatch()方法加入批量的sql参数
                pStat.addBatch();
            }
        }
        //使用executeBatch()方法,批量的执行sql语句
        rtn = pStat.executeBatch();
        //提交批量的sql语句执行
        conn.commit();
    }catch (Exception e){
        e.getStackTrace();
    }finally {
        if(conn != null){
            datasource.push(conn);
        }
    }
    return rtn;
}

 9. 创建一个回调接口,处理执行完返回的数据

/**
 * 静态内部类
 * 查询回调接口
 */
public interface QueryCallback {
    /**
     * 处理返回结果
     * @param rs
     * @throws Exception
     */
    void process(ResultSet rs) throws Exception;
}

二、要点

关于PreparedStatement接口,需要重点记住的是: 
1. PreparedStatement可以写参数化查询,比Statement能获得更好的性能。 
2. 对于PreparedStatement来说,数据库可以使用已经编译过及定义好的执行计划,这种预处理语句查询比普通的查询运行速度更快。 
3. PreparedStatement可以阻止常见的SQL注入式攻击。 
4. PreparedStatement可以写动态查询语句 
5. PreparedStatement与java.sql.Connection对象是关联的,一旦你关闭了connection,PreparedStatement也没法使用了。 
6. “?” 叫做占位符。 
7. PreparedStatement查询默认返回FORWARD_ONLY的ResultSet,你只能往一个方向移动结果集的游标。当然你还可以设定为其他类型的值如:”CONCUR_READ_ONLY”。 
8. 不支持预编译SQL查询的JDBC驱动,在调用connection.prepareStatement(sql)的时候,它不会把SQL查询语句发送给数据库做预处理,而是等到执行查询动作的时候(调用executeQuery()方法时)才把查询语句发送个数据库,这种情况和使用Statement是一样的。 

9. 占位符的索引位置从1开始而不是0,如果填入0会导致java.sql.SQLException invalid column index异常。所以如果PreparedStatement有两个占位符,那么第一个参数的索引时1,第二个参数的索引是2.

查询操作如何调用回调接口:

    //测试查询语句
    final Map testUser = new HashMap();
    String sql = "select name,age from user_test where age = ?";
    jdbc.executeQuery(sql,new Object[]{21},new JDBCHelper.QueryCallback(){
        @Override
        public void process(ResultSet rs) throws Exception {
            if (rs.next()){
                String name = rs.getString(1);
                int age = rs.getInt(2);
                /**
                 * 匿名内部类的重要知识点
                 * 就是在内部类中使用外部的成员如变量等,
                 * 那么必须将此变量设置为final类型才能访问
                 * 不然是访问不了的
                 */
                testUser.put("name",name);
                testUser.put("age",age);
            }
        }
    });
    System.out.println(testUser.get("name") + ":" + testUser.get("age"));
}

你可能感兴趣的:(java)