zoukankan      html  css  js  c++  java
  • JDBC 学习复习6 学习与编写数据库连接池

    之前的工具类DBUtil暴露的问题
    用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、服务器挂掉。

    解决方法-数据库连接池
     数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现的尤为突出.对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响 到程序的性能指标.数据库连接池正式针对这个问题提出来的.数据库连接池负责分配,管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。
    最小连接数:是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费.
    最大连接数:是连接池能申请的最大连接数,如果数据库连接请求超过次数,后面的数据库连接请求将被加入到等待队列中,这会影响以后的数据库操作

    编写数据库连接池
    编写连接池需实现java.sql.DataSource接口
    在DataSource构造函数中批量创建与数据库的连接,并把创建的连接加入LinkedList对象中。
    实现getConnection方法,让getConnection方法每次调用时,从LinkedList中取一个Connection返回给用户。
    当用户使用完Connection,调用Connection.close()方法时,Collection对象应保证将自己返回到LinkedList中,而不要把conn还给数据库。Collection保证将自己返回到 LinkedList中是此处编程的难点。

    首先是 数据库配置文件database.properties

    url=jdbc:mysql://localhost:3306/libweb
    driver=com.mysql.jdbc.Driver
    user=root
    password=
    initSize=5
    

    其次是自己写的数据库连接池类 DBpool

    package dbex.mysql;
    import java.io.InputStream;
    import java.io.PrintWriter;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    import java.util.LinkedList;
    import java.util.Properties;
    
    import javax.sql.DataSource;
    
    import dbex.DBUtil;
    
    /**
     * 
     * @ClassName: DBpool
     * @Description: 自己编写数据库连接池
     * @author penny
     * @date 2017年12月1日 上午10:55:31
     *
     */
    public class DBpool implements DataSource {
    
    	// 使用lindedlist集合存放数据库连接,可供频繁读取
    	private static LinkedList<Connection> pools = new LinkedList<Connection>();
    	static {
    		// 静态代码库中加载数据库配置文件database.xml
    		InputStream in = DBpool.class
    				.getResourceAsStream("../../database.properties");
    		Properties pro = new Properties();
    		try {
    			// 静态代码快加载驱动
    			pro.load(in);
    			Class.forName(pro.getProperty("driver"));
    			int initSize = Integer.parseInt(pro.getProperty("initSize"));
    			for (int i = 0; i < initSize; i++) {
    				Connection conn = DriverManager.getConnection(
    						pro.getProperty("url"), pro.getProperty("user"),
    						pro.getProperty("password"));
    				System.out.println(DBpool.class.toString() + ":数据库连接池初始化第"
    						+ (i + 1) + "个连接=" + conn);
    				pools.add(conn);
    			}
    		} catch (Exception e) {
    			// TODO: handle exception
    		}
    	}
    
    	/**
    	 * 获取数据库连接池的连接
    	 */
    	@Override
    	public Connection getConnection() throws SQLException {
    		// 如果数据库连接池有空余连接
    		if (pools.size() > 0) {
    			final Connection conn = pools.removeFirst();// 从连接池取出一个连接对象
    			System.out.println(DBpool.class.toString() + ":数据库连接池可用连接数="
    					+ pools.size());
    			/**
    			 * 使用动态代理技术构建连接池中的connection
    			 */
    			return (Connection) Proxy.newProxyInstance(DBpool.class.getClassLoader(), conn.getClass().getInterfaces(),
    					new InvocationHandler() {
    						@Override
    						public Object invoke(Object proxy, Method method,Object[] obj) throws Throwable {
    							if (!method.getName().equals("close")) {
    								return method.invoke(conn, obj);
    							} else {
    								pools.add(conn);// 如果调用close()方法就需要把连接回收到连接池
    								System.out.println(DBpool.class.toString()
    										+ ":数据库连接池回收一个连接=" + conn
    										+ ",连接池可用连接数=" + pools.size());
    								return null;
    							}
    						}
    					});
    
    		} else {
    			throw new RuntimeException("对不起,数据库忙,没有可用连接!");
    		}
    
    	}
    
    	@Override
    	public Connection getConnection(String arg0, String arg1)
    			throws SQLException {
    		return null;
    	}
    	@Override
    	public PrintWriter getLogWriter() throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public int getLoginTimeout() throws SQLException {
    		// TODO Auto-generated method stub
    		return 0;
    	}
    
    	@Override
    	public void setLogWriter(PrintWriter arg0) throws SQLException {
    		// TODO Auto-generated method stub
    	}
    	@Override
    	public void setLoginTimeout(int arg0) throws SQLException {
    		// TODO Auto-generated method stub
    	}
    	@Override
    	public boolean isWrapperFor(Class<?> arg0) throws SQLException {
    		// TODO Auto-generated method stub
    		return false;
    	}
    	@Override
    	public <T> T unwrap(Class<T> arg0) throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    }
    
    

    最后是基于数据库连接池的数据库连接工具类

    package dbex;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    import dbex.mysql.DBpool;
    
    /**
     * 
     * @ClassName: DBUtil2 
     * @Description: 使用了数据库连接池的数据库工具类
     * @author penny
     * @date 2017年12月1日 下午10:30:41 
     *
     */
    public class DBUtil2 {
    	
    	private static DBpool pools = new DBpool();
    	/**
    	 * 
    	 * @Title: getConnection 
    	 * @Description: 获取一个数据库连接池的连接对象conn
    	 * @param @return Connection() conn
    	 * @param @throws SQLException    
    	 * @throws SQLException
    	 */
    	public static Connection getConnection() throws SQLException{
    		return pools.getConnection();
    	}
    	/***
    	 * 
    	 * @Title: release 
    	 * @Description: 关闭并释放资源
    	 * @param @param conn 连接对象
    	 * @param @param ppst 预处理语句
    	 * @param @param rs   返回结果集
    	 * @throws
    	 */
    	public void release(Connection conn,PreparedStatement ppst,ResultSet rs){
    		if(conn!=null){
    			try {
    				conn.close();
    			} catch (SQLException e) {
    				throw new RuntimeException("关闭数据库 Connection异常");
    			}
    		}
    		if(ppst!=null){
    			try {
    				conn.close();
    			} catch (SQLException e) {
    				throw new RuntimeException("关闭数据库 PreparedStatement异常");
    			}
    		}
    		if(rs!=null){
    			try {
    				conn.close();
    			} catch (SQLException e) {
    				throw new RuntimeException("关闭数据库 ResultSet异常");
    			}
    		}
    	}
    	public static void main(String[] args) {
    		System.out.println("<====================测试使用数据库连接池的工具类===================>");
    		DBUtil2 db2 = new DBUtil2();
    		
    		try {
    			System.out.println(db2.getConnection());
    			System.out.println(db2.getConnection());
    			System.out.println(db2.getConnection());
    			System.out.println(db2.getConnection());
    			Connection conn=db2.getConnection();
    			System.out.println(conn);
    			db2.release(conn, null, null);
    			System.out.println(db2.getConnection());
    //			db2.getConnection();
    		} catch (SQLException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    	}
    }
    
    

    测试效果

    总结
    首先感谢孤傲沧浪大哥总结和分享,在这里学习了
    1 基础知识反射,以及动态代理模式的熟练使用
    2 研究下其他开源的数据库连接池工具DBCP C3P0 或者代码

  • 相关阅读:
    Java内存模型
    mysql通过“延迟关联”进行limit分页查询优化的一个实例
    RabbitMQ(二):相应机制、持久化、负载均衡分发
    RabbitMQ(一):安装配置、用户权限
    S7.Net与西门子PLC通讯——纯新手必看
    [.net core] 创建和发布NuGet包 (dotnet CLI)
    [收藏]2018年10月最新全网可用接码打码平台 [若快已挂]
    如何写出无法维护的代码
    C# ——Parallel类
    .net语音播放,自定义播报文字
  • 原文地址:https://www.cnblogs.com/humi/p/7940905.html
Copyright © 2011-2022 走看看