Struts框架原型技术和JDBC高级开发之连接池(dbcp,c3p0)

在上篇文章写的Servlet中,在servlet中读取cmd命令,然后进行if判断是sava还是query等方法,如果cmd命令较少,if判断还是可以,但是如果较多,则影响了整体的效率。

 

这里模仿Struts框架的原型技术进行改进

 

 模仿Struts框架的原型技术

 

BaseServlet.java

package cn.hncu.utils;

import java.io.IOException;
import java.lang.reflect.Method;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/*
 * 模仿Struts原型技术,里面要有一个抽象execute方法,由继承它的子类去实现
 */
public abstract class BaseServlet extends HttpServlet{

	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doPost(request, response);
	}

	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		request.setCharacterEncoding("utf-8");
		String cmd=request.getParameter("cmd");
		if(cmd==null||cmd.trim().equals("")){
			cmd="execute";
		}
		//调用子类名为cmd参数的方法------利用类反射动态调用
		try {
			Method m=this.getClass().getMethod(cmd, HttpServletRequest.class,HttpServletResponse.class);
			m.invoke(this,request, response);
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage(), e);//不想在外面抓异常,这里就抓到异常以后,抛RuntimeException
		}
	}
	
	public abstract void execute(HttpServletRequest request,HttpServletResponse responses);
	
}

 

StudServlet.java

package cn.hncu.stud.servlet;

import java.io.IOException;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cn.hncu.domain.Book;
import cn.hncu.domain.Stud;
import cn.hncu.stud.service.IStudService;
import cn.hncu.stud.service.IStudServiceImpl;
import cn.hncu.utils.BaseServlet;
import cn.hncu.utils.TxProxy;

public class StudServlet extends BaseServlet {
//	IStudService service=new IStudServiceImpl();//这是直接在service层中开启事务,也是version 1
//	IStudService service=TxProxy.getProxy(IStudServiceImpl.class);//version 2
	IStudService service=TxProxy.getProxy(new IStudServiceImpl());//version 3
	public void query(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		List<Map<String,String>> stud=service.query();
		request.setAttribute("stud", stud);
		request.getRequestDispatcher("/jsps/show.jsp").forward(request, response);
	}
	public void save(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		//1.收集   2.组织
		String name=request.getParameter("name");
		//图书信息
		String[] bookNames=request.getParameterValues("bookName");
		String prices[]=request.getParameterValues("price");
		
		Stud stud=new Stud();
		stud.setName(name);
		if(bookNames==null||bookNames.length==0){//防护一下,防止为空,下面的price也应该做防护的,我省略了
			return;
		}
		for(int i=0;i<bookNames.length;i++){
			Book book=new Book();
			book.setName(bookNames[i]);
			book.setPrice(Double.parseDouble(prices[i]));

			//在这里完成两个值对象的“一对多”关系的数据封装
			stud.getBook().add(book);
			book.setStud(stud);
		}
		
		//3 调用service层
		try {
			service.save(stud);//把stud放进去就可以
			System.out.println("SUCCESSFULL");
		} catch (Exception e) {
			//导向失败页面
		}
		
	}
	public void abc(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
			service.abc();
	}
	@Override
	public void execute(HttpServletRequest request,
			HttpServletResponse responses) {
		System.out.println("这里实现execute方法......");
	}
}


总结:

BaseServlet继承HttpServlet覆盖doGet,doPost方法,StudServlet继承BaseServlet,在StudServlet中只写query,save等方法,不再写doGet,dePost()方法,

由BaseServlet用类反射调用子类中的方法。

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

应采用这种方式:

 

编写连接池需实现java.sql.DataSource接口。DataSource接口中定义了两个重载的getConnection方法:
        •Connection getConnection()
        •Connection getConnection(String username, String password)
 
实现DataSource接口,并实现连接池功能的步骤:
       •在DataSource构造函数中批量创建与数据库的连接,并把创建的连接加入LinkedList对象中。
       •实现getConnection方法,让getConnection方法每次调用时,从LinkedList中取一个Connection返回给用户。
       •当用户使用完Connection,调用Connection.close()方法时,Collection对象应保证将自己返回到LinkedList中,而不要把conn还给数据库。
       •Collection保证将自己返回到LinkedList中是此处编程的难点。
 
 
使用动态代理技术构建连接池中的connection

 

proxyConn = (Connection) Proxy.newProxyInstance(
      this.getClass().getClassLoader(), 
      conn.getClass().getInterfaces(),
      new InvocationHandler() {
		//此处为内部类,当close方法被调用时将conn还回池中,其它方法直接执行
            public Object invoke(Object proxy, Method method,
                                                              Object[] args) throws Throwable {
                     if (method.getName().equals("close")) {
                              pool.addLast(conn);
                              return null;
                     }
                     return method.invoke(conn, args);
             }
	});

 

由于自己写的连接池我在上一篇文章中已经贴过了,所以这里不再重复。


开源数据库连接池

现在很多WEB服务器(Weblogic, WebSphere, Tomcat)都提供了DataSoruce的实现,即连接池的实现。通常我们把DataSource的实现,按其英文含义称之为数据源,数据源中都包含了数据库连接池的实现。
也有一些开源组织提供了数据源的独立实现:
      •DBCP 数据库连接池
      •C3P0 数据库连接池
实际应用时不需要编写连接数据库代码,直接从数据源获得数据库的连接。程序员编程时也应尽量使用这些数据源的实现,以提升程序的数据库访问性能。
 
 
Dbcp
 
DbcpPoolDemo.java
package cn.hncu.dbcp;

import java.sql.Connection;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import org.junit.Test;


public class DbcpPoolDemo {
	//dbcp连接池----BasicDataSource类对象
	@Test
	public void demo1() throws Exception{
		//纯Java方式----利用空参构造new BasicDataSource(),然后手动set那些参数
		BasicDataSource pool=new BasicDataSource();
		pool.setUsername("hncu");
		pool.setPassword("1234");
		pool.setDriverClassName("com.mysql.jdbc.Driver");
		pool.setUrl("jdbc:mysql://127.0.0.1:3306/hncu?useUnicode=true&characterEncoding=utf-8");
//		Connection con=pool.getConnection();
//		System.out.println(con.hashCode());
		int size=pool.getInitialSize();
		System.out.println("初始化连接的个数:"+size);//个数为  0
		System.out.println("初始化最多能有多少在用的连接:"+pool.getMaxActive());
		System.out.println("最多等待多长时间(超过这个时间就抛出异常):"+pool.getMaxWait());//  -1为无限制等待
		System.out.println("最多空闲多长时间(没有使用就收回连接):"+pool.getMaxIdle());
		//有get就有set方法
		pool.setMaxActive(12);//更改池的参数
		System.out.println("修改后最多能有多少在用的连接:"+pool.getMaxActive());
		//从池中获取多个连接
		 for(int i=0;i<20;i++){
			Connection con2=pool.getConnection();
			System.out.println("第"+i+"个连接: "+con2.hashCode());
			if(i%2==0){
				con2.close();
			}
		}System.out.println("OVER!");
	}
	@Test
	public void test2() throws Exception{
		//使用配置文件的方式(参数名字可参照set方式里面的变量名)----利用工厂类加Properties,创建DataSource
		Properties p=new Properties();
		/*
		 * 根据配置文件的位置,两种加载方式
		 * 1.配置文件放在classpath下(即src下面)
			p.load(DbcpPoolDemo.class.getClassLoader().getResourceAsStream("dbcp.properties"));
		 * 2.配置文件和当前类放在一起 
			p.load(DbcpPoolDemo.class.getResourceAsStream("dbcp.properties"));
		 */
//		p.load(DbcpPoolDemo.class.getClassLoader().getResourceAsStream("dbcp.properties"));
		p.load(DbcpPoolDemo.class.getResourceAsStream("dbcp.properties"));
		DataSource pool=BasicDataSourceFactory.createDataSource(p);
//		Connection con=pool.getConnection();
//		System.out.println(con.hashCode());
		
		//从0开始,到第12个连接开始阻塞
		for(int i=0;i<30;i++){
			System.out.println("第"+i+"个连接"+pool.getConnection().hashCode());
		}
	}
}

DbcpPoolDemo用到的dbcp.properties配置文件
username=hncu
password=1234
driverClassName=com.mysql.jdbc.Driver
url=jdbc\:mysql\://127.0.0.1\:3306/hncu?useUnicode\=true&characterEncoding\=utf-8
maxActive=13

 

效果图:当连接不够时,就是阻塞状态

 

当for循环中i为偶数的时候把连接还回去,便出现了如下结果:连接全是成对出现,可知,dbcp池中的连接是重复使用的,当你还回去一个连接,那么这个连接下次就可以使用。


 
DbcpUtils.java
package cn.hncu.dbcp;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSourceFactory;

public class DbcpUtils {
	//本地线程管理对象,用于实现:同一线程拿到同一个Connection对象
	private static ThreadLocal<Connection> t=new ThreadLocal<Connection>();
	
	private static DataSource pool=null;
	static{
		Properties p = new Properties();
			try {
				p.load(DbcpUtils.class.getResourceAsStream("dbcp.properties"));
				pool=BasicDataSourceFactory.createDataSource(p);
			} catch (Exception e) {
				throw new RuntimeException(e.getMessage(), e);
			}
	}	
	public static synchronized Connection getConn() {
		//先从t中拿,若有就直接拿出去,没有则从pool池中拿且放进t中
		Connection con=t.get();
		if(con==null){
			try {
				con=pool.getConnection();
			} catch (SQLException e) {
				throw new RuntimeException(e.getMessage(), e);
			}
			t.set(con);//从pool池中拿出来,放进ThreadLocal池中
			return getConn();
		}
		System.out.println("拿到连接:"+con);
		return con;
	}
}

 

 


测试dbcp连接池的TestDbcp.java
package cn.hncu.test;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

import org.junit.Test;

import cn.hncu.dbcp.DbcpUtils;

public class TestDbcp {
	@Test 
	public void test() throws Exception{
		Connection con = DbcpUtils.getConn();
		try {
			con.setAutoCommit(false);
			System.out.println("开启一个事务....");
			save1();
			save2();
			con.commit();
			System.out.println("提交一个事务....");
		} catch (Exception e) {
			con.rollback();
			System.out.println("回滚一个事务....");
		}finally{
			con.setAutoCommit(true);
			con.close();
		}
		
	}
	
	public void save1() throws Exception{
		Connection con = DbcpUtils.getConn();
		Statement st  =con.createStatement();
		st.executeUpdate( "insert into stud values('A001','ABB1')" );
		st.executeUpdate( "insert into stud values('A002','ABB2')" );
	}
	
	public void save2() throws Exception{
		Connection con = DbcpUtils.getConn();
		Statement st  =con.createStatement();
		st.executeUpdate( "insert into stud values('C001','CCC1')" );
		st.executeUpdate( "insert into stud values('C002','CCC2')" );
	}
}

成功插入数据库:
 
 
C3p0
 
C3p0PoolDemo.java
package cn.hncu.c3p0;

import java.sql.Connection;
import java.sql.SQLException;

