JDBC基础入门(1)

时间:2022-05-04
本文章向大家介绍JDBC基础入门(1),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

JDBC(Java Database Connectivity)代表Java编程语言与数据库连接的标准API,然而JDBC只是接口,JDBC驱动才是真正的接口实现,没有驱动无法完成数据库连接. 每个数据库厂商都有自己的驱动,用来连接自己公司的数据库(如Oricle, MySQL, DB2, MS SQLServer).

下面我们以MySQL为例,JDBC编程大致步骤如下:

/**
 * @author jifang
 * @since 16/2/18 上午9:02.
 */
public class SQLClient {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        /* 加载数据库驱动 */
        Class.forName("com.mysql.jdbc.Driver");
        /* 通过 DriverManager 获取数据库连接 */
        Connection connection = DriverManager.getConnection("jdbc:mysql://host:port/database", "user", "password");
        /* 通过 Connection 创建 Statement */
        Statement statement = connection.createStatement();
        /* 通过 Statement 执行SQL */
        ResultSet users = statement.executeQuery("SELECT * FROM user");
        /* 操作 ResultSet 结果集 */
        int columnCount = users.getMetaData().getColumnCount();
        while (users.next()) {
            for (int i = 1; i <= columnCount; ++i) {
                System.out.printf("%st", users.getObject(i));
            }
            System.out.println();
        }
        /* 回收数据库资源(推荐使用Java1.7提供的 可以自动关闭资源的try) */
        users.close();
        statement.close();
        connection.close();
    }
}

注意: 需要在pom.xml中添加如下MySQL驱动:

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.36</version>
</dependency>

注: ResultSet参数columnIndex索引从1开始,而不是0!

ConnectionManger

DriverManger

JDBC规定: 驱动类在被加载时,需要主动把自己注册到DriverManger中:

com.mysql.jdbc.Driver

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    //
    // Register ourselves with the DriverManager
    //
    static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }
    /**
     * Construct a new driver and register it with DriverManager
     * 
     * @throws SQLException
     *             if a database error occurs.
     */
    public Driver() throws SQLException {
        // Required for Class.forName().newInstance()
    }
}

代码显示:只要去加载com.mysql.jdbc.Driver类那么就会执行static块, 从而把com.mysql.jdbc.Driver注册到DriverManager中.

java.sql.DriverManager是用于管理JDBC驱动的服务类,其主要功能是获取Connection对象:

1. static Connection getConnection(String url, Properties info)

2. static Connection getConnection(String url, String user, String password)

另: 还可以在获取Connection的URL中设置参数,如: jdbc:mysql://host:port/database?useUnicode=true&characterEncoding=UTF8

useUnicode=true&characterEncoding=UTF8指定连接数据库的过程中使用Unicode字符集/UTF-8编码;

Connection

java.sql.Connection代表数据库连接,每个Connection代表一个物理连接会话, 该接口提供如下创建Statement的方法, 只有获取Statement之后才可执行SQL语句:

Creates a Statement object for sending SQL statements to the database.

其中Connection还提供了如下控制事务/保存点的方法:

Creates a savepoint with the given name in the current transaction and returns the new Savepoint object that represents it.

以上方法还存在不同的重载形式, 详细可参考JDK文档.

ConnectionManger

由于获取Connection的步骤单一,每次可能只是加载的参数不同,因此我们可以将获取Connection的操作封装成一个方法,并使其从配置文件中加载配置:

配置文件形式

## Data Source
mysql.driver.class=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql://host:port/database
mysql.user=admin
mysql.password=admin
ConnectionManger
/**
 * @author jifang
 * @since 16/2/19 上午10:40.
 */
