精华帖 (0) :: 良好帖 (1) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2010-04-30
这段时间,正在学Hibernate,在学习的过程中,回去复习了一下JDBC,收获颇大。其实,很早以前(大二),就接触JDBC了,只是当时觉得很简单,大概是那时对于软件开发的认识比较肤浅吧!总认为,能够通过别人提供的高级工具,例如:Struts、Hibernate、Spring开发出一个东西出来,就是很厉害了。所以,在那时候,感觉能够熟练使用JDBC的一些API,就算是精通JDBC了。
现在才觉得,自己当时的想法是幼稚的,原谅我的年轻,我追求进步。
今天,看了一下DBCP(Apache提供的通过连接池获取Connection的一个项目),然后用MyEclipse的debug功能,看了下使用DBCP的API获取Connection的执行能够过程。接着,花了几个小时的时间,自己写了一个数据源,与大家分享,倘若在设计上有什么不合理的地方,还请大家给鄙人指出。鄙人的进步需要你们。
先谈下“为什么要使用连接池(数据源)”?
我们知道,在我们使用JDBC操作数据库的时候,过程一般都是如此的:注册数据库驱动-->取得连接(Connection)-->取得Statement或者PreparedStatement-->(可能需要)取得ResultSet,而我们知道,在这一操作中,最耗时的操作是“取得Connection”这个过程。
所以,我们在想?既然Connection创建的成本这么高。为什么不去复用它们呢!用完了就关闭,太浪费了。Connection就像一座桥,而PreparedStatement或Statement对象就像一辆车,ResultSet就像一匹货物。货物运完了,就把桥拆了,多浪费呀!所以,我们将这座桥(Connection对象)保留起来。用什么方式保留呢?一种比较容易实现的方式,当Connection对象不要用时,将Connection对象放在一个集合里,而不是关闭了。可以是ArrayList,可以是LinkedList(下面会强调两者的区别)。
好,下面就是具体的实现过程。
<?xml version="1.0" encoding="UTF-8"?> <datasource-configuraction> <!--连接设置 --> <driver-name>com.mysql.jdbc.Driver</driver-name> <url>jdbc:mysql://localhost:3306/jdbc</url> <username>root</username> <password>root</password> <!-- 初始化连接数 --> <inital-size>10</inital-size> <!-- 最大连接数 --> <max-active>20</max-active> <!-- 每个连接最大使用次数 --> <max-used>5</max-used> </datasource-configuraction>
该配置文件,配置了以下信息:
package com.langgou.jdbc.datasource.util; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; /** * 读取数据源配置信息 * * @author <a href="fumingfu1990@gmail.com">胡铭福</a> * */ public final class DataSourceCfgReader { private static Document doc; private static DataSourceInfo dateSourceInfo; private DataSourceCfgReader() { } static { try { doc = new SAXReader().read(DataSourceCfgReader.class .getClassLoader().getResource("dataSourceCfg.xml")); setDataSourceInfo(doc); } catch (DocumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //设置数据源配置信息 private static void setDataSourceInfo(Document doc) { Element driverNameElt = (Element) doc .selectObject("/datasource-configuraction/driver-name"); Element urlElt = (Element) doc .selectObject("/datasource-configuraction/url"); Element userNameElt = (Element) doc .selectObject("/datasource-configuraction/username"); Element passwordElt = (Element) doc .selectObject("/datasource-configuraction/password"); Element initalSizeElt = (Element) doc .selectObject("/datasource-configuraction/inital-size"); Element maxActiveElt = (Element) doc .selectObject("/datasource-configuraction/max-active"); Element maxUsedElt = (Element) doc .selectObject("/datasource-configuraction/max-used"); dateSourceInfo = new DataSourceInfo(); dateSourceInfo.setDriverName(driverNameElt.getText()); dateSourceInfo.setUrl(urlElt.getText()); dateSourceInfo.setUserName(userNameElt.getText()); dateSourceInfo.setPassword(passwordElt.getText()); dateSourceInfo.setInitalSize(Integer.parseInt(initalSizeElt.getText())); dateSourceInfo.setMaxActive(Integer.parseInt(maxActiveElt.getText())); dateSourceInfo.setMaxUsed(Integer.parseInt(maxUsedElt.getText())); } //取得数据源配置信息 public static DataSourceInfo getDataSourceInfo() { return dateSourceInfo; } }
package com.langgou.jdbc.datasource.util; /** * 保存数据源配置信息 * @author <a href="fumingfu1990@gmail.com">胡铭福</a> * */ public class DataSourceInfo { /** * 连接设置 */ private String driverName; private String url; private String userName; private String password; /** * 初始化的Connection个数 */ private int initalSize; /** * 已创建Connection的最大个数 */ private int maxActive; /** * Connection的最大使用次数 */ private int maxUsed; public String getDriverName() { return driverName; } public void setDriverName(String driverName) { this.driverName = driverName; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public int getInitalSize() { return initalSize; } public void setInitalSize(int initalSize) { this.initalSize = initalSize; } public int getMaxActive() { return maxActive; } public void setMaxActive(int maxActive) { this.maxActive = maxActive; } public int getMaxUsed() { return maxUsed; } public void setMaxUsed(int maxUsed) { this.maxUsed = maxUsed; } }
package com.langgou.jdbc.datasource; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.LinkedList; import com.langgou.jdbc.datasource.util.DataSourceCfgReader; import com.langgou.jdbc.datasource.util.DataSourceInfo; public class MyDataSource { /** * 数据源配置文件中的所有信息,当MyDateSource一被加载入虚拟机,就读取所有配置文件信息 */ protected static DataSourceInfo dataSourceInfo = DataSourceCfgReader .getDataSourceInfo(); private final static String PROP_DRIVERCLASSNAME = dataSourceInfo .getDriverName(); private final static String PROP_URL = dataSourceInfo.getUrl(); private final static String PROP_USERNAME = dataSourceInfo.getUserName(); private final static String PROP_PASSWORD = dataSourceInfo.getPassword(); /** * 初始化的Connection个数 */ private final static int PROP_INITIALSIZE = dataSourceInfo.getInitalSize(); /** * 最大的Connection个数 */ private final static int PROP_MAXACTIVE = dataSourceInfo.getMaxActive(); /** * 每个Connection的最大使用次数 */ private final static int PROP_MAXUSED = dataSourceInfo.getMaxUsed(); /** * 记录已创建的Connection个数 */ protected int currentCount = 0; /** * 连接池,将connectionPool设成protected的,只允许包内访问。外部只能通过调用getConnection()方法取得Connection */ protected LinkedList<Connection> connectionPool = new LinkedList<Connection>(); /** * 当MyDataSource被加载到虚拟机,就注册数据库驱动 */ static { try { Class.forName(PROP_DRIVERCLASSNAME); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 当MyDataSource被实例化,就创建一批Connection,放入连接池里 构造方法 */ public MyDataSource() { try { for (int i = 0; i < PROP_INITIALSIZE; i++) { this.connectionPool.addLast(this.createConnection()); this.currentCount++; } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * * @author <a href="fumingfu1990@gmail.com">胡铭福</a> 功能:从连接池里取得Connection * @return */ public Connection getConnection() throws SQLException { // Connection属于非线程安全的,要确保每个线程拿到的都是不相同的Connection synchronized (connectionPool) { // 判断连接池里是否还有Connection if (this.connectionPool.size() > 0) { // 如果连接池里还有Connection,则直接从连接池中取出Connection return connectionPool.removeFirst(); } else if (this.currentCount < PROP_MAXACTIVE) { // 判断当前的Connection个数是否超过Connection个数的上限 // 因为数据库所能承受的Connection个数是有限的,不能让其无限创建下去 this.currentCount++; try { // 创建一个新的Connection return this.createConnection(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } throw new SQLException("已经没有连接啦!"); } } /** * * @author <a href="fumingfu1990@gmail.com">胡铭福</a> 功能:释放Connection * @param conn */ public void free(Connection conn) { // 为了达到Connection的复用,不能将Connection关闭,要放回连接池中 this.connectionPool.add(conn); } /** * * @author <a href="fumingfu1990@gmail.com">胡铭福</a> 功能:创建Connection * @return * @throws SQLException * 问题:创建Connection的时候,采用ThreadLocal封装,是否有效? */ private Connection createConnection() throws SQLException { // //采用ThreadLocal封装Connection,确保每个线程只对应一个Connection // ThreadLocal<Connection> connectionHolder = new // ThreadLocal<Connection>(); Connection conn = null; try { // 取得与当前线程绑定的Connection // conn = connectionHolder.get(); // 如果没有与当前线程绑定的Connection // if(conn==null){ // 创建Connection conn = DriverManager.getConnection(PROP_URL, PROP_USERNAME,PROP_PASSWORD); // 将创建的Connection与当前线程绑定 // connectionHolder.set(conn); // } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } return conn; } // /** // * // * @author <a href="fumingfu1990@gmail.com">胡铭福</a> // * 功能:测试程序 // * @param args // */ // public static void main(String[] args) { // MyDataSource myDataSource = new MyDataSource(); // // for(int i=0;i<5;i++){ // try { // System.out.println(myDataSource.getConnection()); // } catch (SQLException e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } // } // } }
存在的问题:由于当用户显式的调用Connection对象的close()方法时,默认会将Connection对象关闭,而不是返回连接池中,从而不能达到Connection的复用。所以要采取一种机制,改写掉Connection的close()方法默认的行为。
解决方案: 一般采用采用JDK动态代理,拦截所有Connection对象的close()方法,由代理类实现。将默认的关闭Connection对象的行为改为将Connection对象返回连接池,从而达到Connection对象的复用。而除了close()方法,外的其余方法,都由真实对象(realConnection对象)实现.
------------------------------------------------------------------这是一条分割线----------------------------------------------------------------------------------------------
由于时间关系,采用代理类包装Connection,这篇文章就不再介绍了。下篇文章,会详细介绍,并实现类似于DBCP包装Connection的方法。
由于是“处女贴”,写的不好的地方,请谅解。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
浏览 3094 次