日期:2014-05-16  浏览次数:20396 次

我和JAVA数据库操作的那些事儿(4)
通过前面几篇的介绍,对于JDBC的使用应该基本上够上项目开发的要求了。但是,总是觉得还有一些问题,比如,我写了一个DBUtil类,这个类里持有一个Connection对象,而这个对象是被所有需要使用的地方共用的。


private static Connection conn = null;
    public static Connection getConnection() {
        if (conn == null) {
            String url = "jdbc:sqlite:test.db";
            try {
                synchronized (this) {
                    Class.forName("org.sqlite.JDBC");
                    conn = DriverManager.getConnection(url);
                }
            } catch (ClassNotFoundException e) {
                conn = null;
            } catch (SQLException e) {
                conn = null;
            }
        }
        return conn;
    }

public static void close(Connection conn) {

        // 因为connection只有一个,所以不真正关闭
        /*
         * if (conn != null) { try { conn.close(); } catch (SQLException e) {
         * 
         * } conn = null; }
         */
    }


    可以看到,因为创建一个Connection是比较破费资源的操作,所以这个Connection是创建一次然后被公用的,而且也没有真正实现它的close方法(如果每次都用完就close,那每次都要重新创建,就失去意义了)


    这样做,我总是有一点担心,因为老是觉得Connection被多线程同时使用的话,会出现一些问题。目前为止我没有做过这方面的测试,但我看到有相关的论调:


JDBC and Multithreading


"The Oracle JDBC drivers provide full support for, and are highly optimized for, applications that use Java multithreading. Controlled serial access to a connection, such as that provided by connection caching, is both necessary and encouraged. However, Oracle strongly discourages sharing a database connection among multiple threads. Avoid allowing multiple threads to access a connection simultaneously. If multiple threads must share a connection, use a disciplined begin-using/end-using protocol."

    也有人通过ThreadLocal的方式,让每个线程都持有一个Connection的副本,这是另外一种技术了,以后在多线程同步的相关文章里再详细介绍。

    当我知道数据库连接池的时候,就把这部分工作让它去实现了。何乐而不为呢?

    我用过最多的连接池是DBCP,另外也用过c3p0。下面以DBCP为例。

private static DataSource dataSource;

    public static Connection getConnection() {
        if (dataSource == null) {
            buildDataSource();
        }        

        try {
            return dataSource.getConnection();

        } catch (SQLException e) {

            return null;
        }

    }



    private static synchronized void buildDataSource() {
        BasicDataSource ds = new BasicDataSource();
        ds.setDriverClassName("org.sqlite.JDBC");
        ds.setUrl("jdbc:sqlite:test.db");
        dataSource = ds;
    }


    这里使用了DBCP的BasicDataSource(这是一个DataSource接口的实现),在这里通过BasicDataSource.getConnection() 的方法来获取连接,至于Connection的关闭,则会被放到连接池中。关于DataSource还有更多的属性,如maxActive(最大活跃数),maxIdle(最大空闲数)等。当然,更常见的做法是把这些配置放到properties文件里,后面会在引用spring的时候进行讨论。


    关于BasicDataSource,从下面的类图中可以看到相应的属性。