J2EE进阶之JDBC数据库连接池 十九

来源:互联网 发布:php连不上mysql数据库 编辑:程序博客网 时间:2024/06/05 16:31

JDBC之数据库连接池

使用数据库连接池优化程序性能

不用连接池缺点:

使用连接池的话:

编写一个简单的连接池

连接池实现原理:

  1 public class SimpleConnectionPool {  2     //池:存链接  3     private static List<Connection> pool = new ArrayList<Connection>();  4     static{  5         try {  6             //初始化10个链接  7             for(int i=0;i<10;i++){  8                 Connection conn = JdbcUtil.getConnection();  9                 pool.add(conn); 10             } 11         } catch (Exception e) { 12             throw new ExceptionInInitializerError("连接池初始化失败"); 13         } 14     } 15     //从池中取出一个链接 16     public synchronized static Connection getConnection(){ 17         if(pool.size()>0){ 18             Connection conn = pool.remove(0); 19             return conn; 20         }else{ 21             //继续创建一个链接供用户使用 22             //再向池中放一定数量的链接 23             //让用户等:超时。 24             //通知用户:服务器忙 25             throw new RuntimeException("对不起!服务器真忙,请稍后再试"); 26         } 27     } 28     //用完连接后还回池中 29     public static void release(Connection conn){ 30         pool.add(conn); 31     } 32 }

测试

  1     @Test  2     public void save(){  3         Connection conn = null;  4         Statement stmt = null;  5         try{  6             conn = SimpleConnectionPool.getConnection();//从池中取的数据  7             stmt = conn.createStatement();  8             //...  9         }catch(Exception e){ 10             e.printStackTrace(); 11         }finally{ 12             if(stmt!=null){ 13                 try { 14                     stmt.close(); 15                 } catch (SQLException e) { 16                     e.printStackTrace(); 17                 } 18             } 19             if(conn!=null){ 20                 SimpleConnectionPool.release(conn);//还回池中 21             } 22         } 23     }

编写连接池需实现javax.sql.DataSource接口

DataSource编写

利用动态代理实现

MyDataSource1

  1 public class MyDataSource1 implements DataSource {  2     private static List<Connection> pool = new ArrayList<Connection>();  3     static{  4         try {  5             //初始化10个链接  6             for(int i=0;i<10;i++){  7                 Connection conn = JdbcUtil.getConnection();  8                 pool.add(conn);  9             } 10         } catch (Exception e) { 11             throw new ExceptionInInitializerError("连接池初始化失败"); 12         } 13     } 14     //要从池中获取链接,利用动态代理机制。若调用获取连接对象,并使用其方法,是close方法的话,就使用代理规定的方法。 15     public Connection getConnection() throws SQLException { 16         if(pool.size()>0){ 17             final Connection conn = pool.remove(0);//驱动的 18             //返回驱动的实例的代理对象 19              20             /* 21              * 产生代理对象的实例 22              * public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 23              *  24              * ClassLoader loader:既然代理类也是类,必定要类加载器加载。固定写法:和被代理对象一样 25              * Class<?>[] interfaces:代理对象要实现的接口。固定写发:和被代理对象一样。目的就是拥有相同的行为 26              * InvocationHandler h:策略设计模式。是一个接口,你必须传入一个实现了该接口的对象(具体代理方案)。 27              */      28             Connection proxy = (Connection)Proxy.newProxyInstance(conn.getClass().getClassLoader(),  29                     conn.getClass().getInterfaces(),  30                     new InvocationHandler() { 31                          32                     /** 33                      * //调用代理对象的任何方法,该方法都会被执行 34                      * @param proxy 代理对象本身的引用,一般用不着 35                      * @param method 当前执行的方法 36                      * @param args当前方法用到的参数 37                      * @return 当前方法的返回值 38                      * @throws Throwable 39                      */ 40                         public Object invoke(Object proxy, Method method, Object[] args) 41                                 throws Throwable { 42                             //判断是不是close方法,如果是,把链接放回池中 43                             if("close".equals(method.getName())){ 44                                 return pool.add(conn); 45                             } 46                             return method.invoke(conn, args); 47                         } 48                     }); 49             return proxy; 50         }else{ 51             //继续创建一个链接供用户使用 52             //再向池中放一定数量的链接 53             //让用户等:超时。 54             //通知用户:服务器忙 55             throw new RuntimeException("对不起!服务器真忙,请稍后再试"); 56         } 57     } 58     

开源数据源(数据库连接池) DBCP和C3P0

DBCP

搭建开发环境:
a、拷贝jar包:commons-dbcp.jar commons-pool.jar
b、在classpath中添加DBCP的配置文件:
c、编写工具类:

配置文件;

  1 #连接设置  2 driverClassName=com.mysql.jdbc.Driver  3 url=jdbc:mysql://localhost:3306/wsj  4 username=root  5 password=root  6   7 #<!-- 初始化连接 -->  8 initialSize=10  9  10 #最大连接数量 11 maxActive=50 12  13 #<!-- 最大空闲连接 --> 14 maxIdle=20 15  16 #<!-- 最小空闲连接 --> 17 minIdle=5 18  19 #<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 --> 20 maxWait=60000 21  22  23 #JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;]  24 #注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。 25 connectionProperties=useUnicode=true;characterEncoding=utf8 26  27 #指定由连接池所创建的连接的自动提交(auto-commit)状态。 28 defaultAutoCommit=true 29  30 #driver default 指定由连接池所创建的连接的只读(read-only)状态。 31 #如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix) 32 defaultReadOnly= 33  34 #driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。 35 #可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE 36 defaultTransactionIsolation=READ_COMMITTED

工具类

  1 public class DBCPUtil {  2     private static DataSource dataSource;  3     static{  4         try {  5             Properties props = new Properties();  6             ClassLoader cl = DBCPUtil.class.getClassLoader();  7             InputStream in = cl.getResourceAsStream("dbcpconfig.properties");  8             props.load(in);  9             dataSource = BasicDataSourceFactory.createDataSource(props); 10         } catch (Exception e) { 11             throw new ExceptionInInitializerError(e); 12         } 13     } 14     public static DataSource getDataSource(){ 15         return dataSource; 16     } 17     public static Connection getConnection(){ 18         try { 19             return dataSource.getConnection(); 20         } catch (SQLException e) { 21             throw new RuntimeException(e); 22         } 23     } 24 }

测试

public class DBCPUtilTest {    @Test    public void test1() throws SQLException{        Connection conn = DBCPUtil.getConnection();        System.out.println(conn.getClass().getName());        conn.close();    }}

C3P0

a、拷贝jar包:c3p0-0.9.1.2.jar c3p0-oracle-thin-extras-0.9.1.2.jar(oracle瘦客户端需要)
b、在classpath中添加配置文件:
c、编写工具类:

配置文件

工具类

public class C3P0Util {    private static ComboPooledDataSource dataSource = new ComboPooledDataSource();    public static DataSource getDataSource(){        return dataSource;    }    public static Connection getConnection(){        try {            return dataSource.getConnection();        } catch (SQLException e) {            throw new RuntimeException(e);        }    }}

测试

public class C3P0UtilTest {
@Test
public void test1() throws SQLException{
Connection conn = C3P0Util.getConnection();
System.out.println(conn.getClass().getName());
conn.close();
}
}

利用JNDI获取数据源:更加接近真实

1、拷贝数据库的驱动到Tomcat\lib目录中。 (既mysql-connector-java.jar包)
2、在web应用的META-INF目录下,建立一个context.xml的配置文件

<?xml version="1.0" encoding="UTF-8"?><Context>    <Resource name="jdbc/wsj" auth="Container" type="javax.sql.DataSource"               maxActive="50" maxIdle="30" maxWait="10000"               username="root" password="sorry" driverClassName="com.mysql.jdbc.Driver"               url="jdbc:mysql://localhost:3306/wsj"/></Context>

3.重启Tomcat

测试:

    Context initContext = new InitialContext();    Context envContext  = (Context)initContext.lookup("java:/comp/env");//Tomcat的放的资源的路径    DataSource ds = (DataSource)envContext.lookup("jdbc/wsj");//name    Connection conn = ds.getConnection();    out.write(conn.toString());

JDBC简单框架

数据源信息的获取

  1 //数据库元信息的获取  2 public class MetaDataDemo {  3     //数据库本身的元信息  4     @Test  5     public void test1() throws Exception{  6         Connection conn = DBCPUtil.getConnection();  7         DatabaseMetaData dbmd = conn.getMetaData();  8         System.out.println(dbmd.getDatabaseProductName());  9         System.out.println(dbmd.getDatabaseProductVersion()); 10         conn.close(); 11     } 12     //SQL语句占位符的元信息:计算?的个数 13     @Test 14     public void test2()throws Exception{ 15         Connection conn = DBCPUtil.getConnection(); 16         PreparedStatement stmt = conn.prepareStatement("?????"); 17         ParameterMetaData pmd = stmt.getParameterMetaData(); 18         int paramNum = pmd.getParameterCount();//得到占位符的数量 19         System.out.println(paramNum); 20         conn.close(); 21     } 22     //查询结果集的元数据 23     @Test 24     public void test3()throws Exception{ 25         Connection conn = DBCPUtil.getConnection(); 26         PreparedStatement stmt = conn.prepareStatement("select * from account"); 27         ResultSet rs = stmt.executeQuery(); 28         ResultSetMetaData rsmd = rs.getMetaData(); 29         //有几列? 每列的列名是啥?类型又是啥? 30         int columnCount = rsmd.getColumnCount(); 31         for(int i=0;i<columnCount;i++){ 32             String columnName = rsmd.getColumnName(i+1);//列名。第一列的索引是1 33             int columnType = rsmd.getColumnType(i+1);//列类型。用的是整数常量表示的。 34             System.out.println(columnName+":"+columnType); 35         } 36         conn.close(); 37     } 38 }

工具类编写

封装数据;

BeanHandler

  1 /**  2  * 适合结果集只有一条的情况。  3  * 使用前提:数据库的字段名和目标类的字段名保持一致。约定。  4  * 目标对象到底啥:谁用谁知道  5  */  6 public class BeanHandler implements ResultSetHandler {  7   8     private Class clazz;//目标类型  9     public BeanHandler(Class clazz){ 10         this.clazz = clazz; 11     } 12      13     public Object handle(ResultSet rs) { 14          15         try { 16             Object bean = clazz.newInstance(); 17             //封装数据 18             if(rs.next()){ 19                 ResultSetMetaData rsmd = rs.getMetaData(); 20                 int columnCount = rsmd.getColumnCount();//列数 21                 for(int i=0;i<columnCount;i++){ 22                     String fieldName = rsmd.getColumnName(i+1);//列名。和javabean的字段名一样 23                     Object fieldValue = rs.getObject(i+1); 24                     //给javabean的字段赋值 25                     Field field = clazz.getDeclaredField(fieldName);// private int id; 26                     field.setAccessible(true);//强暴他 27                     field.set(bean, fieldValue);//   private int id=1; 28                 } 29             } 30             return bean; 31         } catch (Exception e) { 32             throw new RuntimeException(e); 33         } 34     } 35  36 }

ResultSetHandler

public interface ResultSetHandler {    /**     * 把结果集中的数据封装到对象中     * @param rs     * @return     */    Object handle(ResultSet rs);}

BeanListHandler 多条

  1 public class BeanListHandler implements ResultSetHandler {  2     private Class clazz;//目标类型  3     public BeanListHandler(Class clazz){  4         this.clazz = clazz;  5     }  6     public Object handle(ResultSet rs) {  7         try {  8             List list = new ArrayList();  9             //封装数据 10             while(rs.next()){ 11                 Object bean = clazz.newInstance(); 12                 ResultSetMetaData rsmd = rs.getMetaData(); 13                 int columnCount = rsmd.getColumnCount();//列数 14                 for(int i=0;i<columnCount;i++){ 15                     String fieldName = rsmd.getColumnName(i+1);//列名。和javabean的字段名一样 16                     Object fieldValue = rs.getObject(i+1); 17                     //给javabean的字段赋值 18                     Field field = clazz.getDeclaredField(fieldName);// private int id; 19                     field.setAccessible(true);//强暴他 20                     field.set(bean, fieldValue);//   private int id=1; 21                 } 22                 list.add(bean); 23             } 24             return list; 25         } catch (Exception e) { 26             throw new RuntimeException(e); 27         } 28     } 29  30 }

增删改查DBAssist

  1 public class DBAssist {  2       3     private DataSource dataSource;  4     public DBAssist(DataSource dataSource){  5         this.dataSource = dataSource;  6     }  7     /**  8      * 查询  9      * @param sql 只能是查询语句 10      * @param params 如果没有参数,传null 11      * @return 封装后的信息 12      */ 13     public Object query(String sql,Object[] params,ResultSetHandler rsh) { 14         Connection conn = null; 15         PreparedStatement stmt = null; 16         ResultSet rs = null; 17         try{ 18             conn = dataSource.getConnection(); 19             stmt = conn.prepareStatement(sql); 20             //参数的一些处理 21             //得到sql中的参数元信息 22             ParameterMetaData pmd = stmt.getParameterMetaData(); 23             int paramCount = pmd.getParameterCount(); 24             if(paramCount>0){ 25                 //有参数 26                 if(params==null) 27                     throw new RuntimeException("有占位符,不能不传递参数"); 28                 if(paramCount!=params.length) 29                     throw new RuntimeException("参数个数与占位符个数不匹配"); 30                 //个数匹配 31                 for(int i=0;i<paramCount;i++){ 32                     stmt.setObject(i+1, params[i]); 33                 } 34             } 35             rs = stmt.executeQuery(); 36             //查出来的数据在rs中。 用户执行的sql语句确定吗?不确定 37             //封装到JavaBean中或List中。封装到什么对象中确定吗?不确定 38              39             //确定:把结果集中的东西封装到JavaBean中。怎么封装?谁用谁知道。把路子铺好(接口),由使用着来走。策略设计模式 40              41             return rsh.handle(rs); 42         }catch(Exception e){ 43             throw new RuntimeException(e); 44         }finally{ 45             release(rs, stmt, conn); 46         } 47     } 48     /** 49      * 完成曾删改操作 50      * @param sql sql语句,参数要使用占位符 51      * @param params 顺序要和占位符的顺序对应.Java基础加强之可变参数 52      */ 53     public void update(String sql,Object[] params) { 54         Connection conn = null; 55         PreparedStatement stmt = null; 56         ResultSet rs = null; 57         try{ 58             conn = dataSource.getConnection(); 59             stmt = conn.prepareStatement(sql); 60             //参数的一些处理 61             //得到sql中的参数元信息 62             ParameterMetaData pmd = stmt.getParameterMetaData(); 63             int paramCount = pmd.getParameterCount(); 64             if(paramCount>0){ 65                 //有参数 66                 if(params==null) 67                     throw new RuntimeException("有占位符,不能不传递参数"); 68                 if(paramCount!=params.length) 69                     throw new RuntimeException("参数个数与占位符个数不匹配"); 70                 //个数匹配 71                 for(int i=0;i<paramCount;i++){ 72                     stmt.setObject(i+1, params[i]); 73                 } 74             } 75             stmt.executeUpdate(); 76         }catch(Exception e){ 77             throw new RuntimeException(e); 78         }finally{ 79             release(rs, stmt, conn); 80         } 81     } 82     private void release(ResultSet rs,Statement stmt,Connection conn){ 83         if(rs!=null){ 84             try { 85                 rs.close(); 86             } catch (SQLException e) { 87                 e.printStackTrace(); 88             } 89             rs = null; 90         } 91         if(stmt!=null){ 92             try { 93                 stmt.close(); 94             } catch (SQLException e) { 95                 e.printStackTrace(); 96             } 97             stmt = null; 98         } 99         if(conn!=null){100             try {101                 conn.close();102             } catch (SQLException e) {103                 e.printStackTrace();104             }105             conn = null;106         }107     }108 }

bean类

  1 public class Account implements Serializable {  2     private int id;  3     private String name;  4     private float money;  5     public int getId() {  6         return id;  7     }  8     public void setId(int id) {  9         this.id = id; 10     } 11     public String getName() { 12         return name; 13     } 14     public void setName(String name) { 15         this.name = name; 16     } 17     public float getMoney() { 18         return money; 19     } 20     public void setMoney(float money) { 21         this.money = money; 22     } 23     @Override 24     public String toString() { 25         return "Account [id=" + id + ", name=" + name + ", money=" + money 26                 + "]"; 27     } 28      29 }

测试:

DBAssistTest

  1 public class DBAssistTest {  2     private DBAssist da = new DBAssist(DBCPUtil.getDataSource());  3     @Test  4     public void testAdd(){  5         da.update("insert into account (id,name,money) values (?,?,?)",   6                 new Object[]{4,"ddd",10000});  7     }  8     @Test  9     public void testUpdate(){ 10         da.update("update account set money=? where id=?",  11                 new Object[]{1000,4}); 12     } 13     @Test 14     public void testDel(){ 15         da.update("delete from account where id=?",  16                 new Object[]{4}); 17     } 18     @Test 19     public void testFindOne(){ 20         Account a = (Account)da.query("select * from account", null, new BeanHandler(Account.class)); 21         System.out.println(a); 22     } 23     @Test 24     public void testFindAll(){ 25         List<Account> list = (List<Account>)da.query("select * from account", null, new BeanListHandler(Account.class)); 26         for(Account a:list) 27             System.out.println(a); 28     } 29 }