DBCP和JDBC學習總結(應用篇)
DBCP是一個常用的資料庫連接配接池,JDBC是資料庫連接配接的一套API。從應用層面學習一下兩個的使用。
JDBC 連接配接示例
public class DataBaseTest {
public static Connection getConnection() throws SQLException,ClassNotFoundException {
/**
* 在加載這個類的時候,會執行靜态代碼塊中的代碼,将自己注冊到DriverManager類中
*/
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://server/yourdatabase";
String username="xxxx";
String password = "xxxx";
Connection conn = DriverManager.getConnection(url,username,password);
return conn;
}
public static void main(String[] args) {
try {
Connection conn = getConnection();
Statement sqlStatement = conn.createStatement();
String query = "select * from sequence";
ResultSet result = sqlStatement.executeQuery(query);
while(result.next()) {
Date gmtModified = result.getDate("gmt_modified");
String name = result.getString("name");
Integer value = result.getInt("value");
System.out.println("gmt_modified="+gmtModified+" name="+name+" value="+value);
}
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
以上是一個jdbc連接配接資料庫的一個demo。要是用jdbc連接配接mysql,首先要将mysql的驅動程式注冊到 DriverManager中,注冊這個操作通過
Class.forName("com.mysql.jdbc.Driver");
來完成的, 這行代碼是怎麼完成mysql驅動程式的注冊的呢?其實這個注冊的功能主要是驅動程式自己完成的,在通過Class.forName()顯示加載com.mysql.jdbc.Driver的時候,會執行Driver的靜态代碼塊,這個靜态代碼塊就調用了DriverManager添加驅動的方法,将自己注冊到了驅動管理器中。 com.mysql.jdbc.Driver将自己注冊到驅動管理器的源代碼如下:
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
以上這段代碼位于驅動程式的靜态代碼塊中,在類被Class.forName顯示加載的時候會被執行,從代碼中看以看到把自己注冊到了DriverManager中。注冊了mysql驅動之後,就可以通過DB連接配接資訊擷取到資料庫的一個Connection了。擷取Connection的這個邏輯主要是這個樣子的:
應用通過DriverManager擷取Connection,DriverManager找到合适的驅動程式後,調用驅動程式來擷取一個Connection,這個Connection一般情況下都是重新new一個,主要和連接配接池技術是相對的。 DriverManager部分源碼:
for (int i = 0; i < drivers.size(); i++) {
DriverInfo di = (DriverInfo)drivers.elementAt(i);
// If the caller does not have permission to load the driver then
// skip it.
if ( getCallerClass(callerCL, di.driverClassName ) != di.driverClass ) {
println(" skipping: " + di);
continue;
}
try {
println(" trying " + di);
Connection result = di.driver.connect(url, info);
if (result != null) {
// Success!
println("getConnection returning " + di);
return (result);
}
} catch (SQLException ex) {
if (reason == null) {
reason = ex;
}
}
}
以上這個邏輯就是循環的周遊資料總管清單,找到合适的驅動程式擷取連接配接。 driver.connect()這個主要的源碼如下:
public java.sql.Connection connect(String url, Properties info)
throws SQLException {
if (url != null) {
if (StringUtils.startsWithIgnoreCase(url, LOADBALANCE_URL_PREFIX)) {
return connectLoadBalanced(url, info);
} else if (StringUtils.startsWithIgnoreCase(url,
REPLICATION_URL_PREFIX)) {
return connectReplicationConnection(url, info);
}
}
Properties props = null;
if ((props = parseURL(url, info)) == null) {
return null;
}
try {
Connection newConn = new com.mysql.jdbc.Connection(host(props),
port(props), props, database(props), url);
return newConn;
} catch (SQLException sqlEx) {
// Don't wrap SQLExceptions, throw
// them un-changed.
throw sqlEx;
} catch (Exception ex) {
throw SQLError.createSQLException(Messages
.getString("NonRegisteringDriver.17") //$NON-NLS-1$
+ ex.toString()
+ Messages.getString("NonRegisteringDriver.18"), //$NON-NLS-1$
SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE);
}
}
DBCP使用DEMO:
public class DbcpConnection {
private static DataSource dataSource;
private static Connection con;
public DbcpConnection() {
}
public static Connection getConnection() {
if (dataSource == null) {
initDataSource();
}
try {
con = dataSource.getConnection();
print();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return con;
}
public static void initDataSource() {
FileInputStream is = null;
Properties p = new Properties();
String driverClassName = null;
String url = null;
String username = null;
String password = null;
int initialSize = 0;
int minIdle = 0;
int maxIdle = 0;
int maxWait = 0;
int maxActive = 0;
try {
String path = DbcpConnection.class.getClass().getResource("/")
.getPath();
is = new FileInputStream(path + "dbcp.properties");
p.load(is);
driverClassName = p.getProperty("dbcp.driverClassName");
url = p.getProperty("dbcp.url");
username = p.getProperty("dbcp.username");
password = p.getProperty("dbcp.password");
initialSize = Integer.parseInt(p.getProperty("dbcp.initialSize"));
minIdle = Integer.parseInt(p.getProperty("dbcp.minIdle"));
maxIdle = Integer.parseInt(p.getProperty("dbcp.maxIdle"));
maxWait = Integer.parseInt(p.getProperty("dbcp.maxWait"));
maxActive = Integer.parseInt(p.getProperty("dbcp.maxActive"));
} catch (NumberFormatException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
BasicDataSource ds = new BasicDataSource();
ds.setUrl(url);
ds.setDriverClassName(driverClassName);
ds.setUsername(username);
ds.setPassword(password);
ds.setInitialSize(initialSize); // 初始的連接配接數;
ds.setMaxActive(maxActive);
ds.setMinIdle(minIdle);
ds.setMaxIdle(maxIdle);
ds.setMaxWait(maxWait);
ds.setRemoveAbandoned(true);
ds.setRemoveAbandonedTimeout(2000);
dataSource = ds;
}
/* 用于測試連接配接狀态的方法 */
public static void print() {
BasicDataSource ds = (BasicDataSource) dataSource;
System.out.println(ds.getInitialSize());
System.out.println(ds.getNumActive());
System.out.println(ds.getNumIdle());
System.out.println(ds.getDefaultAutoCommit());
}
public static void main(String[] args) {
Connection con;
try {
con = DbcpConnection.getConnection();
print();
Statement stmt = con.createStatement();
ResultSet result = stmt.executeQuery("select * from sequence");
while (result.next()) {
Date gmtModified = result.getDate("gmt_modified");
String name = result.getString("name");
Integer value = result.getInt("value");
System.out.println("gmt_modified="+gmtModified+" name="+name+" value="+value);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} }
這段代碼主要使用了DBCP連接配接池連接配接資料庫的一個DEMO,前面一堆就是設定了資料庫連接配接池的一些參數,這些參數是從一個props檔案中讀取的。通過javax.sql.DataSource可以擷取資料的連接配接。 javax.sql.DataSource定義了DataSource的一套接口。javax中DataSource的概念可以這麼解釋: DataSource作為DriverManager的替代項,從其定義的接口中可以擷取實體資料源的連接配接,是擷取資料源連接配接的一個首選方法。DataSource接口的對象通常在基于JNDI API的命名服務中注冊。 DataSource接口的驅動實作由各供應商提供,共有三種類型的實作: (1)基本實作:生成标準的Connection對象(就是每次new個Connection出來) (2)連接配接池實作:生成自動參與連接配接池的Connection對象。此實作與中間層連接配接池管理器一起使用 (3)分布式事務實作:生成一個Connection對象,該對象可用于分布式事務,大多數情況下總是參與連接配接池。此實作與中間層事務管理器一起使用,大多數情況下總是與連接配接池管理器一起使用。 common-dbcp是common-pool在資料庫通路方面的一個具體應用,即dbcp是依賴common-tool的。
tomcat資料源配置DEMO
在tomcat中配置資料源也是非常友善的,tomcat中内置了dbcp連接配接池。可以在context.xml中配置datasource的基礎資訊,然後再代碼中通過jndi方式擷取連接配接。具體的執行個體如下: 在 %CATALINA_HOME%/conf/context.xml中 添加如下這段代碼:
<Resource
name="jdbc/testdbcp"
auth="Container"
type="javax.sql.DataSource"
maxActive="20"
maxIdel="10"
maxWait="1000"
username="xxxx"
password="xxxx"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://dbserver/yourdatabase">
</Resource>
主要填寫一下資料庫連接配接的基礎資訊。然後在java代碼中調用示例如下:
public void exeSql(String sql) {
try {
Context context = new InitialContext();
/**
* java:/comp/env/ 是固定寫法,後面接的是context.xml中的Resource中name屬性的值
*/
DataSource ds = (DataSource)context.lookup("java:/comp/env/jdbc/testdbcp");
Connection conn = ds.getConnection();
Statement stmt = conn.createStatement();
ResultSet result = stmt.executeQuery(sql);
while(result.next()) {
Date gmtModified = result.getDate("gmt_modified");
String name = result.getString("name");
Integer value = result.getInt("value");
System.out.println("gmt_modified="+gmtModified+" name="+name+" value="+value);
}
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
通過JNDI的方式擷取到DataSource,然後就可以輕松的擷取DB連接配接執行sql了,整個過程沒有看到涉及到DBCP的東西,因為DBCP的東西完全内置在tomcat中了,在初始化JNDI上下文的時候,會根據Context.xml檔案中配置的datasource資訊來裝配具體的Datasource資訊來完成對外服務。
對于tomcat對context.xml中資料源的解析可以參考這篇部落格: http://blog.csdn.net/lantian0802/article/details/9099977 這裡就不在多說了! 這篇文章主要從應用層面簡單的總結下DataSource,下篇将從源碼角度分析下DBCP連接配接池,以及tomcat的加載流程。 我的部落格:http://blog.csdn.net/lantian0802