简单的酒店管理系统
系统架构
1. Controller 控制器层:业务分发、返回业务处理结果
2. View 视图层:展示数据
3. Model 模型层:包装起来的数据,本质是数据
4. Service 业务层:业务逻辑处理
5. Dao 数据库访问层:与数据库通信
数据库设计
访问流程图
通信协议制定
1.获取表的静态数据:
{account:用户账号
tableName:表名,
tableHead:[[属性名,列名,input类型,…]],
opUrl:编辑链接}
说明:
属性名对应数据对象的属性名、列名是展示给用户看的列名
编辑链接告诉试图层要不要编辑表的功能
视图层根据这些信息动态生成不同数据表的展示页面
2.查询表:
请求:{pageSize:页大小,pageIndex:第几页,url}
返回:{recordNum:记录数,records:[数据对象数组]}
3.编辑表:
请求:{url,op:(增/删/改),属性名:属性值,属性名:属性值,…}
返回:{msg:成功与否}
编辑前端页面
实现相关工具类
JdbcUtils
使用条件:
数据表id设置为 id String
数据列表名及顺序与对象属性名一致
1.<T> List<T> queryBySql(String sql,Class<T> type)
功能:根据sql语句查询,并将查询结果包装成传入的type类型对象,再放入list集合并返回
其中用到BeanUtils类的<T> T packBean(String[] values, Class<T> type)方法,用于将values属性值数组包装到type类型对象中并返回
public static <T> List<T> queryBySql(String sql,Class<T> type)
throws ReflectiveOperationException{
ArrayList<T> list = new ArrayList<>();
Connection conn = ConnectionPool.get(); //连接池中获取连接
try(
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
){
if(!rs.next()) {//若无结果,返回集合赋空
list = null;
}else{
do {//包装查询结果为对象,并放入集合
list.add(BeanUtils.packBean(
getFields(rs,type.getDeclaredFields()),type));
}while(rs.next());
}
}catch(SQLException e) {
e.printStackTrace();
}finally {
ConnectionPool.close(conn); //连接返回池中
}
return list;
}
2.String[] getFields(ResultSet rs, Field[] fields)
功能:从ResultSet对象中取得对应Filed对象的属性值的字符串格式
public static String[] getFields(ResultSet rs, Field[] fields) throws SQLException {
String[] values = new String[fields.length];
for (int i=0;i<fields.length;i++) {
fields[i].setAccessible(true);
values[i] = rs.getString(fields[i].getName());
}
return values;
}
其他方法主要围绕queryBySql(String)、updateBySql(String)经行扩展
BeanUtils
功能:主要是将String类型的属性值转化为Field.getType()类型的属性值并设置进对象
1.<T> T packBean(String[] values, Class<T> type)
功能:将values数组的值包装进type类型对象中
使用条件:数组中属性值顺序应与对象属性定义顺序一致
public static <T> T packBean(String[] values, Class<T> type)
throws ReflectiveOperationException {
T obj = type.newInstance();
Field[] fields = type.getDeclaredFields();
for(int i=0;i<values.length;i++) {
if(values[i]==null) {//若字符串值为空,开始下次循环
continue;
}
fields[i].setAccessible(true);
Object value = null;
try {//将字符串值转化为属性对应的类型
value = getRealValue(fields[i].getType(),values[i]);
fields[i].set(obj, value);//设置属性值
} catch (Exception e) {
e.printStackTrace();
continue;
}
}
return obj;
}
2.Object getRealValue(Class<?> type,String value)
功能:将value转化为type类型,若无法转化抛出异常
public static Object getRealValue(Class<?> type,String value) throws Exception{
Object obj = value;
if(type==int.class||type==Integer.class) {
obj = Integer.valueOf(value);
}else if(type==String.class) {
}else if(type==BigDecimal.class) {
obj = new BigDecimal(value);
}else if(type==Date.class) {
obj = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(value);
}else if(type==boolean.class||type==Boolean.class) {
obj = (value.equals("1")||value.equals("true"))?true:false;
}else if(type==double.class||type==Double.class) {
obj = Double.valueOf(value);
}else if(type==byte.class||type==Byte.class) {
obj = Byte.valueOf(value);
}else if(type==long.class||type==Long.class) {
obj = Long.valueOf(value);
}else if(type==short.class||type==Short.class) {
obj = Short.valueOf(value);
}else if(type==float.class||type==Float.class) {
obj = Float.valueOf(value);
}
return obj;
}
3.<T> T packBean(Map<String, String[]> paramMap,Class<T> type)
功能:将Map<属性名,字符串属性值>集合包装成对应type类型对象
public static <T> T packBean(Map<String, String[]> paramMap,
Class<T> type) throws ReflectiveOperationException{
T obj = type.newInstance();
Set<Entry<String, String[]>> entrys = paramMap.entrySet();
for (Entry<String, String[]> entry: entrys) {
Field field = null;
try {//获取该类型对象对应属性名的Field对象,若无,开始下次循环
field = type.getDeclaredField(entry.getKey());
} catch (ReflectiveOperationException e) {
continue;
}
//若字符串属性值为空可以不设置,开始下次循环
if(entry.getValue()[0]==null) {
continue;
}
field.setAccessible(true);
Object value = null;
try {//字符串属性值不为空,设置属性值
value = getRealValue(field.getType(),entry.getValue()[0]);
field.set(obj, value);
} catch (Exception e) {
e.printStackTrace();
}
}
return obj;
}
BaseController、BaseService、BaseDao、BaseBean
每个数据库表都对应一个BaseBean的子类,也有对应的BaseDao、BaseService的子类
edit方法、getJsonData方法操作的数据库表决定与BaseController的子类控制器传入edit方法、getJsonData方法的service对象
也就是只要数据库表对应的模型层、数据库访问层、业务层继承对应基类,即可实现基本的增删改查功能
Basecontroller
1.doPost调用doGet方法
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
doGet(req, resp);
}
2.doGet
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
String uri = req.getRequestURI();
int startIndex = uri.lastIndexOf("/")+1;
String target = uri.substring(startIndex);//获得请求目标
try {//反射调用目标方法处理请求
Method method = this.getClass().getDeclaredMethod(target,
HttpServletRequest.class,HttpServletResponse.class);
String command = (String) method.invoke(this, req,resp);
doOver(req,resp,command);//根据返回信息响应浏览器
} catch (ReflectiveOperationException e) {
e.printStackTrace();
resp.sendError(404);
}
}
3.doOver响应
command格式为: 指令:信息
private void doOver(HttpServletRequest req, HttpServletResponse resp,
String command) throws ServletException, IOException {
String[] arr = command.split(":", 2);
switch (arr[0]) {
case "forward":
req.getRequestDispatcher(arr[1]).forward(req, resp);
break;
case "redirect":
resp.sendRedirect(arr[1]);
break;
case "error":
resp.sendError(Integer.valueOf(arr[1]));
break;
case "data":
resp.getOutputStream().write(arr[1].getBytes());
break;
default:
break;
}
}
4.getJsonData 获取json格式的数据表数据
传入的service不同,获取的数据表数据不同
protected <T extends BaseBean> String getJsonData(HttpServletRequest req,BaseService<T> service) {
Map<String,String[]> paramMap = req.getParameterMap();
String vagueSql = req.getParameter("vagueSql");
Page page = null;
try {
page = BeanUtils.packBean(paramMap, Page.class);
} catch (ReflectiveOperationException e) {
e.printStackTrace();
}
//根据条件查询数据表,并将结果包装为list集合
List<T> list = service.querySome(vagueSql, page);
//查询记录数
int recordNum = service.queryCount(vagueSql);
JSONObject jsonData = new JSONObject();
jsonData.put("recordNum", recordNum);
jsonData.put("records", list);
return jsonData.toJSONString();
}
5.edit编辑数据表
protected <T extends BaseBean> String edit(BaseService<T> service,String command,T obj) {
String ret = "";
switch (command) {
case "modify":
//修改数据库中id与obj.id相同的记录,并返回修改结果
ret = (service.modify(obj))?"修改成功!":"修改失败!";
break;
case "add":
//添加obj到数据库,并返回添加结果
ret = (service.add(obj))?"添加成功!":"添加失败!";
break;
case "del":
//删除数据库中id与obj.id相同的记录,并返回删除结果
ret = (service.del(obj))?"删除成功!":"删除失败!";
break;
default:
break;
}
return ret;
}
BaseDao
public class BaseDao<T extends BaseBean> {
protected Class<T> targetClass; //数据表对应的对象类型
protected String tableName; //数据表名
protected BaseDao(String tableName,Class<T> targetClass) {
this.tableName = tableName;
this.targetClass = targetClass;
}
//根据id查询记录
public T queryById(String id) {
try {
return (T) JdbcUtils.getEntryById(tableName, id, targetClass);
} catch (ReflectiveOperationException e) {
e.printStackTrace();
}
return null;
}
//插入记录
public synchronized boolean insert(T bean) {
if(queryById(bean.getId())==null) {
try {
return JdbcUtils.insertEntry(bean, tableName);
} catch (ReflectiveOperationException e) {
e.printStackTrace();
}
}
return false;
}
//删除记录
public boolean delete(T bean) {
return JdbcUtils.delEntryById(tableName, bean.getId());
}
//查询记录
public List<T> querySome(String vagueSql, int limitA, int limitB) {
List<T> list = null;
try {
list = JdbcUtils.querySome(
tableName, targetClass,vagueSql,limitA,limitB);
} catch (ReflectiveOperationException e) {
e.printStackTrace();
}
return list;
}
//查询记录数
public int queryCount(String vagueSql) {
String sql = "select count(*) from "+tableName+vagueSql;
return JdbcUtils.queryCount(sql);
}
//修改数据库,实际是替换数据库中id与bean.id相同的记录
public synchronized boolean modify(T bean) {
T dbBean;
if((dbBean=queryById(bean.getId())) != null) {
delete(dbBean);
try {
return JdbcUtils.insertEntry(bean, tableName);
} catch (ReflectiveOperationException e) {
e.printStackTrace();
}
}
return false;
}
//查询所有记录
public List<T> queryAll() {
List<T> list = null;
try {
list = JdbcUtils.queryAll(tableName,targetClass);
} catch (ReflectiveOperationException e) {
e.printStackTrace();
}
return list;
}
}
BaseService
public class BaseService<T extends BaseBean> {
protected BaseDao<T> dao; //对应的dao对象
protected Class<T> targetClass; //数据表对应的对象类型
protected BaseService(BaseDao<T> dao,Class<T> targetClass){
this.dao = dao;
this.targetClass = targetClass;
}
//查询一些记录
public List<T> querySome(String vagueSql, Page page) {
List<T> list = null;
if(vagueSql==null) {
vagueSql = "";
}
list = dao.querySome(vagueSql,page.getLimitA(), page.getPageSize());
return list;
}
//查询记录数量
public int queryCount(String vagueSql) {
if(vagueSql==null) {
vagueSql = "";
}
return dao.queryCount(vagueSql);
}
//修改记录
public boolean modify(T bean) {
if(bean.getId()==null||bean.getId().equals("")) {
return false;
}
return dao.modify(bean);
}
//添加记录
public boolean add(T bean) {
if(bean.getId()==null||bean.getId().equals("")) {
bean.setId(randomId());
}
return dao.insert(bean);
}
//生成随机Id
public String randomId() {
return UUID.randomUUID().toString().replaceAll("-", "");
}
//删除记录
public boolean del(T bean) {
if(bean.getId()==null||bean.getId().equals("")) {
return false;
}
return dao.delete(bean);
}
}
总结
1.确立需求,并分析需求建立好的数据库非常重要,后期修改数据库是非常麻烦的事情
2.提前定义通信流程、视图层与控制器层的通信协议,能让项目编写过程更加顺畅
3.善于辨别必要的通信数据可以更好的制定通信协议
4.编写好的工具类能使项目编写过程方便很多
5.根据相关类的共性并利用映射与泛型提取出基类,可以大大降低代码耦合度并使后续扩张简单