写在前面,大家好!我是【跨考菌】,一枚跨界的程序猿,专注于后台技术的输出,目标成为
全栈攻城狮
!这博客是对我跨界过程的总结和思考。如果你也对Java
、后端技术
感兴趣,抑或是正在纠结于跨界,都可以关注我的动态,让我们一起学习,一起进步~
我的博客地址为:【跨考菌】的博客
目录
上篇【零基础上手JavaWeb】17 自己实现线程安全的JdbcUtils工具类 介绍了自己实现线程安全的JdbcUtils工具类。本文介绍一个基于Servlet的MVC架构客户关系管理系统案例,借此熟悉Spring框架底层的原理。和【跨考菌】一起加油吧~
源码:
链接:https://pan.baidu.com/s/128VFiWnd_YM9uBUgEpIomA 提取码:tjrl
如果你有收获,记得帮博主一键三连哦😊
1 BaseServlet
1.1 BaseServlet的作用
在开始客户管理系统之前,我们先写一个工具类:BaseServlet。
我们知道,写一个项目可能会出现N多个Servlet,而且一般一个Servlet只有一个方法(doGet或doPost),如果项目大一些,那么Servlet的数量就会很惊人。
为了避免Servlet的“膨胀”,我们写一个BaseServlet。它的作用是让一个Servlet可以处理多种不同的请求。不同的请求调用Servlet的不同方法。我们写好了BaseServlet后,让其他Servlet继承BaseServlet,例如CustomerServlet继承BaseServlet,然后在CustomerServlet中提供add()、update()、delete()等方法,每个方法对应不同的请求。
1.2 BaseServlet分析
我们知道,Servlet中处理请求的方法是service()方法,这说明我们需要让service()方法去调用其他方法。例如调用add()、mod()、del()、all()等方法!具体调用哪个方法需要在请求中给出方法名称!然后service()方法通过方法名称来调用指定的方法。
无论是点击超链接,还是提交表单,请求中必须要有method参数,这个参数的值就是要请求的方法名称,这样BaseServlet的service()才能通过方法名称来调用目标方法。例如某个链接如下:
<a href=”/xxx/CustomerServlet?method=add”>添加客户</a>
1.3 BaseServlet代码
public class BaseServlet extends HttpServlet {
/*
* 它会根据请求中的m,来决定调用本类的哪个方法
*/
protected void service(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
res.setContentType("text/html;charset=utf-8");
// 例如:http://localhost:8080/demo1/xxx?m=add
String methodName = req.getParameter("method");// 它是一个方法名称
// 当没用指定要调用的方法时,那么默认请求的是execute()方法。
if(methodName == null || methodName.isEmpty()) {
methodName = "execute";
}
Class c = this.getClass();
try {
// 通过方法名称获取方法的反射对象
Method m = c.getMethod(methodName, HttpServletRequest.class,
HttpServletResponse.class);
// 反射方法目标方法,也就是说,如果methodName为add,那么就调用add方法。
String result = (String) m.invoke(this, req, res);
// 通过返回值完成请求转发
if(result != null && !result.isEmpty()) {
req.getRequestDispatcher(result).forward(req, res);
}
} catch (Exception e) {
throw new ServletException(e);
}
}
}
2 效果图
3 实现逻辑
这里只对业务逻辑内容进行说明,源码参考文章开头的资源,jsp部分的逻辑这里不做赘述。
3.1 DAO
类路径下配置好c3p0配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<!-- 这是默认配置信息 -->
<default-config>
<!-- 连接四大参数配置 -->
<property name="jdbcUrl">jdbc:mysql://localhost:3306/ssm</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="user">root</property>
<property name="password">xxx</property>
<!-- 池参数配置 -->
<property name="acquireIncrement">3</property>
<property name="initialPoolSize">10</property>
<property name="minPoolSize">2</property>
<property name="maxPoolSize">10</property>
</default-config>
<!-- 专门为oracle提供的配置信息 -->
<named-config name="oracle-config">
<property name="jdbcUrl">jdbc:mysql://localhost:3306/mydb1</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="user">root</property>
<property name="password">123</property>
<property name="acquireIncrement">3</property>
<property name="initialPoolSize">10</property>
<property name="minPoolSize">2</property>
<property name="maxPoolSize">10</property>
</named-config>
</c3p0-config>
DAO层代码:
package cn.itcast.cstm.dao;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import cn.itcast.cstm.domain.Customer;
import cn.itcast.cstm.domain.PageBean;
import cn.itcast.jdbc.TxQueryRunner;
/**
* 持久层
*
* @author cxf
*
*/
public class CustomerDao {
// 你配置的c3p0配置文件会覆盖掉jar包中的配置文件。
private QueryRunner qr = new TxQueryRunner();
/**
* 添加客户
*
* @param c
*/
public void add(Customer c) {
try {
String sql = "insert into t_customer values(?,?,?,?,?,?,?)";
Object[] params = { c.getCid(), c.getCname(), c.getGender(),
c.getBirthday(), c.getCellphone(), c.getEmail(),
c.getDescription()};
qr.update(sql, params);
} catch(SQLException e) {
throw new RuntimeException(e);
}
}
/**
* 查询所有
* @return
*/
public PageBean<Customer> findAll(int pc, int ps) {
try {
/*
* 1. 他都PageBean对象pb
* 2. 设置pb的pc和ps
* 3. 得到tr,设置给pb
* 4. 得到beanList,设置给pb
* 5. 返回pb
*/
PageBean<Customer> pb = new PageBean<Customer>();
pb.setPc(pc);
pb.setPs(ps);
/*
* 得到tr
*/
String sql = "select count(*) from t_customer";
Number num = (Number)qr.query(sql, new ScalarHandler());
int tr = num.intValue();
pb.setTr(tr);
/*
* 得到beanList
*/
sql = "select * from t_customer order by cname limit ?,?";
List<Customer> beanList = qr.query(sql,
new BeanListHandler<Customer>(Customer.class),
(pc-1)*ps, ps);
pb.setBeanList(beanList);
return pb;
} catch(SQLException e) {
throw new RuntimeException(e);
}
}
/**
* 加载客户
* @param cid
* @return
*/
public Customer load(String cid) {
try {
String sql = "select * from t_customer where cid=?";
return qr.query(sql, new BeanHandler<Customer>(Customer.class), cid);
} catch(SQLException e) {
throw new RuntimeException(e);
}
}
/**
* 编辑客户
* @param c
*/
public void edit(Customer c) {
try {
String sql = "update t_customer set cname=?,gender=?,birthday=?," +
"cellphone=?,email=?,description=? where cid=?";
Object[] params = {c.getCname(), c.getGender(),
c.getBirthday(), c.getCellphone(), c.getEmail(),
c.getDescription(), c.getCid()};
qr.update(sql, params);
} catch(SQLException e) {
throw new RuntimeException(e);
}
}
/**
* 多条件组合查询
* @param criteria
* @return
*/
// public List<Customer> query(Customer criteria) {
// try {
// /*
// * 1. 给出sql模板
// * 2. 给出参数
// * 3. 调用query方法,使用结果集处理器:BeanListHandler
// */
// /*
// * 一、 给出sql模板
// * 二、 给出参数!
// */
// /*
// * 1. 给出一个sql语句前半部
// */
// StringBuilder sql = new StringBuilder("select * from t_customer where 1=1");
// /*
// * 2. 判断条件,完成向sql中追加where子句
// */
// /*
// * 3. 创建一个ArrayList,用来装载参数值
// */
// List<Object> params = new ArrayList<Object>();
// String cname = criteria.getCname();
// if(cname != null && !cname.trim().isEmpty()) {
// sql.append(" and cname like ?");
// params.add("%" + cname + "%");
// }
//
// String gender = criteria.getGender();
// if(gender != null && !gender.trim().isEmpty()) {
// sql.append(" and gender=?");
// params.add(gender);
// }
//
// String cellphone = criteria.getCellphone();
// if(cellphone != null && !cellphone.trim().isEmpty()) {
// sql.append(" and cellphone like ?");
// params.add("%" + cellphone + "%");
// }
//
// String email = criteria.getEmail();
// if(email != null && !email.trim().isEmpty()) {
// sql.append(" and email like ?");
// params.add("%" + email + "%");
// }
//
// /*
// * 三、执行query
// */
// return qr.query(sql.toString(),
// new BeanListHandler<Customer>(Customer.class),
// params.toArray());
// } catch(SQLException e) {
// throw new RuntimeException(e);
// }
// }
public PageBean<Customer> query(Customer criteria, int pc, int ps) {
try {
/*
* 1. 创建PageBean对象
* 2. 设置已有的属性,pc和ps
* 3. 得到tr
* 4. 得到beanList
*/
/*
* 创建pb,设置已有属性
*/
PageBean<Customer> pb = new PageBean<Customer>();
pb.setPc(pc);
pb.setPs(ps);
/*
* 得到tr
*/
/*
* 1. 给出一个sql语句前半部
*/
StringBuilder cntSql = new StringBuilder("select count(*) from t_customer");
StringBuilder whereSql = new StringBuilder(" where 1=1");
/*
* 2. 判断条件,完成向sql中追加where子句
*/
/*
* 3. 创建一个ArrayList,用来装载参数值
*/
List<Object> params = new ArrayList<Object>();
String cname = criteria.getCname();
if(cname != null && !cname.trim().isEmpty()) {
whereSql.append(" and cname like ?");
params.add("%" + cname + "%");
}
String gender = criteria.getGender();
if(gender != null && !gender.trim().isEmpty()) {
whereSql.append(" and gender=?");
params.add(gender);
}
String cellphone = criteria.getCellphone();
if(cellphone != null && !cellphone.trim().isEmpty()) {
whereSql.append(" and cellphone like ?");
params.add("%" + cellphone + "%");
}
String email = criteria.getEmail();
if(email != null && !email.trim().isEmpty()) {
whereSql.append(" and email like ?");
params.add("%" + email + "%");
}
/*
* select count(*) .. + where子句
* 执行之
*/
Number num = (Number)qr.query(cntSql.append(whereSql).toString(),
new ScalarHandler(), params.toArray());
int tr = num.intValue();
pb.setTr(tr);
/*
* 得到beanList
*/
StringBuilder sql = new StringBuilder("select * from t_customer");
// 我们查询beanList这一步,还需要给出limit子句
StringBuilder limitSql = new StringBuilder(" limit ?,?");
// params中需要给出limit后两个问号对应的值
params.add((pc-1)*ps);
params.add(ps);
// 执行之
List<Customer> beanList = qr.query(sql.append(whereSql).append(limitSql).toString(),
new BeanListHandler<Customer>(Customer.class),
params.toArray());
pb.setBeanList(beanList);
return pb;
} catch(SQLException e) {
throw new RuntimeException(e);
}
}
}
3.2 domain
Customer.java:
package cn.itcast.cstm.domain;
/**
* 领域对象 与表单和数据库表对应
*
* @author cxf
*
*/
public class Customer {
/*
* 对应数据库表
*/
private String cid;// 主键
private String cname;// 客户名称
private String gender;// 客户性别
private String birthday;// 客户生日
private String cellphone;// 客户手机
private String email;// 客户邮箱
private String description;// 客户的描述
public String getCid() {
return cid;
}
public void setCid(String cid) {
this.cid = cid;
}
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getBirthday() {
return birthday;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
public String getCellphone() {
return cellphone;
}
public void setCellphone(String cellphone) {
this.cellphone = cellphone;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public String toString() {
return "Customer [cid=" + cid + ", cname=" + cname + ", gender="
+ gender + ", birthday=" + birthday + ", cellphone="
+ cellphone + ", email=" + email + ", description="
+ description + "]";
}
}
PageBean.java:
package cn.itcast.cstm.domain;
import java.util.List;
public class PageBean<T> {
private int pc;// 当前页码page code
// private int tp;// 总页数total page
private int tr;// 总记录数total record
private int ps;// 每页记录数page size
private List<T> beanList;// 当前页的记录
private String url;//它就是url后的条件!
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public int getPc() {
return pc;
}
public void setPc(int pc) {
this.pc = pc;
}
/**
* 计算总页数
*
* @return
*/
public int getTp() {
// 通过总记录数和每页记录数来计算总页数
int tp = tr / ps;
return tr % ps == 0 ? tp : tp + 1;
}
// public void setTp(int tp) {
// this.tp = tp;
// }
public int getTr() {
return tr;
}
public void setTr(int tr) {
this.tr = tr;
}
public int getPs() {
return ps;
}
public void setPs(int ps) {
this.ps = ps;
}
public List<T> getBeanList() {
return beanList;
}
public void setBeanList(List<T> beanList) {
this.beanList = beanList;
}
}
3.3 Service
package cn.itcast.cstm.service;
import java.util.List;
import cn.itcast.cstm.dao.CustomerDao;
import cn.itcast.cstm.domain.Customer;
import cn.itcast.cstm.domain.PageBean;
/**
* 业务层
* @author cxf
*
*/
public class CustomerService {
private CustomerDao customerDao = new CustomerDao();
/**
* 添加客户
* @param c
*/
public void add(Customer c) {
customerDao.add(c);
}
/**
* 查询所有
* @return
*/
public PageBean<Customer> findAll(int pc, int ps) {
return customerDao.findAll(pc, ps);
}
/**
* 加载客户
* @param cid
* @return
*/
public Customer load(String cid) {
return customerDao.load(cid);
}
/**
* 编辑客户
* @param c
*/
public void edit(Customer c) {
customerDao.edit(c);
}
/**
* 多条件组合查询
* @param criteria
* @return
*/
public PageBean<Customer> query(Customer criteria, int pc, int ps) {
return customerDao.query(criteria, pc, ps);
}
}
3.4 Servlet
package cn.itcast.cstm.web.servlet;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import cn.itcast.commons.CommonUtils;
import cn.itcast.cstm.domain.Customer;
import cn.itcast.cstm.domain.PageBean;
import cn.itcast.cstm.service.CustomerService;
import cn.itcast.servlet.BaseServlet;
/**
* Web层
* @author cxf
*
*/
public class CustomerServlet extends BaseServlet {
private CustomerService customerService = new CustomerService();
/**
* 添加客户
*/
public String add(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/*
* 1. 封装表单数据到Customer对象
* 2. 补全:cid,使用uuid
* 3. 使用service方法完成添加工作
* 4. 向request域中保存成功信息
* 5. 转发到msg.jsp
*/
Customer c = CommonUtils.toBean(request.getParameterMap(), Customer.class);
c.setCid(CommonUtils.uuid());
customerService.add(c);
request.setAttribute("msg", "恭喜,添加客户成功!");
return "f:/msg.jsp";
}
/**
* 查询所有
* @param request
* @param response
* @return
* @throws ServletException
* @throws IOException
*/
// public String findAll(HttpServletRequest request, HttpServletResponse response)
// throws ServletException, IOException {
// /*
// * 1. 调用service得到所有客户
// * 2. 保存到request域
// * 3. 转发到list.jsp
// */
// request.setAttribute("cstmList", customerService.findAll());
// return "f:/list.jsp";
// }
public String findAll(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/*
* 1. 获取页面传递的pc
* 2. 给定ps的值
* 3. 使用pc和ps调用service方法,得到PageBean,保存到request域
* 4. 转发到list.jsp
*/
int pc = getPc(request);//得到pc
int ps = 10;//给定ps的值,第页10行记录
PageBean<Customer> pb = customerService.findAll(pc, ps);//传递pc, ps给Service,得到PageBean
// 设置url
pb.setUrl(getUrl(request));
request.setAttribute("pb", pb);//保存到request域中
return "f:/list.jsp";//转发到list.jsp
}
/**
* 获取pc
* @param request
* @return
*/
private int getPc(HttpServletRequest request) {
/*
* 1. 得到pc
* 如果pc参数不存在,说明pc=1
* 如果pc参数存在,需要转换成int类型即可
*/
String value = request.getParameter("pc");
if(value == null || value.trim().isEmpty()) {
return 1;
}
return Integer.parseInt(value);
}
/**
* 编辑之前的加载工作
* @param request
* @param response
* @return
* @throws ServletException
* @throws IOException
*/
public String preEdit(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/*
* 1. 获取cid
* 2. 使用cid来调用service方法,得到Customer对象
* 3. 把Customer保存到request域中
* 4. 转发到edit.jsp显示在表单中
*/
String cid = request.getParameter("cid");
Customer cstm = customerService.load(cid);
request.setAttribute("cstm", cstm);
return "f:/edit.jsp";
}
/**
* 编辑方法
* @param request
* @param response
* @return
* @throws ServletException
* @throws IOException
*/
public String edit(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/*
* 1. 封装表单数据到Customer对象中
* 2. 调用service方法完成修改
* 3. 保存成功信息到request域
* 4. 转发到msg.jsp显示成功信息
*/
// 已经封装了cid到Customer对象中
Customer c = CommonUtils.toBean(request.getParameterMap(), Customer.class);
customerService.edit(c);
request.setAttribute("msg", "恭喜,编辑客户成功!");
return "f:/msg.jsp";
}
// public String query(HttpServletRequest request, HttpServletResponse response)
// throws ServletException, IOException {
// /*
// * 1. 封装表单数据到Customer对象中,它只有四个属性(cname、gender、cellphone、email)
// * 它就是一个条件
// * 2. 使用Customer调用service方法,得到List<Customer>
// * 3. 保存到request域中
// * 4. 转发到list.jsp
// */
// Customer criteria = CommonUtils.toBean(request.getParameterMap(), Customer.class);
// List<Customer> cstmList = customerService.query(criteria);
// request.setAttribute("cstmList", cstmList);
// return "/list.jsp";
// }
public String query(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// System.out.println(getUrl(request));
/*
* 0. 把条件封装到Customer对象中
* 1. 得到pc
* 2. 给定ps
* 3. 使用pc和ps,以及条件对象,调用service方法得到PageBean
* 4. 把PageBean保存到request域中
* 5. 转发到list.jsp
*/
// 获取查询条件
Customer criteria = CommonUtils.toBean(request.getParameterMap(), Customer.class);
/*
* 处理GET请求方式编码问题!
*/
criteria = encoding(criteria);
int pc = getPc(request);//得到pc
int ps = 10;//给定ps的值,第页10行记录
PageBean<Customer> pb = customerService.query(criteria, pc, ps);
// 得到url,保存到pb中
pb.setUrl(getUrl(request));
request.setAttribute("pb", pb);
return "f:/list.jsp";
}
/**
* 处理四样
* @param criteria
* @return
* @throws UnsupportedEncodingException
*/
private Customer encoding(Customer criteria) throws UnsupportedEncodingException {
String cname = criteria.getCname();
String gender = criteria.getGender();
String cellphone = criteria.getCellphone();
String email = criteria.getEmail();
if(cname != null && !cname.trim().isEmpty()) {
cname = new String(cname.getBytes("ISO-8859-1"), "utf-8");
criteria.setCname(cname);
}
if(gender != null && !gender.trim().isEmpty()) {
gender = new String(gender.getBytes("ISO-8859-1"), "utf-8");
criteria.setGender(gender);
}
if(cellphone != null && !cellphone.trim().isEmpty()) {
cellphone = new String(cellphone.getBytes("ISO-8859-1"), "utf-8");
criteria.setCellphone(cellphone);
}
if(email != null && !email.trim().isEmpty()) {
email = new String(email.getBytes("ISO-8859-1"), "utf-8");
criteria.setEmail(email);
}
return criteria;
}
/**
* 截取url
* /项目名/Servlet路径?参数字符串
* @param request
* @return
*/
private String getUrl(HttpServletRequest request) {
String contextPath = request.getContextPath();//获取项目名
String servletPath = request.getServletPath();//获取servletPath,即/CustomerServlet
String queryString = request.getQueryString();//获取问号之后的参数部份
// 判断参数部份中是否包含pc这个参数,如果包含,需要截取下去,不要这一部份。
if(queryString.contains("&pc=")) {
int index = queryString.lastIndexOf("&pc=");
queryString = queryString.substring(0, index);
}
return contextPath + servletPath + "?" + queryString;
}
}
其中BaseServlet在上面已经定义了,只是项目中是以工具包的形式导入的。
所有的请求都会被BaseServlet的service基于反射机制获取到请求的method参数,进而调用Servlet中的其他方法。
4 分页
4.1 分页数据分析
页面需要什么数据:
当前页页码(currPageCode):Servlet提供;
共几页(totalPage):Servlet提供;
当前页数据(datas):Servlet提供;
Servlet需要什么数据:
当前页页码(currPageCode):页面提供,如果页面没有提供,那么默认为1;
总记录数(totalRecord):通过数据库来查询;
每页记录数(pagesize):系统数据;
共几页(totalPage):通过totalRecord和pagesize来计算;
当前页第一行记录位置(currPageBeginIndex):通过currPageCode和pagesize计算;
当前页数据(datas):通过currPageBginIndex和pagesize查询数据库;
4.2 PageBean
把分布数据封装成PageBean类对象
public class PageBean<T> {
private List<T> datas;// 当前页记录数, 需要传递
private int totalRecord;// 总记录数, 需要传递
private int currPageCode;// 当前页码, 需要传递
private int pagesize;// 每页记录数, 需要传递
private int totalPage;// 总页数, 计算
private int currPageBeginIndex; //需要计算
public PageBean(int currPageCode, int totalRecord, int pagesize) {
this.currPageCode = currPageCode;
this.totalRecord = totalRecord;
this.pagesize = pagesize;
init();
}
private void init() {
this.totalPage = totalRecord / pagesize;
if(totalRecord % pagesize != 0) {
this.totalPage++;
}
this.currPageBeginIndex = (this.currPageCode-1) * this.pagesize;
}
...
}
如果对你有帮助,记得帮博主一键三连😊哦