`
阿尔萨斯
  • 浏览: 4184228 次
社区版块
存档分类
最新评论

【web】赵雅智_jdbc开发

 
阅读更多

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

应用程序直接获取链接的缺点

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


【案例】传统方法连接数据库


  1. 配置文件db.properties
    driver=com.mysql.jdbc.Driver
    url=jdbc:mysql://localhost:3306/java
    username=root
    password=123456

  2. 加载数据库驱动
  3. 连接数据库java代码 DBConn.java
    package com.hbsi.util;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.Properties;
    
    public class DBConn {
    
    	static String driver;
    	static String url;
    	static String username;
    	static String password;
    	static{
    		InputStream in = DBConn.class.getClassLoader().getResourceAsStream("db.properties");
    		Properties pro = new Properties();
    		try {
    			pro.load(in);
    		} catch (IOException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		driver = pro.getProperty("driver");
    		url = pro.getProperty("url");
    		username = pro.getProperty("username");
    		password = pro.getProperty("password");
    	}
    	public static Connection getConnection(){
    		Connection conn = null;
    		try {
    			Class.forName(driver);
    			conn = DriverManager.getConnection(url,username,password); 
    		} catch (ClassNotFoundException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}catch (SQLException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		
    		return conn;
    		
    	}
    	
    	public static void close(ResultSet rs, PreparedStatement ps, Connection conn){
    		if(rs != null){
    			try {
    				rs.close();
    			} catch (SQLException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    		
    		if(ps != null){
    			try {
    				ps.close();
    			} catch (SQLException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    		if(conn != null){
    			try {
    				conn.close();
    			} catch (SQLException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    	}
    
    }
    

  4. 测试 DBConnTest.java
    package com.hbsi.test;
    
    import org.junit.Test;
    
    import com.hbsi.util.DBConn;
    
    
    import junit.framework.TestCase;
    
    public class DBConnTest extends TestCase {
    
    	@Test
    	public void testGetConn(){
    		System.out.print(DBConn.getConnection());
    	}
    }
    

  5. java应用类
    package com.hbsi.demo;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    
    import com.hbsi.util.DBConn;
    
    public class ChuanTong {
    
    	public static void main(String[] args) {
    		Connection conn = null;
    		PreparedStatement ps =null;
    		ResultSet rs = null;
    		try{
    		conn = DBConn.getConnection();			
    		//.....
    		   System.out.println(conn);
    		//.....
    		}catch(Exception e){
    			e.printStackTrace();
    		}finally{
    			DBConn.close(rs, ps, conn);
    		}
    		
    
    	}
    }
    

使用连接池:




编写数据库连接池

  • 编写连接池需实现javax.sql.DataSource接口。DataSource接口中定义了两个重载的getConnection方法:
    1. Connection getConnection()
    2. Connection getConnection(String username, String password)
  • 实现DataSource接口,并实现连接池功能的步骤:
    1. 在DataSource构造函数中批量创建与数据库的连接,并把创建的连接加入LinkedList对象中。
    2. 实现getConnection方法,让getConnection方法每次调用时,从LinkedList中取一个Connection返回给用户。
    3. 当用户使用完Connection,调用Connection.close()方法时,Collection对象应保证将自己返回到LinkedList中,而不要把conn还给数据库。
    4. Collection保证将自己返回到LinkedList中是此处编程的难点。
使用动态代理技术构建连接池中的connection--自己编写
动态代理
  • 明确两个概念:
    1. 代理对象存在的价值:主要用于拦截对真实业务对象的访问。
    2. 代理对象有什么方法
  • Java提供了一个Proxy类,调用它的newInstance方法可以生成某个对象的代理对象,使用该方法生成代理对象时,需要三个参数:
    1. 生成代理对象使用哪个类装载器
    2. 生成哪个对象的代理对象,通过接口指定
    3. 生成的代理对象的方法里干什么事,由开发人员编写handler接口的实现来指定。
  • 初学者必须理解,或不理解必须记住的2件事情:
    1. Proxy类负责创建代理对象时,如果指定了handler(处理器),那么不管用户调用代理对象的什么方法,该方法都是调用处理器的invoke方法。
    2. 由于invoke方法被调用需要三个参数:代理对象、方法、方法的参数,因此不管代理对象哪个方法调用处理器的invoke方法,都必须把自己所在的对象、自己(调用invoke方法的方法)、方法的参数传递进来。

建立了一个代理类,传入代理对象、方法、方法的参数

如果想访问真是对象的方法只能通过invoke访问


【案例】自编写连接池

  1. java类
    package com.hbsi.util;
    
    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;
    
    public class MyJdbcPool implements DataSource {
    
    	// 创建的连接放在linklist里
    	private static LinkedList<Connection> list = new LinkedList<Connection>();
    	private static String driver;
    	private static String url;
    	private static String username;
    	private static String password;
    	// 通过静态代码块创建连接
    	static {
    		try {
    			// 类加载器
    			InputStream in = MyJdbcPool.class.getClassLoader()
    					.getResourceAsStream("db.properties");
    			//属性对象
    			Properties prop = new Properties();
    			prop.load(in);
    
    			driver = prop.getProperty("driver");
    			url = prop.getProperty("url");
    			username = prop.getProperty("username");
    			password = prop.getProperty("password");
    
    			//连接数据库,加载驱动
    			Class.forName(driver);
    
    			//创建一批连接
    			for (int i = 0; i < 10; i++) {
    				Connection conn = DriverManager.getConnection(url, username,
    						password);
    				System.out.println("向池中加入了:" + conn);
    				list.add(conn);
    			}
    
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    
    	}
    
    	public Connection getConnection() throws SQLException {
    		if (list.size() > 0) {
    			//不用get方法,get方法会出现重复利用
    			final Connection conn = list.removeFirst();
    			System.out.println("用户从池中取走了:" + conn);
    			System.out.println("池的大小为:" + list.size());
    
    			//动态代理,那个类,哪个对象,干什么事
    			return (Connection) Proxy.newProxyInstance(MyJdbcPool.class
    					.getClassLoader(), new Class[] { Connection.class },
    					new InvocationHandler() {
    
    				//做什么事情,放回连接池里
    						public Object invoke(Object proxy, Method method,
    								Object[] args) throws Throwable {
    							if (!method.getName().equalsIgnoreCase("close")) {
    								return method.invoke(conn, args);
    							}
    							System.out.println(conn + "被还了");
    							list.add(conn);
    							System.out.println("池的大小为:" + list.size());
    							return null;
    						}
    					});
    
    		} else {
    			throw new RuntimeException("对不起,请稍等!");
    
    		}
    	}
    
    	public Connection getConnection(String username, String password)
    			throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	public PrintWriter getLogWriter() throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	public int getLoginTimeout() throws SQLException {
    		// TODO Auto-generated method stub
    		return 0;
    	}
    
    	public void setLogWriter(PrintWriter out) throws SQLException {
    		// TODO Auto-generated method stub
    
    	}
    
    	public void setLoginTimeout(int seconds) throws SQLException {
    		// TODO Auto-generated method stub
    
    	}
    
    	public boolean isWrapperFor(Class<?> iface) throws SQLException {
    		// TODO Auto-generated method stub
    		return false;
    	}
    
    	public <T> T unwrap(Class<T> iface) throws SQLException {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    }
    

    
    
    
    
    
    
    
    
    
    
    
    
  2. 测试java类
    package com.hbsi.demo;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    
    import com.hbsi.util.DBConn;
    import com.hbsi.util.MyJdbcPool;
    
    public class MyJdbcPoolDemo {
    	public static void main(String[] args) {
    		Connection conn = null;
    		PreparedStatement ps =null;
    		ResultSet rs = null;
    		try{
    			MyJdbcPool pool = new MyJdbcPool();
    			conn =pool.getConnection();			
    		//.....
    		   System.out.println(conn);
    		//.....
    		}catch(Exception e){
    			e.printStackTrace();
    		}finally{
    			DBConn.close(rs, ps, conn);
    		}
    	}
    
    }
    


开源数据库连接池

  • 现在很多WEB服务器(Weblogic, WebSphere, Tomcat)都提供了DataSource的实现,即连接池的实现。通常我们把DataSource的实现,按其英文含义称之为数据源,数据源中都包含了数据库连接池的实现。
  • 也有一些开源组织提供了数据源的独立实现:
    • DBCP 数据库连接池
    • C3P0 数据库连接池
  • 实际应用时不需要编写连接数据库代码,直接从数据源获得数据库的连接。程序员编程时也应尽量使用这些数据源的实现,以提升程序的数据库访问性能。
DBCP数据源
  • DBCP 是 Apache 软件基金组织下的开源连接池实现,使用DBCP数据源,应用程序应在系统中增加如下两个 jar 文件:
    1. Commons-dbcp.jar:连接池的实现
    2. Commons-pool.jar:连接池实现的依赖库
  • Tomcat 的连接池正是采用该连接池来实现的。该数据库连接池既可以与应用服务器整合使用,也可由应用程序独立使用。
【案例】dbcp连接数据库

  1. 考jar包
  2. 写配置文件
    #Á¬½ÓÉèÖÃ
    driverClassName=com.mysql.jdbc.Driver
    url=jdbc\:mysql\://localhost\:3306/java
    username=root
    password=123456
    
    #<!-- ³õʼ»¯Á¬½Ó -->
    initialSize=10
    
    #×î´óÁ¬½ÓÊýÁ¿
    maxActive=50
    
    #<!-- ×î´ó¿ÕÏÐÁ¬½Ó -->
    maxIdle=20
    
    #<!-- ×îС¿ÕÏÐÁ¬½Ó -->
    minIdle=5
    
    #<!-- ³¬Ê±µÈ´ýʱ¼äÒÔºÁÃëΪµ¥Î» 6000ºÁÃë/1000µÈÓÚ60Ãë -->
    maxWait=60000
    
    
    #JDBCÇý¶¯½¨Á¢Á¬½Óʱ¸½´øµÄÁ¬½ÓÊôÐÔÊôÐԵĸñʽ±ØÐëΪÕâÑù£º[ÊôÐÔÃû=property;] 
    #×¢Ò⣺"user" Óë "password" Á½¸öÊôÐԻᱻÃ÷È·µØ´«µÝ£¬Òò´ËÕâÀï²»ÐèÒª°üº¬ËûÃÇ¡£
    connectionProperties=useUnicode=true;characterEncoding=gbk
    
    #Ö¸¶¨ÓÉÁ¬½Ó³ØËù´´½¨µÄÁ¬½ÓµÄ×Ô¶¯Ìá½»£¨auto-commit£©×´Ì¬¡£
    defaultAutoCommit=true
    
    #driver default Ö¸¶¨ÓÉÁ¬½Ó³ØËù´´½¨µÄÁ¬½ÓµÄÖ»¶Á£¨read-only£©×´Ì¬¡£
    #Èç¹ûûÓÐÉèÖøÃÖµ£¬Ôò¡°setReadOnly¡±·½·¨½«²»±»µ÷Óᣣ¨Ä³Ð©Çý¶¯²¢²»Ö§³ÖÖ»¶Áģʽ£¬È磺Informix£©
    defaultReadOnly=
    
    #driver default Ö¸¶¨ÓÉÁ¬½Ó³ØËù´´½¨µÄÁ¬½ÓµÄÊÂÎñ¼¶±ð£¨TransactionIsolation£©¡£
    #¿ÉÓÃֵΪÏÂÁÐÖ®Ò»£º£¨ÏêÇé¿É¼ûjavadoc¡££©NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
    defaultTransactionIsolation=READ_UNCOMMITTED
    修改乱码:
    1. #连接设置
      driverClassName=com.mysql.jdbc.Driver
      url=jdbc\:mysql\://localhost\:3306/java
      username=root
      password=123456
      
      #<!-- 初始化连接 -->
      initialSize=10
      
      #最大连接数量
      maxActive=50
      
      #<!-- 最大空闲连接 -->
      maxIdle=20
      
      #<!-- 最小空闲连接 -->
      minIdle=5
      
      #<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
      maxWait=60000
      
      
      #JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;] 
      #注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。
      connectionProperties=useUnicode=true;characterEncoding=gbk
      
      #指定由连接池所创建的连接的自动提交(auto-commit)状态。
      defaultAutoCommit=true
      
      #driver default 指定由连接池所创建的连接的只读(read-only)状态。
      #如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
      defaultReadOnly=
      
      #driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
      #可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
      defaultTransactionIsolation=READ_UNCOMMITTED
  3. 编写java工具类 DBManger_dbcp.java
    package com.hbsi.util;
    
    import java.io.InputStream;
    import java.sql.Connection;
    import java.sql.SQLException;
    import java.util.Properties;
    
    import javax.sql.DataSource;
    
    import org.apache.commons.dbcp.BasicDataSourceFactory;
    
    public class DBManger_dbcp {
    	private static DataSource ds ;
    	
    	static{
    		try{
    			InputStream in = DBManger_dbcp.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
    			Properties prop = new Properties();
    			prop.load(in);
    			
    			ds = BasicDataSourceFactory.createDataSource(prop);
    			
    			
    		}catch(Exception e){
    			e.printStackTrace();
    		}
    	}
    	
         public static Connection getConnction() throws SQLException{
        	
    		return ds.getConnection();
    		
         }
    
    }
    

  4. 提供一个方法,从池里去连接DBManger_dbcpDemo.java
    package com.hbsi.demo;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    
    import com.hbsi.util.DBConn;
    import com.hbsi.util.DBManger_dbcp;
    
    public class DBManger_dbcpDemo {
    
    	public static void main(String[] args) {
    		Connection conn = null;
    		PreparedStatement ps =null;
    		ResultSet rs = null;
    		try{
    			conn = DBManger_dbcp.getConnction();
    		//.....
    		   System.out.println(conn);
    		//.....
    		}catch(Exception e){
    			e.printStackTrace();
    		}finally{
    			DBConn.close(rs, ps, conn);
    		}
    	}
    }
    


C3P0 数据源
【案例】c3p0连接数据库
  • 加jar包
  • 编写工具类DBManager_c3p0.java
    package com.hbsi.util;
    
    import java.sql.Connection;
    import java.sql.SQLException;
    
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    
    public class DBManager_c3p0 {
    	
    	private static ComboPooledDataSource ds = null;
    	
    	static{
    		try{
    			//创建连接池			
    			ds = new ComboPooledDataSource("mysql");
    		}catch(Exception e){
    			e.printStackTrace();
    		}
    	}
    	
    	public static Connection getConnection() throws SQLException{
    		return ds.getConnection();
    	}
    
    }
    

  • 编写连接配置文件c3p0-config.xml
    <?xml version="1.0" encoding="UTF-8"?>
    
    
    <c3p0-config>
    	<default-config>
    		<property name="driverClass">com.mysql.jdbc.Driver</property>
    		<property name="jdbcUrl">jdbc:mysql://localhost:3306/java</property>
    		<property name="user">root</property>
    		<property name="password">123456</property>
    		
    		<property name="acquireIncrement">5</property>
    		<property name="initialPoolSize">10</property>
    		<property name="minPoolSize">5</property>
    		<property name="maxPoolSize">20</property>
    		
    		
    	</default-config>
    
    	<named-config name="mysql">
    		<property name="driverClass">com.mysql.jdbc.Driver</property>
    		<property name="jdbcUrl">jdbc:mysql://localhost:3306/java</property>
    		<property name="user">root</property>
    		<property name="password">123456</property>
    		<property name="acquireIncrement">5</property>
    		<property name="initialPoolSize">10</property>
    		<property name="minPoolSize">5</property>
    		<property name="maxPoolSize">20</property>
    	</named-config>
    
    </c3p0-config>
    

  • 相应实现DBManager_c3p0Demo.java
    package com.hbsi.demo;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    
    import com.hbsi.util.DBConn;
    import com.hbsi.util.DBManager_c3p0;
    
    public class DBManager_c3p0Demo {
    	public static void main(String[] args) {
    		Connection conn = null;
    		PreparedStatement ps =null;
    		ResultSet rs = null;
    		try{
    			conn = DBManager_c3p0.getConnection();			
    		//.....
    		   System.out.println(conn);
    		//.....
    		}catch(Exception e){
    			e.printStackTrace();
    		}finally{
    			DBConn.close(rs, ps, conn);
    		}
    	}
    
    }
    

配置Tomcat数据源

特别提醒:此种配置下,驱动jar文件需放置在tomcat的lib下


JNDI技术简介

  • JNDI(Java Naming and Directory Interface),Java命名和目录接口,它对应于J2SE中的javax.naming包,
  • 这套API的主要作用在于:它可以把Java对象放在一个容器中(JNDI容器),并为容器中的java对象取一个名称,以后程序想获得Java对象,只需通过名称检索即可。
  • 其核心API为Context,它代表JNDI容器,其lookup方法为检索容器中对应名称的对象。
【实例】利用Tomcat实现数据库连接
在META-INF下新建context.xml

context.xml

<Context>
  <Resourse name="jdbc/javaDB"
    auth="Container"
    type="javax.sql.DataSource"
    username="root"
    password="123456"
    driverClassName="com.mysql.jdbc.Driver"
    url="jdbc:mysql://localhost:3306/java"
    maxActive="8"
    maxIdle="4"/>
</Context>

做一个servlet,读取xml文件
package com.hbsi.util;

import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;

public class TomcatPool extends HttpServlet {
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		try {
			//初始化jndi容器
			Context initCxt = new InitialContext();
			//获取容器,检索出web服务器中的jndi容器
			Context envCxt = (Context)initCxt.lookup("java:comp/env");
			//从jndi容器中检索出连接池
			DataSource ds = (DataSource)envCxt.lookup("jdbc/javaDB");
			//获取连接
			Connection conn = ds.getConnection();
			System.out.println(conn);
		} catch (NamingException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doGet(request, response);
	}

}

把jar包考到Tomcat的lib文件夹下

结果:


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics