前言:上一篇文章中讲了DBCP的用法以及实现原理, 这一篇再来说下C3P0和JDNI的用法.
1.1、C3P0数据源
C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate,Spring等。C3P0数据源在项目开发中使用得比较多。
- dbcp没有自动回收空闲连接的功能
- c3p0有自动回收空闲连接功能
1.2、在应用程序中加入C3P0连接池
1.导入相关jar包
c3p0-0.9.2-pre1.jar、mchange-commons-0.2.jar,如果操作的是Oracle数据库,那么还需要导入c3p0-oracle-thin-extras-0.9.2-pre1.jar 2、在类目录下加入C3P0的配置文件:c3p0-config.xmlc3p0-config.xml的配置信息如下:
com.mysql.jdbc.Driver jdbc:mysql://localhost:3306/jdbcstudy root XDP 5 10 5 20 com.mysql.jdbc.Driver jdbc:mysql://localhost:3306/jdbcstudy root XDP 5 10 5 20
如下图所示:
C3P0使用实例: C3P0Utils.java
1 public class C3P0Utils { 2 private static DataSource dataSource = new ComboPooledDataSource(); 3 4 //从池中获取一个连接 5 public static Connection getConnection() throws SQLException{ 6 return dataSource.getConnection(); 7 } 8 9 public static void closeAll(ResultSet rs,Statement stmt,Connection conn){10 if(rs!=null){11 try {12 rs.close();13 } catch (SQLException e) {14 e.printStackTrace();15 }16 }17 18 if(stmt!=null){19 try {20 stmt.close();21 } catch (SQLException e) {22 e.printStackTrace();23 }24 }25 26 if(conn!=null){27 try {28 conn.close();//关闭29 } catch (SQLException e) {30 e.printStackTrace();31 }32 }33 }34 }
测试类:UserDaoImpl.java
1 public class UserDaoImpl { 2 @Test 3 public void testInsert(){ 4 Connection conn = null; 5 PreparedStatement ps = null; 6 try { 7 conn = C3P0Utils.getConnection(); 8 ps = conn.prepareStatement(""); 9 //...10 System.out.println(conn.getClass().getName());11 } catch (SQLException e) {12 e.printStackTrace();13 }finally{14 C3P0Utils.closeAll(null, ps, conn);15 16 }17 }18 }
private static DataSource dataSource = new ComboPooledDataSource();
ComboPooledDataSource.java
1 // not reassigned post-ctor; mutable elements protected by their own locks 2 // when (very rarely) necessery, we sync this -> wcpds -> dmds 3 4 // note that serialization of these guys happens via out superclass 5 // we just have to make sure they get properly reset on deserialization 6 transient DriverManagerDataSource dmds; 7 transient WrapperConnectionPoolDataSource wcpds; 8 9 public ComboPooledDataSource()10 { this( true ); }11 12 public ComboPooledDataSource( boolean autoregister )13 {14 super( autoregister );15 16 // System.err.println("...Initializing ComboPooledDataSource.");17 18 dmds = new DriverManagerDataSource();19 wcpds = new WrapperConnectionPoolDataSource();20 21 wcpds.setNestedDataSource( dmds );22 23 try24 { this.setConnectionPoolDataSource( wcpds ); }25 catch (PropertyVetoException e)26 {27 logger.log(MLevel.WARNING, "Hunh??? This can't happen. We haven't set up any listeners to veto the property change yet!", e);28 throw new RuntimeException("Hunh??? This can't happen. We haven't set up any listeners to veto the property change yet! " + e);29 }30 31 // set things up in case there are future changes to our ConnectionPoolDataSource32 //33 setUpPropertyEvents();34 }
具体更多细节 还请大家看下源码了, 这里我就不继续往后扩展了. 源码下载地址:
2.1 , JDNI
JNDI(Java Naming and Directory Interface),Java命名和目录接口,它对应于J2SE中的javax.naming包,
这 套API的主要作用在于:它可以把Java对象放在一个容器中(JNDI容器),并为容器中的java对象取一个名称,以后程序想获得Java对象,只需 通过名称检索即可。其核心API为Context,它代表JNDI容器,其lookup方法为检索容器中对应名称的对象。Tomcat服务器创建的数据源是以JNDI资源的形式发布的,所以说在Tomat服务器中配置一个数据源实际上就是在配置一个JNDI资源,通过查看Tomcat文档,我们知道使用如下的方式配置tomcat服务器的数据源:
12 7
服务器创建好数据源之后,我们的应用程序又该怎么样得到这个数据源呢,Tomcat服务器创建好数据源之后是以JNDI的形式绑定到一个JNDI容器中的,我们可以把JNDI想象成一个大大的容器,我们可以往这个容器中存放一些对象,一些资源,JNDI容器中存放的对象和资源都会有一个独一无二的名称,应用程序想从JNDI容器中获取资源时,只需要告诉JNDI容器要获取的资源的名称,JNDI根据名称去找到对应的资源后返回给应用程序。我们平时做javaEE开发时,服务器会为我们的应用程序创建很多资源,比如request对象,response对象,服务器创建的这些资源有两种方式提供给我们的应用程序使用:第一种是通过方法参数的形式传递进来,比如我们在Servlet中写的doPost和doGet方法中使用到的request对象和response对象就是服务器以参数的形式传递给我们的。第二种就是JNDI的方式,服务器把创建好的资源绑定到JNDI容器中去,应用程序想要使用资源时,就直接从JNDI容器中获取相应的资源即可。
对于上面的name="jdbc/datasource"数据源资源,在应用程序中可以用如下的代码去获取
1 Context initCtx = new InitialContext();2 Context envCtx = (Context) initCtx.lookup("java:comp/env");3 dataSource = (DataSource)envCtx.lookup("jdbc/datasource");
此种配置下,数据库的驱动jar文件需放置在tomcat的lib下
2.2、配置Tomcat数据源
1、在Web项目的WebRoot目录下的META-INF目录创建一个context.xml文件
如下图所示:
2、在context.xml文件配置tomcat服务器的数据源
12 12
3、将数据库的驱动jar文件需放置在tomcat的lib下
4、在获取数据库连接的工具类(如jdbcUtils)的静态代码块中获取JNDI容器中的数据源
1 public class JdbcUtils_JNDI { 2 3 private static DataSource ds = null; 4 //在静态代码块中创建数据库连接池 5 static{ 6 try{ 7 //初始化JNDI 8 Context initCtx = new InitialContext(); 9 //得到JNDI容器10 Context envCtx = (Context) initCtx.lookup("java:comp/env");11 //从JNDI容器中检索name为jdbc/datasource的数据源12 ds = (DataSource)envCtx.lookup("jdbc/datasource");13 }catch (Exception e) {14 throw new ExceptionInInitializerError(e);15 }16 }17 18 public static Connection getConnection() throws SQLException{19 //从数据源中获取数据库连接20 return ds.getConnection();21 }22 23 24 public static void release(Connection conn,Statement st,ResultSet rs){25 if(rs!=null){26 try{27 //关闭存储查询结果的ResultSet对象28 rs.close();29 }catch (Exception e) {30 e.printStackTrace();31 }32 rs = null;33 }34 if(st!=null){35 try{36 //关闭负责执行SQL命令的Statement对象37 st.close();38 }catch (Exception e) {39 e.printStackTrace();40 }41 }42 43 if(conn!=null){44 try{45 //将Connection连接对象还给数据库连接池46 conn.close();47 }catch (Exception e) {48 e.printStackTrace();49 }50 }51 }52 }
写一个Servlet测试JNDI数据源
1 public class JNDITest extends HttpServlet { 2 3 public void doGet(HttpServletRequest request, HttpServletResponse response) 4 throws ServletException, IOException { 5 Connection conn = null; 6 PreparedStatement st = null; 7 ResultSet rs = null; 8 try{ 9 //获取数据库连接10 conn = JdbcUtils_JNDI.getConnection();11 String sql = "insert into test1(name) values(?)";12 st = conn.prepareStatement(sql);13 st.setString(1, "gacl");14 st.executeUpdate();15 //获取数据库自动生成的主键16 rs = st.getGeneratedKeys();17 if(rs.next()){18 System.out.println(rs.getInt(1));19 }20 }catch (Exception e) {21 e.printStackTrace();22 }finally{23 //释放资源24 JdbcUtils_JNDI.release(conn, st, rs);25 }26 }27 28 public void doPost(HttpServletRequest request, HttpServletResponse response)29 throws ServletException, IOException {30 doGet(request, response);31 }33 }