BaseServlet
1、希望在一个Servlet中可以有多个请求处理方法
2、客户端发送请求时,必须多给出一个参数,用来说明要调用的方法
请求处理方法的签名必须与service相同,即返回值和参数,以及声明的异常都相同
3、客户端必须传递名为method的参数
domain:User
dao:UserDao
service:UserService
servlet:UserService
vouid init(ServletConfig config)
void destory()
void service(ServletRequest,ServletResponse)
throws IOException,ServletException{
在这里让他去调用其他的方法
要求:用户发出请求时,需要给出一个参数,来说明要调用哪一个方法
//获取参数,通过参数名称来确定要调用的方法
}
http://localhost:8080/xxx/AServlet?m=addUser
Service事务
在Service使用ThreadLocal来完成事务,为学习Spring事务做准备
1、DAO中的事务
比较容易处理,但不应该存在业务,而只是对数据库的基本访问
2、Service才是处理事务的地方
DAO不是处理事务的地方,因为DAO中的每一个方法都是对数据库的一次操作,而Service中的方法才是对应一个业务逻辑,
也就是说我们需要在Service中的一方法中调用DAO的多个方法,而这些方法应该在一个事务中
怎么才能让DAO的多个方法使用相同的Connection呢。方法不能再自己来获得Connection,而是由外界传递进去
在Service中调用DAO的多个方法时,传递相同的Connection就可以了
但是,在Service中不应该出现Connection,应该只在DAO中出现,因为是JDBC的东西,JDBC的东西是用来连接数据库的,
连接数据库是DAO的事,但是事务是Service的事,不能放到DAO中
3、修改JdbcUtils
把对事务的开启和关闭放到JdbcUtils中,在Service中调用JdbcUtils的方法来完成事务的处理,但在Service中就不会再出现Connection了
DAO中的方法不用再让Service来传递Connection了。DAO会主动从JdbcUtils中获取Connection对象,这样JdbcUtils成为DAO和Service的中介了
我们在JdbcUtils中添加beginTransaction()和rollbackTransaction()以及commitTransaction()方法
public class BaseServlet extends HttpServlet {
public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/*
* 1、获取参数,用来识别用户想请求的方法 需要在地址栏加?method=xxx
* 2、然后判断是哪一个方法,是哪一个我们就调用哪一个 重用性差
*/
String methodName = req.getParameter("method");
// if(methodName.equals("addUser")){
// addUser(req,resp);
// } else if(methodName.equals("editUser")){
// editUser(req, resp);
// } else if(methodName.equals("deleteUser")){
// deleteUser(req, resp);
// }
if(methodName==null || methodName.trim().isEmpty()){
throw new RuntimeException("没有传递method参数 无法确定要调用的方法");
}
/*
* 得到方法名称,是否可以通过反射来调用方法
* 1、得到方法名,通过方法名再得到Method类的对象
* 需要得到class,然后调用它的方法进行查询。得到Method
* 我们要查询是当前类的方法,所以我们需要得到当前类的Class
*/
Class c= this.getClass();//得到当前类的class对象
Method method = null;
try {
method = c.getMethod(methodName,
HttpServletRequest.class,HttpServletResponse.class);
} catch (Exception e) {
throw new RuntimeException("要调用的方法:"+methodName+"(HttpServletRequest,HttpResponse),不存在");
}
/*
* 调用method表示的方法
*/
try {
//反射调用
String result = (String) method.invoke(this, req,resp);//this.addUser(req,resp)
/*
* 获取请求处理方法执行后返回的字符串,表示转发或重定向的路径
* 完成转发或重定向
*/
/*
* 如果用户返回的字符串为null,或为"",那么什么也不做
*/
if(result == null || result.trim().isEmpty()){
return;
}
/*
* 查看返回的字符串是否包含冒号,如果没有,表示转发
* 如果有:使用冒号分割字符串,得到前缀和后缀
* 其中前缀如果是f,表示转发,如果是r表示重定向,后缀就是要转发或重定向的连接
*/
if(result.contains(":")){
//使用冒号分割字符串,得到前缀和后缀
int index = result.indexOf(":");//获取冒号的位置
String s = result.substring(0,index);//截取出前缀 表示操作
String path = result.substring(index+1);//截取出后缀 表示路径
if(s.equalsIgnoreCase("r")){//如果前缀是r那么重定向
resp.sendRedirect(req.getContextPath()+path);
}else if(s.equalsIgnoreCase("f")){
req.getRequestDispatcher(path).forward(req, resp);
} else{
throw new RuntimeException("指定的操作"+s+"当前版本还不支持");
}
} else{//没有冒号 默认为转发
req.getRequestDispatcher(result).forward(req, resp);
}
} catch (Exception e) {
System.out.println("调用的"+methodName+",内部抛出了异常");
throw new RuntimeException(e);
}
}
}
/**
* 给出多个请求处理方法
* 请求处理方法:除了名称以外,都与service方法相同
* @author Administrator
*
*/
public class AServlet extends BaseServlet {
public void addUser(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("addUser");
//throw new IOException("测试");
}
public void editUser(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("editUser");
}
public void deleteUser(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("deleteUser");
}
}
public class BServlet extends BaseServlet {
public String fun1(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("fun1()");
return "f:/index.jsp";
}
public String fun2(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
return "r:/index.jsp";
}
public String fun3(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("fun1()");
return "d:/WEB-INF/files/a.rmvb";
}
}
JdbcUtils
public class JdbcUtils {
//配置文件的默认配置。要求必须给出c3p0-config.xml
private static ComboPooledDataSource dataSource = new ComboPooledDataSource();
//是事务专用连接 每个事务有自己独立的线程
private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
/**
* 使用连接池返回一个连接对象
* @return
* @throws SQLException
*/
public static Connection getConnection() throws SQLException{
Connection con = tl.get();
//当con 不等于null,说明已经嗲用过beginTransaction(),表示开启事务
if(con !=null) return con;
return dataSource.getConnection();
}
/**
* 返回连接池对象
*/
public static DataSource getDataSource(){
return dataSource;
}
/**
* 开启事务
* 1、获取一个Connection 设置它的setAutoCommit(false)
* 2、还要保证dao中使用的连接是我们刚刚创建的
* --------------------------------------------------
* 1、创建一个Connection,设置为手动提交
* 2、把这个Connection给dao用
* 3、还要让commitTransaction或rollbackTransaction可以获取到
* @throws SQLException
*/
public static void beginTransaction() throws SQLException{
Connection con = tl.get();
if(con != null) throw new SQLException("已经事务,不要重复开启");
/*
* 1、给con赋值
* 2、给con设置为手动提交
*/
con = getConnection();//给con赋值,表示事务已经开始
con.setAutoCommit(false);
tl.set(con);//把当前线程的连接保存起来
}
/**
* 提交事务
* 1、获取beginTransaction提供的Connection,然后调用commit方法
* @throws SQLException
*/
public static void commitTransaction() throws SQLException{
Connection con = tl.get();//获取当前线程的专用连接
if(con == null) throw new SQLException("还没开启事务,不能提交");
/*
* 1、直接使用con.commit()
*/
con.commit();
con.close();
//重新创建Connection 不然获取的是已经关闭的
//把它设置为null,表示事务已经结束了,下次再去调用getConnection()返回的就不是con了
//con = null;
tl.remove();//从tl中移除连接
}
/**
* 提交事务
* 1、获取beginTransaction提供的Connection,然后调用commit方法
* @throws SQLException
*/
public static void rollbackTransaction() throws SQLException{
Connection con = tl.get();
if(con == null) throw new SQLException("还没开启事务,不能回滚");
/*
* 1、直接使用con.rollback()
*/
con.rollback();
con.close();
tl.remove();
}
/**
* 释放连接
* @param con
* @throws SQLException
*/
public static void releaseConnection(Connection connection) throws SQLException{
Connection con = tl.get();
/*
* 判断是不是事务专用,如果是,就不关闭,
* 如果不是,那么就要关闭
*/
//如果con==null 那么说明现在没有事务,那么connection一定不是事务专用
if(con == null) connection.close();
//如果con!=null 说明有事务,那么需要判断参数连接是否域con相等。若不等,说明参数连接不是事务专用连接
if(con != connection) connection.close();
}
}
public class AccountDao {
public static void update(String name,double money) throws SQLException{
QueryRunner qr = new TxQueryRunner();
String sql = "update account set balance=balance+? where aname=?";
Object[] params ={money,name};
/*//需要自己来提供连接,保证多次调用的是使用同一连接 直接使用QueryRunner
Connection con = JdbcUtils.getConnection();
qr.update(con,sql,params);
JdbcUtils.releaseConnection(con);*/
//使用TxQueryRunner后
qr.update(sql,params);
}
}
public class Demo1 {
private AccountDao dao = new AccountDao();
@Test
public void serviceMethod() throws SQLException{
try {
JdbcUtils.beginTransaction();
dao.update("zs",100);
//if(true) throw new RuntimeException();
dao.update("ls",-100);
JdbcUtils.commitTransaction();
JdbcUtils.getConnection();
} catch (SQLException e) {
try {
JdbcUtils.rollbackTransaction();
} catch (SQLException e1) {
e1.printStackTrace();
}
throw e;
}
}
}
/**
* 这个类中的方法,自己来处理连接的问题
* 无需外界的传递
* 通过JdbcUtils.getConnection()得到连接,有可能是事务连接,也可能是普通的连接
* JdbcUtils.releaseConnection()完成对连接的释放,如果是普通连接 关闭之
*/
public class TxQueryRunner extends QueryRunner{
@Override
public int[] batch(String sql, Object[][] params) throws SQLException {
/*
* 1、得到连接
* 2、执行父类方法,传递连接对象
* 3、释放连接
* 4、返回值
*/
Connection con = JdbcUtils.getConnection();
int[] result = super.batch(sql, params);
JdbcUtils.releaseConnection(con);
return result;
}
@Override
public <T> List<T> execute(String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
Connection con = JdbcUtils.getConnection();
List<T> result = super.execute(con,sql,rsh,params);
JdbcUtils.releaseConnection(con);
return result;
}
@Override
public <T> T insert(String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
Connection con = JdbcUtils.getConnection();
T result = super.insert(con,sql,rsh,params);
JdbcUtils.releaseConnection(con);
return result;
}
@Override
public <T> T insert(String sql, ResultSetHandler<T> rsh) throws SQLException {
Connection con = JdbcUtils.getConnection();
T result = super.insert(con,sql,rsh);
JdbcUtils.releaseConnection(con);
return result;
}
@Override
public <T> T insertBatch(String sql, ResultSetHandler<T> rsh, Object[][] params) throws SQLException {
Connection con = JdbcUtils.getConnection();
T result = super.insertBatch(con,sql,rsh,params);
JdbcUtils.releaseConnection(con);
return result;
}
@Override
public <T> T query(String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
Connection con = JdbcUtils.getConnection();
T result = super.query(con,sql, rsh,params);
JdbcUtils.releaseConnection(con);
return result;
}
@Override
public <T> T query(String sql, ResultSetHandler<T> rsh) throws SQLException {
Connection con = JdbcUtils.getConnection();
T result = super.query(con,sql, rsh);
JdbcUtils.releaseConnection(con);
return result;
}
@Override
public int update(String sql, Object... params) throws SQLException {
Connection con = JdbcUtils.getConnection();
int result = super.update(con,sql,params);
JdbcUtils.releaseConnection(con);
return result;
}
@Override
public int update(String sql, Object param) throws SQLException {
Connection con = JdbcUtils.getConnection();
int result = super.update(con,sql,param);
JdbcUtils.releaseConnection(con);
return result;
}
@Override
public int update(String sql) throws SQLException {
Connection con = JdbcUtils.getConnection();
int result = super.update(con,sql);
JdbcUtils.releaseConnection(con);
return result;
}
}