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

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 }

猜你喜欢

转载自blog.csdn.net/onceing/article/details/77538342