在上篇文章写的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的大致一样,这里不再重复贴,贴上结果:也可以成功操纵数据库。