(十) Web与企业应用中的连接管理
?
1.JNDI
在企业环境中部署JDBC应用时,数据库连接管理与JNDI(Java名字和目录接口)是集成在一起的。
e.g. ?Context jndiContext = new InitialContext();
? ? ? DataSource source = (DataSource)jndiContext.lookup("java:comp/env/jdbc/corejava");
? ? ? Connection conn = source.getConnection();
注意:不再使用DriverManager,而是使用JNDI服务来定位数据源。一个数据源就是一个能够提供简单的JDBC连接和更高级服务的接口。javax.sql标准扩展包定义了DataSource接口。
?
在JavaEE 5的容器中,甚至可以不必编程进行JNDI查找,只需要在DataSource域上使用Resource注解,当加载应用时,这个数据源引用将被设置。
e.g. ?@Resource("jdbc/corejava")
? ? ? private DataSource source;
?
2.连接池
因为数据库连接是有限的资源,如果用户在某个时刻离开应用,那么他占用的连接就不应该保持开发状态;另一方面每次查询都要获取连接并在随后关闭它的代价也是相当高的。
解决这个问题的办法是建立数据库连接池,这意味着数据库连接在物理上并未被关闭,而是保留在一个队列中并被反复重用。
连接池可以通过获取数据源并调用getConnection方法得到连接池中的连接。使用完连接后,调用close方法,使用该方法并不在物理上关闭连接,而只是告诉连接池已经使用完该连接。连接池通常还会将池机制用于预备语句上。
import java.io.FileInputStream;import java.sql.Connection;import java.sql.DriverManager;import java.sql.Statement;import java.util.Properties;import com.mchange.v2.c3p0.ComboPooledDataSource;public class DBUtil {private static ComboPooledDataSource dataSource = null;private static Properties param = null;private static String prefix = null;static{FileInputStream fis = null;try {fis = new FileInputStream(System.getProperty("user.dir") + "/bin/database.properties");param = new Properties();param.load(fis);fis.close();if("MySQL".equalsIgnoreCase(param.getProperty("DatabaseType"))){prefix = "mysql.";}else if("Derby".equalsIgnoreCase(param.getProperty("DatabaseType"))){prefix = "derby.";}}catch(Exception e){e.printStackTrace();}}public static Connection getConnection(){try{String drivers = param.getProperty(prefix + "jdbc.drivers");if (drivers != null && !"".equals(drivers)) {System.setProperty("jdbc.drivers", drivers);}String url = param.getProperty(prefix + "jdbc.url");String username = param.getProperty(prefix + "jdbc.username");String password = param.getProperty(prefix + "jdbc.password");return DriverManager.getConnection(url,username,password);}catch(Exception e){e.printStackTrace();return null;}}public static Connection getConnectionByC3P0(){try{if(dataSource == null){DBUtil.C3P0Init();}return dataSource.getConnection();}catch(Exception e){e.printStackTrace();return null;}} public static void C3P0Init(){ try{ dataSource = new ComboPooledDataSource(); String drivers = param.getProperty(prefix+"jdbc.drivers"); if (drivers != null && !"".equals(drivers)) { dataSource.setDriverClass(drivers); } dataSource.setJdbcUrl(param.getProperty(prefix + "jdbc.url")); dataSource.setUser(param.getProperty(prefix + "jdbc.username")); dataSource.setPassword(param.getProperty(prefix + "jdbc.password")); //连接池中保留的最大连接数 dataSource.setMaxPoolSize(20); //连接池中保留的最小连接数 dataSource.setMinPoolSize(3); //初始化时获取三个连接,取值应在minPoolSize与maxPoolSize之间。Default: 3 dataSource.setInitialPoolSize(3); //最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 dataSource.setMaxIdleTime(0); //当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 dataSource.setAcquireIncrement(3); //定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 dataSource.setAcquireRetryAttempts(30); //两次连接中间隔时间,单位毫秒。Default: 1000 dataSource.setAcquireRetryDelay(1000); //如果设为true那么在取得连接的同时将校验连接的有效性。Default: false dataSource.setTestConnectionOnCheckin(false); //c3p0将建一张名为Test的空表,并使用其自带的查询语句进行测试。如果定义了这个参数那么 //属性preferredTestQuery将被忽略。你不能在这张Test表上进行任何操作,它将只供c3p0测试 //使用。Default: null dataSource.setAutomaticTestTable(null); //当连接池用完时客户端调用getConnection()后等待获取新连接的时间,超时后将抛出 //SQLException,如设为0则无限期等待。单位毫秒。Default: 0 dataSource.setCheckoutTimeout(0); //JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。 dataSource.setMaxStatements(0); }catch(Exception e){ e.printStackTrace(); } } public static boolean createTable(Connection conn){try{Statement stmt = conn.createStatement();String createTable = "CREATE TABLE test_table(" + " id INT," + " str VARCHAR(20)," + " ds TIMESTAMP," + " PRIMARY KEY (`id`))";stmt.executeUpdate(createTable);stmt.close();return true;}catch(Exception e){e.printStackTrace();}return false;}public static boolean destroyTable(Connection conn){try{Statement stmt = conn.createStatement();stmt.execute("DROP TABLE test_table");stmt.close();return true;}catch(Exception e){e.printStackTrace();}return false;}}?import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;import java.sql.Timestamp;import java.util.Date;import com.shaogq.jdbc.util.DBUtil;public class TestC3P0 {public static void main(String[] args) {Connection conn = null;try {try{//JDBC//conn = DBUtil.getConnection();//连接池conn = DBUtil.getConnectionByC3P0();//DBUtil.createTable(conn);TestC3P0 t = new TestC3P0();//t.init(conn);//t.insertTimestamp(conn);//t.moreResultSelect(conn);t.insertGeneratedKeys(conn);}finally{//DBUtil.destroyTable(conn);conn.close();}} catch (SQLException e) {e.printStackTrace();}}/** * 初始化数据库表中数据 */public void init(Connection conn){try{PreparedStatement prepStmt = null;try{String insertDate = "INSERT INTO test_table values(?,?,?)";prepStmt = conn.prepareStatement(insertDate);int[] ch = new int[]{1,2,3,4,5,6,7,8};String[] str = new String[]{"a","b","c","d","e","f","g","h"};for(int i=0;i<ch.length&&i<str.length;i++){prepStmt.setInt(1, ch[i]);prepStmt.setString(2, str[i]);prepStmt.setTimestamp(3, new Timestamp(new Date().getTime()));prepStmt.executeUpdate();}prepStmt.close();}finally{prepStmt.close();}}catch(SQLException e){for(Throwable t : e){t.printStackTrace();}}}/** * 通过转义插入时间 */public void insertTimestamp(Connection conn){try{Statement stmt = null;try{stmt = conn.createStatement();String sql = "INSERT INTO test_table VALUES(100,'abc',{ts '2011-11-11 23:59:59'})";stmt.executeUpdate(sql);}finally{stmt.close();}}catch(SQLException e){for(Throwable t : e){t.printStackTrace();}}}/** * 查询更多结果集 */public void moreResultSelect(Connection conn){try{PreparedStatement prepStmt = null;try{String sql = "SELECT * FROM test_table where str=? ";prepStmt = conn.prepareStatement(sql);prepStmt.setString(1, "abc");boolean hasResult = prepStmt.execute();if(hasResult){do{ResultSet result = prepStmt.getResultSet();while(result.next()){System.out.println(result.getString("str") + "," + result.getInt("id") + "," + result.getTimestamp("ds"));}}while(prepStmt.getMoreResults());}}finally{prepStmt.close();}}catch(SQLException e){for(Throwable t : e){t.printStackTrace();}}}/** * 测试自动插入主键 */private void insertGeneratedKeys(Connection conn) {try{Statement stmt = null;try{stmt = conn.createStatement();String sql = "INSERT INTO test_table VALUES('a',{ts '2011-11-11 23:59:59'})";stmt.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS);}finally{}}catch(SQLException e){for(Throwable t : e){t.printStackTrace();}}}}?JNDI+C3P0tomcat/conf/context.xml
<!-- 一种实现方式 不用写web.xml<Resource name="TestJNDI" auth="Container" type="javax.sql.DataSource" maxActive="100" maxIdle="30" maxWait="10000" testOnBorrow="true" testOnReturn="true" testWhileIdle="true" validationQuery=" select 1 " username="root" password="12345678" driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/corejava"/>--><!--下面注释部分为使用jndi+tomcat自带的连接池 需要写web.xml<Resource name="TestJNDI" type= "javax.sql.DataSource"username="root" password= "12345678" driverClassName= "com.mysql.jdbc.Driver" url= "jdbc:mysql://localhost:3306/corejava"maxIdle="30" maxWait= "5000" maxActive= "100" />--><!--下面注释部分为使用jndi+c3p0--><Resource name="TestJNDI"type="com.mchange.v2.c3p0.ComboPooledDataSource"maxPoolSize="50" minPoolSize="2" acquireIncrement="2"factory="org.apache.naming.factory.BeanFactory"user="root" password="12345678"drivername="code"><resource-ref><description>DBConnection</description><res-ref-name>TestJNDI</res-ref-name><res-type>javax.sql.DataSource</res-type><res-auth>Container</res-auth></resource-ref>?
private static Connection conn = null; static Context context = null; static DataSource dataSource = null; public static Connection getConnection() throws NamingException, SQLException{ context = new InitialContext(); dataSource = (DataSource) context.lookup("java:/comp/env/TestJNDI"); if(dataSource!=null){ conn = dataSource.getConnection(); } return conn; }?注意:要通过TOMCAT DBCP来实现JNDI,连接数据库,需要将驱动JAR包拷贝到 apache-tomcat/lib/ 目录下