import org.junit.Test;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class C3p0PoolDemo {
	@Test
	public void demo1() throws Exception{
		//法1  纯Java方式
		ComboPooledDataSource pool=new ComboPooledDataSource();
		
		pool.setUser("hncu");
		pool.setPassword("1234");
		pool.setDriverClass("com.mysql.jdbc.Driver");
		pool.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/hncu?useUnicode=true&characterEncoding=utf-8");
//		Connection con=pool.getConnection();
//		System.out.println(con);
		for(int i=0;i<20;i++){
			Connection con=pool.getConnection();
			System.out.println("第"+i+"个连接:"+con.hashCode());
			if(i%2==0){
				con.close();
			}
		}
		System.out.println("OVER!");
	}
	/*
	 * 法2:使用配置文件:
	 * 注意:这里的配置文件名和存放位置都是固定的(c3p0-config.xml , 放在classpath下)
	 */
	@Test
	public void demo2() throws Exception{
		ComboPooledDataSource pool=new ComboPooledDataSource("hncu");//这里如果给空参就是默认配置
		for(int i=0;i<20;i++){
			Connection con=pool.getConnection();
			System.out.println("第"+i+"个连接:"+con.hashCode());
		}
		System.out.println("OVER!");
	}
}


C3p0PoolDemo用的的c3p0-config.xml
 
<c3p0-config>
	<!-- 默认配置,如果没有指定则使用这个配置 -->
	<default-config>
		<property name="driverClass">com.mysql.jdbc.Driver</property>
		<property name="jdbcUrl">
			<![CDATA[jdbc:mysql://127.0.0.1:3306/hncu?useUnicode=true&characterEncoding=UTF-8]]>
		</property>
		<property name="user">hncu</property>
		<property name="password">1234</property>
		<!-- 初始化池大小 -->
		<property name="initialPoolSize">2</property>
		<!-- 最大空闲时间 -->
		<property name="maxIdleTime">30</property>
		<!-- 最多有多少个连接 -->
		<property name="maxPoolSize">10</property>
		<!-- 最少几个连接 -->
		<property name="minPoolSize">2</property>
		<!-- 每次最多可以执行多少个批处理语句 -->
		<property name="maxStatements">50</property>
	</default-config> 
	<!-- 命名的配置 -->
	<named-config name="hncu">
		<property name="driverClass">com.mysql.jdbc.Driver</property>
		<property name="jdbcUrl">
			<![CDATA[jdbc:mysql://127.0.0.1:3306/mydb?useUnicode=true&characterEncoding=UTF-8]]>
		</property>
		<property name="user">hncu</property>
		<property name="password">1234</property>
		<property name="acquireIncrement">2</property><!-- 如果池中数据连接不够时一次增长多少个 -->
		<property name="initialPoolSize">3</property>
		<property name="minPoolSize">3</property>
		<property name="maxPoolSize">10</property>
		<property name="maxStatements">0</property>
		<property name="maxStatementsPerConnection">5</property> <!-- he's important, but there's only one of him -->
	</named-config>
</c3p0-config> 

C3p0的默认配置:在配置xml的时候里面的参数可以参考下图中红字的最后一行,C3p0在拿连接的时候便会输出这一行,里面显示了所有的配置参数。
 
 
 
在xml配置文件中修改了连接数:
 
 

 

当for循环中i为偶数的时候把连接还回去,便出现了如下结果:连接全部都不一样,可知,C3p0池中的连接是不是重复使用的,当你还回去一个连接,这个连接也不再会使用。

 

 

C3p0Utils.java

package cn.hncu.c3p0;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class C3p0Utils {
	private static ThreadLocal<Connection> t=new ThreadLocal<Connection>();
	private static DataSource pool=null;
	static{
			try {
				pool=new ComboPooledDataSource();
			} catch (Exception e) {
				throw new RuntimeException(e.getMessage(), e);
			}
	}	
	public static synchronized Connection getConn() {
		Connection con=t.get();
		if(con==null){
			try {
				con=pool.getConnection();
			} catch (SQLException e) {
				throw new RuntimeException(e.getMessage(), e);
			}
			t.set(con);//从pool池中拿出来,放进ThreadLocal池中
			return getConn();
		}
		System.out.println("拿到连接:"+con);
		return con;
	}
}

 

C3p0的测试类与Dbcp的大致一样,这里不再重复贴,贴上结果:也可以成功操纵数据库。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值