数据库连接池
JDBC的弊端
用户每次请求都需要向数据库获得连接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设一个网站每天有10万次访问量,那么数据库服务器就需要创建10万次连接,这极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、脱机
为了优化性能,应用程序一启动,就向数据库要一批连接放到池(也就是一个集合而已)中,当用户箱操作数据库的时候,直接找连接池要,当完成对数据库的操作后,再把连接还给连接池供其他用户使用。
public class JdbcPool implements DataSource { private static LinkedList<Connection> list = new LinkedList<Connection>(); static{ try{ InputStream in = JdbcPool.class.getClassLoader().getResourceAsStream("db.properties"); Properties prop = new Properties(); prop.load(in);//加载配置文件 String driver = prop.getProperty("driver"); String url = prop.getProperty("url"); String username = prop.getProperty("username"); String password = prop.getProperty("password"); Class.forName(driver); //加载驱动 //连接池中创建10个Connection for(int i = 0; i < 10; i++){ Connection conn = DriverManager.getConnection(url, username, password); System.out.println("获取到了连接" + conn); list.add(conn); } } catch(Exception e){ e.printStackTrace(); throw new ExceptionInInitializerError(e); } } /* * 用动态代理,返回一个代理对象出去,拦截close方法的调用,对close进行增强 */ @Override public synchronized Connection getConnection() throws SQLException { if(list.size() > 0){ final Connection conn = list.removeFirst();//删掉并返回给conn // return conn;//这里不能直接return,因为用户使用完了后,调用conn.close()会操作数据库,并没有把这个conn返回给连接池中 System.out.println("池大小是" + list.size()); //下面用动态代理技术来写: //动态代理技术:使用的是拦截技术 return (Connection) Proxy.newProxyInstance(JdbcPool.class.getClassLoader(), new Class[]{Connection.class}, new InvocationHandler() { //这里的第二个参数原来为conn.getClass.getInterface(),不过会出错,最终改成了new Class[]{Connection.class} //原因见帖子:http://blog.csdn.net/njchenyi/article/details/3091092 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(!method.getName().equals("close")){//如果判断不是调用close方法,不管就是了 return method.invoke(conn, args); } else{//如果是调用close方法,将conn放到连接池里 list.add(conn); System.out.println(conn + "被还到池中"); System.out.println("池大小为" + list.size()); return null; } } }); } else{ throw new RuntimeException("对不起,数据库忙"); } } //下面是其他需要实现的方法,默认生成即可,不用写代码 @Override ...... }
数据库连接池
很多web服务器(Weblogic,WebSphere, Tomcat)都提供DataSource的实现,即连接池的实现。通常我们把DataSource的实现,称之为数据源,数据源中都包含了数据库连接池的实现。也有一些开源组织提供了数据源的独立实现:如DBCP数据库连接池和C3P0数据库连接池(Spring中用的就是这个)。使用这些连接池时不需要编写连接数据库的代码,直接从数据源获得数据库的连接,程序员编程时也尽量使用这些数据源的实现,以提升程序的数据库访问性能。
DBCP是Apache软件基金组织下的开源连接池实现,使用DBCP数据源需要在程序中加入下面两个jar包:
commons-dbcp.jar:连接池的实现;commons-pool.jar:连接池实现的依赖库
Tomcat的连接池正是采用DBCP连接池来实现的,该数据库连接池既可以与应用服务器整合使用,也可以独立使用,下面我们使用DBCP来改写上面的JdbcUtils工具类:
public class JdbcUtils_DBCP { private static DataSource ds = null;//定义数据源 static{ try{ InputStream in = JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("dbcp.properties"); Properties prop = new Properties(); prop.load(in); BasicDataSourceFactory factory = new BasicDataSourceFactory();//数据源工厂 ds = factory.createDataSource(prop);//工厂产生数据源 System.out.println(ds); }catch(Exception e){ throw new ExceptionInInitializerError(e); } } public static synchronized Connection getConnection() throws SQLException { Connection conn = ds.getConnection();//从数据源中拿一个Connection来用~ return conn; } public static void release(Connection conn, Statement st, ResultSet rs) { if(rs != null){ try{ rs.close(); }catch(Exception e) { e.printStackTrace(); } rs = null; } if(st != null){ try{ st.close(); }catch(Exception e) { e.printStackTrace(); } st = null; } if(conn != null){ try{ conn.close(); }catch(Exception e) { e.printStackTrace(); } conn = null; } } }
从上面代码中可以看出,获得数据源dataSource后,就可以直接通过数据源拿到Connection,说明拿之前连接池中已经放好了一些Connection了,这些都已经被DBCP封装好,我们不用去管,我们需要做的就是在配置文件中做一些配置,DBCP会根据配置文件中的配置去初始化数据库连接池的。我们看一下都需要配置啥:
#连接设置 driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/databasename username=root password=root #初始化连接:10个 initialSize=10 #最大连接数量 maxActive=50 #最大空闲连接 maxIdle=20 #最小空闲连接 minIdle=5 #超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 maxWait=60000 #JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;] #注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。 connectionProperties=useUnicode=true;characterEncoding=gbk #指定由连接池所创建的连接的自动提交(auto-commit)状态。 defaultAutoCommit=true #driver default 指定由连接池所创建的连接的只读(read-only)状态。 #如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix) defaultReadOnly= #driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。 #可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE defaultTransactionIsolation=READ_UNCOMMITTED
C3P0连接池
用C3P0数据库连接池,需要导入下面两个jar包
c3p0-0.9.5.1.jar mchange-commons-java-0.2.10.jar
使用C3P0来改写JdbcUtils工具类了:
public class JdbcUtils_C3P0 { private static ComboPooledDataSource ds = null; static { try { //配置文件可以用properties文件,也可以用xml,这里使用xml配置,下面给出配置好的xml(要放在类路径下) ds = new ComboPooledDataSource(); //使用默认配置 //ds = new ComboPooledDataSource("mysql"); //指定配置 } catch (Exception e) { throw new ExceptionInInitializerError(e); } } public static synchronized Connection getConnection() throws SQLException { Connection conn = ds.getConnection(); return conn; } public static void release(Connection conn, Statement st, ResultSet rs) {....} }
C3P0的配置文件c3p0-config.xml:
<?xml version="1.0" encoding="UTF-8"?> <c3p0-config> <default-config> <!--默认配置--> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql://localhost:3306/databasename</property> <property name="user">root</property> <property name="password">root</property> <property name="acquireIncrement">5</property> <property name="initialPoolSize">10</property> <property name="minPoolSize">5</property> <property name="maxPoolSize">20</property> <!-- intergalactoApp adopts a different approach to configuring statement caching --> <property name="maxStatements">0</property> <property name="maxStatementsPerConnection">5</property> <!-- he's important, but there's only one of him --> </default-config> <named-config name="mysql"> <!--mysql的配置,在new ComboPooledDataSource()时候括号中指定,所以还可以再对oracle进行配置--> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql://localhost:3306/databasename</property> <property name="user">root</property> <property name="password">root</property> <property name="acquireIncrement">5</property> <property name="initialPoolSize">10</property> <property name="minPoolSize">5</property> <property name="maxPoolSize">20</property> <!-- intergalactoApp adopts a different approach to configuring statement caching --> <property name="maxStatements">0</property> <property name="maxStatementsPerConnection">5</property> <!-- he's important, but there's only one of him --> </named-config> </c3p0-config>
配置Tomcat数据源
Tomcat服务器在启动时可以帮我们创建一个池,这样我们可以直接利用这个连接池,但是需要进行配置。在META-INF目录下新建一个context.xml文档(也可以在WEB-INF目录下的web.xml中进行配置),然后在里面进行如下配置:
<Context>
<Resource name="jdbc/EmployeeDB"
auth="Container"
type="javax.sql.DataSource"
username="root"
password="root"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/day16"
maxTotal="8"
maxIdle="4"/>
</Context>
其他参数可以参照dbcp的配置文件进行设置,因为tomcat连接池内部就是dbcp。然后新建一个servlet,在servlet中编写如下代码(Tomcat连接池的模板代码):
try { Context initCtx = new InitialContext();// 初始化jndi Context envCtx = (Context) initCtx.lookup("java:comp/env");// 得到jndi容器 DataSource ds = (DataSource) envCtx.lookup("jdbc/EmployeeDB");// 从容器中检索连接池 Connection conn = ds.getConnection(); System.out.println(conn); } catch (Exception e) { e.printStackTrace(); }
参考:
https://blog.csdn.net/eson_15/article/details/51454428
浙公网安备 33010602011771号