public class ConnectionManger {
    /*获取原生Connection*/
    public static Connection getConnection(String file) {
        Properties config = SQLUtil.loadConfig(file);
        try {
            Class.forName(config.getProperty("mysql.driver.class"));
            String url = config.getProperty("mysql.url");
            String username = config.getProperty("mysql.user");
            String password = config.getProperty("mysql.password");
            return DriverManager.getConnection(url, username, password);
        } catch (SQLException | ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }
}
SQLUtil
/**
 * @author jifang
 * @since 16/2/18 上午8:24.
 */
public class SQLUtil {
    /**
     * 加载.properties配置文件
     *
     * @param file
     * @return
     */
    public static Properties loadConfig(String file) {
        Properties properties = new Properties();
        try {
            properties.load(ClassLoader.getSystemResourceAsStream(file));
            return properties;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

数据库连接池

前面通过DriverManger获得Connection, 一个Connection对应一个实际的物理连接,每次操作都需要打开物理连接, 使用完后立即关闭;这样频繁的打开/关闭连接会造成不必要的数据库系统性能消耗.

数据库连接池提供的解决方案是:当应用启动时,主动建立足够的数据库连接,并将这些连接组织成连接池,每次请求连接时,无须重新打开连接,而是从池中取出已有连接,使用完后并不实际关闭连接,而是归还给池.

JDBC数据库连接池使用javax.sql.DataSource表示, DataSource只是一个接口, 其实现通常由服务器提供商(如WebLogic, WebShere)或开源组织(如DBCP,C3P0和HikariCP)提供.

数据库连接池的常用参数如下:

数据库初始连接数;

连接池最大连接数;

连接池最小连接数;

连接池每次增加的容量;

C3P0

Tomcat默认使用的是DBCP连接池,但相比之下,C3P0则比DBCP更胜一筹(hibernate推荐使用C3P0),C3P0不仅可以自动清理不再使用的Connection, 还可以自动清理Statement/ResultSet, 使用C3P0需要在pom.xml中添加如下依赖:

<dependency>
    <groupId>com.mchange</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.5.2</version>
</dependency>
<dependency>
    <groupId>com.mchange</groupId>
    <artifactId>mchange-commons-java</artifactId>
    <version>0.2.11</version>
</dependency>
ConnectionManger
public class ConnectionManger {
    /*双重检测锁保证DataSource单例*/
    private static DataSource dataSource;
    /*获取DataSource*/
    public static DataSource getDataSourceC3P0(String file) {
        if (dataSource == null) {
            synchronized (ConnectionManger.class) {
                if (dataSource == null) {
                    Properties config = SQLUtil.loadConfig(file);
                    try {
                        ComboPooledDataSource source = new ComboPooledDataSource();
                        source.setDriverClass(config.getProperty("mysql.driver.class"));
                        source.setJdbcUrl(config.getProperty("mysql.url"));
                        source.setUser(config.getProperty("mysql.user"));
                        source.setPassword(config.getProperty("mysql.password"));
                        // 设置连接池最大连接数
                        source.setMaxPoolSize(Integer.valueOf(config.getProperty("pool.max.size")));
                        // 设置连接池最小连接数
                        source.setMinPoolSize(Integer.valueOf(config.getProperty("pool.min.size")));
                        // 设置连接池初始连接数
                        source.setInitialPoolSize(Integer.valueOf(config.getProperty("pool.init.size")));
                        // 设置连接每次增量
                        source.setAcquireIncrement(Integer.valueOf(config.getProperty("pool.acquire.increment")));
                        // 设置连接池的缓存Statement的最大数
                        source.setMaxStatements(Integer.valueOf(config.getProperty("pool.max.statements")));
                        // 设置最大空闲时间
                        source.setMaxIdleTime(Integer.valueOf(config.getProperty("pool.max.idle_time")));
                        dataSource = source;
                    } catch (PropertyVetoException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
        return dataSource;
    }
    /*获取Connection*/
    public static Connection getConnectionC3P0(String file) {
        return getConnection(getDataSourceC3P0(file));
    }
    public static Connection getConnection(DataSource dataSource) {
        try {
            return dataSource.getConnection();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
    // ...
}

C3P0还可以使用配置文件来初始化连接池(配置文件可以是properties/XML, 在此仅介绍XML),C3P0配置文件名必须为c3p0-config.xml,其放在类路径下:

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <default-config>
        <property name="jdbcUrl">jdbc:mysql://host:port/database</property>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="user">user</property>
        <property name="password">password</property>
        <property name="acquireIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">3</property>
        <property name="maxPoolSize">20</property>
    </default-config>
    <named-config name="mysql-config">
        <property name="jdbcUrl">jdbc:mysql://host:port/common</property>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="user">user</property>
        <property name="password">password</property>
        <property name="acquireIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">3</property>
        <property name="maxPoolSize">20</property>
    </named-config>
</c3p0-config>

这样, 我们在创建ComboPooledDataSource时就默认加载配置文件中的配置, 无须手动配置:

public static DataSource getDataSourceC3P0(String file) {
    if (dataSource == null) {
        synchronized (ConnectionManger.class) {
            if (dataSource == null) {
                dataSource = new ComboPooledDataSource();
            }
        }
    }
    return dataSource;
}

C3P0配置文件可以配置多个连接信息, 并为每个配置命名, 这样可以方便的通过配置名称来切换配置信息:

public static DataSource getDataSourceC3P0(String file) {
    if (dataSource == null) {
        synchronized (ConnectionManger.class) {
            if (dataSource == null) {
                dataSource = new ComboPooledDataSource("mysql-config");
            }
        }
    }
    return dataSource;
}