问题:
本案例要求使用Apach DBCP 连接数据池重构类DBUtility 为ConnectionSource类,并重构案例“实现DBUtility”,提供连接的获取,关闭功能的"中的EmpDAO类,在该类中使用ConnectionSource来获取连接。
方案:
直接使用JDBC访问数据库时,需要避免一下隐患:
1.每一次数据操作请求都是需要建立数据库连接,打开连接,存取数据和关闭数据等步骤。而建立和打开数据库是一件既耗资源有费时间的过程,如果频繁发生这样的数据库操作,势必会使系统性能下降。
2.连接对象代表着数据库系统的连接进程,是有限的资源。如果系统的使用用户非常多,有可能超出数据库服务器的承受极限,造成系统的崩溃。
数据库连接池是解决上述问题最常用的方法。所谓连接池,即可以创建并持有数据库连接的组件。连接池可以预先创建并封装一些连接对象并将其缓存起来,当需要使用连接对象时可以向连接池“借”一个“连接”,用完之后将其“归还”到连接池中。数据库连接池的主要功能如下:
1>连接池对象的创建和释放。
2>服务器启动时,创建指定数量的数据库连接。
3>为用户请求提供可用连接。如果没有空闲连接,且连接数没有超出最大值,创建一个新的数据库连接。
4>将用户不再使用的连接标识为可用连接,等待其他用户请求。
5>当空闲的连接数过多时,释放连接对象。
连接池组件一般都需要实现JDBC规范中的javax.sql.DataSource接口。DataSource接口定义了获取连接对象的方法getConnection()方法。常用的连接池组件有DBCP、c3p0和proxool等,本案例以Apache的DBCP组件为例,来实现数据库连接池。简单的应用代码如下;
BasicDataSource ds=new BasicDataSource(); ds.setUrl("jdbc:mysql://127.0.0.1:3306/emp"); ds.setDriverClassName("com.mysql.jdbc.Driver"); ds.setUsername("wonderq"); ds.setPassword("root"); Connection con=ds.getConnection(); Statement stmt=con.createStatement(); ResultSet rs=stmt.executeQuery("select count(*) from emp") if(rs.next()){ System.out.println(rs.getInt(1)); } stmt.close(); con.close();
步骤:
实现此案例需要按照如下步骤进行;
步骤一:导入使用DBCP组件所需的jar包
在当前工程下,导入使用DBCP组件所需的jar包,包括commons-dbcp.jar以及commons-pool.jar两个jar包,这两个jar包的名字可能会因为版本的不同,名字的最后版本信息,例如:commons-dbcp-1.4.jar commons-pool-1.5.jar
有时还需要commons-collections-3.1.jar
步骤二:重构db.properties
重构此文件,在该文件中添加创建数据库连接池所需的信息,包括初始化连接数、最大空闲连接数、最小空闲连接数、最大连接数量以及超时回收时间。该文件内容如下:
<span style="white-space:pre"> </span>jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/emp jdbc.user=wonderq jdbc.password=root dataSource.initialSize=10;//初始化连接大小 dataSource.maxIdle=20;//最大空闲数 dataSource.minIdle=5;//最小空闲数 dataSource.maxActive=50;//最大连接数 dataSource.maxWait=1000//超时等待时间,以毫秒为单位。
步骤三:穿件ConnectionSource类,然后在该类中添加init方法,在该方法中对数据源信息进行初始化,代码如下所示:
package dao; import java.io.IOException; import java.sql.Connection; import java.sql.SQLException; import java.util.Properties; import org.apache.commons.dbcp.BasicDataSource; public class ConnectionSource { private static BasicDataSource dataSource=null; public ConnectionSource(){ } public static void init(){ Properties dbprops=new Properties(); //取配置文件可以根据实际的不同修改 try { dbprops.load(ConnectionSource.class.getClassLoader().getResourceAsStream("db.properties")); } catch (Exception e) { e.printStackTrace(); } try { String driverClassName=dbprops.getProperty("jdbc.driver"); String url=dbprops.getProperty("jdbc.url"); String username=dbprops.getProperty("jdbc.user"); String password=dbprops.getProperty("jdbc.password"); String initialSize=dbprops.getProperty("dataSource.initialSize"); String minIdle=dbprops.getProperty("dataSource.minIdle"); String maxIdle=dbprops.getProperty("dataSource.maxIdle"); String maxWait=dbprops.getProperty("dataSource.maxWait"); String maxActive=dbprops.getProperty("dataSource.maxActive"); dataSource =new BasicDataSource(); dataSource.setDriverClassName(driverClassName); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); //初始化连接数 if(initialSize!=null){ dataSource.setInitialSize(Integer.parseInt(initialSize)); } //最小空闲连接 if(minIdle!=null){ dataSource.setMinIdle(Integer.parseInt(minIdle)); } //最大空闲连接 if(maxIdle!=null){ dataSource.setMaxIdle(Integer.parseInt(maxIdle)); } //超时回收时间(以毫秒为单位) if(maxWait!=null){ dataSource.setMaxWait(Long.parseLong(maxWait)); } //最大连接数 if(maxActive!=null){ if(!maxActive.trim().equals("0")){ dataSource.setMaxActive(Integer.parseInt(maxActive)); } } } catch (Exception e) { e.printStackTrace(); System.out.println("创建连接池失败!请检查设置!!!"); } } }
步骤四:添加获取连接的方法
在ConnectionSource类中添加获取连接的方法,getConnection,代码如下所示:
public static synchronized Connection getConnection() throws SQLException{ if(dataSource==null){ init(); } Connection con=null; if(dataSource!=null){ con=dataSource.getConnection(); } return con; }
步骤四:重构EmpDAO类
重构EmpDAO类,在该类中使用ConnectionSource类的getConnection()方法获取连接,代码如下所示:
package dao; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class EmpDAO { public static void main(String [] args){ EmpDAO dao=new EmpDAO(); dao.findAll(); } public void findAll(){ Connection con=null; Statement stmt=null; ResultSet rs=null; try { con=ConnectionSource.getConnection(); stmt=con.createStatement(); rs=stmt.executeQuery("select empno,ename,sal,hiredate from emp;"); while(rs.next()){ System.out.println(rs.getInt("empno")+","+rs.getString("ename")+","+rs.getDouble("sal")+","+rs.getDate("hiredate")); } } catch (SQLException e) { System.out.println("数据库访问异常!"); throw new RuntimeException(e); } finally{ try { if(rs!=null){ rs.close(); } if(stmt!=null){ stmt.close(); } if(con!=null){ con.close(); } } catch (SQLException e) { System.out.println("释放资源时发生异常!"); } } } }
在此,调用Connection类的close方法关闭连接,会将该连接归还到连接池中。
运行EmpDAO类,输出如下:
和之前的案例输出结果一致。
本节,先更新到这里,下次继续写:如何更新和插入Emp数据。