【Project】原生JavaWeb工程 01 概述,搭建
一、环境准备:
操作系统:Windows7 或者 Windows10
IDE集成环境:IDEA 2018版本或者更高
数据库:MySQL 5版本或者更高
服务器:Tomcat 8版本或者更高
二、数据库的表设计:
# 年级表 - 班级表 - 课程表 - 章节表 - 题目表
# 暂不涉及RBAC
就只给一个用户表,设置简单的访问权限控制即可
# 所有表都具备主键
这里省略不谈了 [主键,非空,自增,无符号]
# 命名规范
建议是同一个模块内的数据表使用相同的前缀
# 例如:module1_tableA,module1_tableB
# 标识符的命名不要使用大小写区分,例如Oracle的数据库默认使用全大写标识
# 不建议使用数据库的外键关联强制约束,
这么做看起来数据维护非常的准确,但是这对我们后段程序的编写是极其不利的
# 是否设置is_del属性?
我们希望数据提供给使用者访问时,不应该使用真正的删除,为保留数据,会使用这一字段来实现
# 接上面的is_del属性的软删除,我们是否还需要支持硬删除?
【就我自己考虑而言,我认为这功能不会出现,但是可以给管理员使用,由管理员来抉择】
# 对于元数据的一些设置: 基于所有开发者共同遵守的不成文约定:
整数取INT,字符串取VARCHAR,日期时间DATETIME 【其他的不是很常见,类型得看情况】
偶然看到一个博友的元数据范围喜欢以2的次幂来设置,这看起来很棒,所以我也这么做了
对主键的设置使用8,不要超过Int范围就好了
对字符串类型的范围一般来说使用64,如果是这种描述的文本内容,可以设置128,224。。。
1、年级表
年级名称,顺序编号, 如果需要更多描述,创建时间,修改时间,年级描述 t_grade grade_id,
grade_name,
grade_order_id,
grade_is_del,
2、班级表
即每一个年级有多少个班级,和年级同理
一个年级具有若干个班级
但是一个班级只能对应所属的一个年级
1、班级所属的年级编号
2、班级名称
3、班级顺序编号
班级描述
创建时间
修改时间 t_class class_id,
class_grade_id,
class_name,
class_order_id,
class_is_del 注意班级的英文单词,和Java的保留字相冲突,可以使用clasz、clazz、clazs相替换
在这里的疑问?
虽然大学使用的是XXX大学XXX院校XXX系XXX级的XXX这样的
如果想我们之前的小中高中这样的,每一年就会升级,又如何考虑呢?
可以确定的是我们设置的年级应该是可以被确定的...先想到这里把
3、课程表
课程是提供给年级设计的,
即一个年级需要多个课程,
但是一个课程只能对应一个年级 t_course course_id,
course_grade_id,
course_name,
course_order_id,
course_is_del course_description,
course_gen_time,
course_alt_time
可以设想一下课程和班级的关系
似乎,多个课程对于多个班级,相反这道理也是一样,两者没有直接的关系
多对多,就只有这样了
4、章节表
章节表 只对应所属的课程
一个课程具有多个章节
一个章节对应一个课程 t_chapter chapter_id,
chapter_course_id,
chapter_name,
chapter_order_id,
chapter_is_del, chapter_gen_time,
chapter_alt_time 注:
设计之初忽略了书这个玩意
也许可以创建一个书的表
应该是一个课程需要多本书,一本书对应一个课程,如此这样
书和章节的关系也是如此,看起来就说得通了
5、题目表
就是一些测试,考试的题目,应该简称测验
英文好简单写一些:exam
我们希望题目可以直接被一些关键字段直接检索
简单点说就是:
我要XXX年级的题目
我要XXX班的题目
我要XXX课程的题目
我要XXX章节的题目
... ... t_exam exam_id,
exam_grade_id, 所属的年级
exam_class_id, 所属的班级
exam_course_id, 所属的课程
exam_chapter_id, 所属的章节
exam_title, 题目的标题
exam_type, 题目的类型,1选择题,2填空题,3简答题,4...
exam_score, 题目分值
exam_option_a, 选项... 和对应的选择描述
exam_option_b,
exam_option_c,
exam_option_d,
exam_answer, 答案
exam_difficulty, 难易程度 0简单的离谱 1简单 2正常 3困难 4很困难 5困难的离谱
exam_is_del,
上述的这些仅作为基础数据查询,或者应该说作为对于这个系统的元数据
6、用户表
简单的也有,复杂的也有,只谈目前阶段需要设置的东西则非常简单 sys_user
sys表示系统的意思,rbac权限是和系统的模块相关 user_id,
user_name,
user_password,
user_status,
user_is_del user_phone,
user_email,
user_gen_time,
user_alt_time,
user_login_time,
user_last_login_time,
user_login_count,
三、工程目录划分:
使用IDEA创建JavaWeb工程:
声明项目名称:
[OA-Project-Phase1]
项目的初始结构:
所有的资源,应该被明确且优雅的管理归类,以便于我们后期的管理
src目录用于存放我们编写的Java源码,即后台程序
web目录一般用于存放网页静态资源,不过这是基于Tomcat所规定的规范
另外基于原生的JavaWeb,在不采用框架技术的情况,只能使用JSP完成页面和后台的交互
关于这些目录的一些概念
src目录可能需要存放的一些目录,:
pojo Plain Ordinary Java Object
实体类,存放一些数据模型类,又称为domain,entity,model等等
po Persistant Object
持久化对象 持久层对象的意思,对应数据库中表的字段,
数据库表中的记录在java对象中的显示状态,最形象的理解就是一个PO就是数据库中的一条记录。
好处是可以把一条记录作为一个对象处理,可以方便的转为其它对象。
Vo和Po,都是属性加上属性的get和set方法;表面看没什么不同,但代表的含义是完全不同的
dto Data Transport Object
数据传输对象 提高数据传输的速度(减少了传输字段),二能隐藏后端表结构
dao Data Access Object
数据访问层,存放和数据库访问相关的访问类
bo Business Object
业务对象 把业务逻辑封装为一个对象(注意是逻辑,业务逻辑),
这个对象可以包括一个或多个其它的对象。通过调用Dao方法,结合Po或Vo进行业务操作
vo View Object
视图对象 对于一个WEB页面将整个页面的属性封装成一个对象,
然后用一个VO对象在控制层与视图层进行传输交换
service Business
业务逻辑层,存放业务实际处理相关
serverModel 消息模型层,例如JSON,翻页等等,消息数据的模型层
servlet manager controller
视图处理层,存放转发和数据携带相关的servlet规范类
filter
过滤器,存放一些拦截过滤的处理
util
工具包目录,提高开发效率的工具类
web目录
/static 静态文件目录(如果采用模版或者其他文件,这目录的命名看情况,不完全要求)
/WEB-INF/jsp /view /... 所有jsp文件都会放在这里面,访问安全限制,至于二级目录各种命名,习惯就好
/WEB-INF/lib 存放jar依赖组件
构建工具和IDEA提供的一些规范目录
- SourcesRoot
源文件根目录,也就是指类路径,存放所有Java源码的地方,其内部的包必须遵守Java命名规范
- TestSourcesRoot
测试根目录,用来存放测试代码的地方,在生成实际工程文件时,这个测试目录内的代码不会被添加进去
- ResourcesRoot
资源根目录,用以存放各种各样的配置文件
- Test ResourcesRoot
测试资源目录,用以存放测试需要的配置文件,可以把单元测试组件和测试用的连接配置单独放在这里面
- Excluded
被排除目录,放在这里面的资源在工程生成时不会被添加进去
四、主要开发组件【简单的轮子】:
前后端使用Ajax数据交互的统一格式要求使用JSON,
基于这个规范,我们设置一个统一的后端处理的对象:JsonResult
前端也默认只通过这个对象获取需要的数据在页面渲染
package cn.dzz.util.common; /**
* @author DaiZhiZhou
* @file OA-Project-Phase1
* @create 2020-07-02 8:34
*/
public class JsonResult { private Integer status;
private String message;
private Object data; public JsonResult() {
} public JsonResult(Object data) {
this.data = data;
} public JsonResult(Integer status, String message) {
this.status = status;
this.message = message;
} public JsonResult(Integer status, String message, Object data) {
this.status = status;
this.message = message;
this.data = data;
} public Integer getStatus() {
return status;
} public void setStatus(Integer status) {
this.status = status;
} public String getMessage() {
return message;
} public void setMessage(String message) {
this.message = message;
} public Object getData() {
return data;
} public void setData(Object data) {
this.data = data;
} @Override
public String toString() {
return "JsonResult{" +
"status=" + status +
", message='" + message + '\'' +
", data=" + data +
'}';
}
}
要注意的是这里没有实现序列化,但相对于这个类的成员不会发生变化的情况来说,这个设置可有可无,非必要
简单的请求分发处理器BaseServlet:
package cn.dzz.util.common; import com.alibaba.fastjson.JSON; import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method; /**
* @author DaiZhiZhou
* @file OA-Project-Phase1
* @create 2020-07-02 8:34
*/
public abstract class BaseServlet extends HttpServlet { @Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 设置统一的异常处理,所有的需要通过BaseServlet调用的方法一定会到这里
try { // 我们对需要调用的方法做一个统一的访问标识,供反射获取
String act = req.getParameter("act"); // 这个this不是本BaseServlet,一定是继承的子类的实例获取的子类字节对象
Class<? extends BaseServlet> subClass = this.getClass(); // 通过上面的约束规范获取封装的方法对象
Method subServletDeclaredMethod = subClass.getDeclaredMethod(act, HttpServletRequest.class, HttpServletResponse.class); // 打印查看
System.out.println("[请求访问地址:" + req.getRequestURL() + " \n访问的类:" + subClass.getName() + ",访问的方法" + subServletDeclaredMethod.getName() + "]"); // 通过这个方法对象调用方法
Object afterThings = subServletDeclaredMethod.invoke(this, req, resp); // 如果返回String我们就知道是需要重定向和转发了,因为会返回一个资源路径
if (afterThings instanceof String) { // 类型转换,或者得到需要的内容
String accessCoordinates = afterThings.toString(); // 是否包含redirect:信息
if (accessCoordinates.contains("redirect:")) { // 砍掉redirect: 获取地址
String visitLocate = accessCoordinates.substring(9); // 响应给客户端重定向访问的地址, 因为重定向不是从工程路径发送,是根/地址,所以需要动态补充
resp.sendRedirect(req.getContextPath() + visitLocate); // 打印查看,结束方法
System.out.println("响应,重定向地址 -> " + visitLocate);
return;
} // 转发使用频率非常的频繁
req.getRequestDispatcher(accessCoordinates).forward(req,resp);
System.out.println("请求,转发地址 -> " + accessCoordinates);
} else if (afterThings instanceof JsonResult) { // 响应给前端的数据会乱码,这里单独设置响应的编码
resp.setContentType("text/html;charset=UTF-8"); // 把数据对象交给FastJson处理成json字符串
String jsonString = JSON.toJSONString(afterThings); // 获取响应写入流,谁发出的请求,就响应写入给谁
PrintWriter writer = resp.getWriter(); // 写入Json字符串
writer.write(jsonString); // 释放资源
writer.close(); // 结束方法
return;
} } catch (Exception exception) {
exception.printStackTrace();
// req.setAttribute("errorMessage",exception);
// req.getRequestDispatcher("/一个访问错误的404页面").forward(req, resp);
}
} @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 代码可重用, GET请求 - doGet, POST请求 - doPost -> doGet
doGet(req, resp);
}
}
分页模型:
package cn.dzz.util.common; import javax.servlet.http.HttpServletRequest;
import java.util.List; /**
* @author DaiZhiZhou
* @file OA-Project-Phase1
* @create 2020-07-02 9:07
*
* 分页,翻页的模型对象
*/
public class Page<Entity> { // 总记录数量,如果是带有筛选条件查询的结果,此变量也应该是根据那个结果求出的总记录数量
private Long total; // 每页显示的记录数量
private Integer pageSize; // 总页数,使用上述的两个变量求得
private Integer totalPage; // 从第几页开始
private Integer pageIndex; // 对于不同的请求参数,我们需要动态的拼接处理传输,因此要求表单必须以Get方式发送
private String url; // 当前页的记录列表
private List<Entity> currentPageList; public Long getTotal() {
return total;
}
public Integer getPageSize() {
return pageSize;
}
public Integer getTotalPage() {
return totalPage;
}
public Integer getPageIndex() {
return pageIndex;
}
public String getUrl() {
return url;
}
public List<Entity> getCurrentPageList() {
return currentPageList;
} public void setTotal(Long total) {
this.total = total;
} public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
} // 获取总页数就是求余,不能整除的再加一页,关于数据类型转换,就这样
public void setTotalPage(Integer pageSize) {
Long totalPage =
total % pageSize == 0 ?
total / pageSize : total / pageSize + 1; this.totalPage = totalPage.intValue();
} public void setPageIndex(Integer pageIndex) {
this.pageIndex = pageIndex;
} public void setUrl(HttpServletRequest request) {
//String contextPath = request.getContextPath(); String queryString = request.getQueryString();
String servletPath = request.getServletPath(); if (queryString != null && !queryString.trim().isEmpty()) {
if (queryString.contains("&p=")) {
int index = queryString.indexOf("&p=");
queryString = queryString.substring(0,index);
}
} this.url = servletPath + "?" + queryString;
} public void setCurrentPageList(List<Entity> currentPageList) {
this.currentPageList = currentPageList;
}
}
公用的分页页面片段:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<div class="dataTables_paginate paging_simple_numbers"
id="DataTables_Table_0_paginate">
<a class="paginate_button previous disabled"
aria-controls="DataTables_Table_0" data-dt-idx="0" tabindex="0"
id="DataTables_Table_0_previous" href="<c:url value="${requestScope.page.url }"/>">首页</a>
<c:if test="${requestScope.page.pageIndex > 1 }">
<a class="paginate_button previous disabled"
aria-controls="DataTables_Table_0" data-dt-idx="0" tabindex="0"
id="DataTables_Table_0_previous" href="<c:url value='${requestScope.page.url }p=${requestScope.page.pageIndex - 1}'/>">上一页</a>
</c:if> <c:choose>
<c:when test="${requestScope.page.pageSize <= 10}">
<c:set var="begin" value="1" />
<c:set var="end" value="${requestScope.page.pageSize}" />
</c:when>
<c:otherwise>
<c:set var="begin" value="${requestScope.page.pageIndex-4 }" />
<c:set var="end" value="${requestScope.page.pageIndex + 5}" />
<c:choose>
<c:when test="${begin < 1}">
<c:set var="begin" value="1" />
<c:set var="end" value="10" />
</c:when>
<c:when test="${end > requestScope.page.pagecount }">
<c:set var="begin" value="${requestScope.page.pageSize - 9}" />
<c:set var="end" value="${requestScope.page.pageSize}" />
</c:when>
</c:choose>
</c:otherwise>
</c:choose> <c:forEach begin="${begin}" end="${end}" var="i">
<c:choose> <c:when test="${i == requestScope.page.pageIndex}" >${i}</c:when> <c:otherwise>
<span>
<a
class="paginate_button current"
aria-controls="DataTables_Table_0"
data-dt-idx="1"
tabindex="0"
href="<c:url value='${requestScope.page.url }p=${i}'/>"
>${i}
</a>
</span>
</c:otherwise>
</c:choose>
</c:forEach> <c:if test="${requestScope.page.pageIndex < requestScope.page.pageSize}">
<a
class="paginate_button next disabled"
aria-controls="DataTables_Table_0"
data-dt-idx="2"
tabindex="0"
id="DataTables_Table_0_next"
href="<c:url value='${requestScope.page.url }p=${requestScope.page.pageIndex + 1}'/>"
>下一页</a> </c:if>
<a
class="paginate_button next disabled"
aria-controls="DataTables_Table_0"
data-dt-idx="2"
tabindex="0"
id="DataTables_Table_0_next"
href="<c:url value='${requestScope.page.url }p=${requestScope.page.pageSize}'/>"
>尾页</a> </div>
其他一些
JdbcUtil 单连接对象获取,CRUD的操作封装,
TransactionManager 事务管理器,保证业务层的业务逻辑安全 各种数据源【连接池】的获取工具类,不详细赘述,百度吧
C3p0Util
HikariUtil
DruidUtil
关于使用事务执行Jbdc的概述:
数据源 - 事务管理器类 - JdbcUtil或者其他的SQL操作工具类
数据源:
- 连接参数的配置文件
- 对应的各种连接池组件【C3p0,Druid,Hikari,... ...】
hikari在连接参数上的问题得不到解决,所以放弃了,
事务调用事发生了找不到jdbc驱动的问题,可是hikari链接不允许设置驱动
事务管理器:
- 获取数据源,提供连接
- 只要能提供数据源,就应该和数据源解耦,注入谁的数据源都应该可以
- 但是我找不到合适的注入点位置,所以和数据源一样,采用了配置文件注入
- dataSource.propertis,利用反射动态注入
Jdbc的工具操作:
- 只要注入事务提供的数据源,才能够被动态事务代理
- 或者不需要事务,直接执行
五、前端模版:
H-ui Admin
http://www.h-ui.net/H-ui.admin.shtml
【Project】原生JavaWeb工程 01 概述,搭建的更多相关文章
- ] 解决myeclipse中新建javaweb工程,无法使用Web App Libraries问题
] 解决myeclipse中新建javaweb工程,无法使用Web App Libraries问题 标签: myeclipsejavawebWeb App Libraries 2013-10-16 1 ...
- idea新建javaweb工程
最近尝试了idea的使用,将idea建立javaweb工程的步骤记录下来 1.方框里边是重点 2.next后输入工程文件名点击finish 3.如图看到项目文件夹里边没有WEB-INF文件夹及里边的w ...
- 创建一个JavaWeb工程
1.用到的工具:eclipse编译器+Tomcat9,在自己电脑上已配置好jdk和tomcat的环境变量 2.新建一个project 2.选择web文件中的Dynamic Web project,进入 ...
- 问题总结:mysql和javaweb工程连接的过程中容易产生的问题
问题背景:自己在本机的mysql8瘫痪了,将Oracle中的数据迁移到mysql之后,配置好javaweb工程和虚拟机上的远程Mysql连接的文件之后:遇见了无法访问的问题 具体的配置: dataso ...
- maven实战(01)_搭建开发环境
一 下载maven 在maven官网上可下载maven:http://maven.apache.org/download.cgi 下载好后,解压.我的解压到了:D:\maven\apache-mave ...
- maven构建的模块化的JavaWeb工程
最近对maven构建的模块化的JavaWeb工程,比较感兴趣,所以自己就想从头弄一个出来,在此做一个记录,供以后学习. 前置条件:电脑上有eclipse(或者myeclipse,记事本也可以,那样就得 ...
- Linux环境下在Tomcat上部署JavaWeb工程
本文讲解如何将我们已经编译好的JavaWeb工程在Linux环境下的Tomcat上进行部署,总体上的思路是和Windows下JavaWeb项目部署到tomcat差不多,具体步骤和命令如下. 注:部署之 ...
- 如何将Javaweb工程的访问协议由http改为https及通过域名访问?
将javaweb工程的http访问协议更改为https,需要做一下几部操作: 通过jre生成证书 调整tomcat的配置 调整工程的web.xm配置 具体详细过程如下: 一.生成证书 打开cmd切换到 ...
- idea中创建maven的Javaweb工程并进行配置
学完maven后,可以创建maven的javaweb工程,在创建完成后还需要一些配置,下面来说下具体步骤,在这里我创建的是一个模块,创建web项目的方式和创建模块一样 1.创建一个模块,点new-Mo ...
- 普通 Javaweb项目模板的搭建
普通 Javaweb项目模板的搭建 1. 创建一个web项目模板的maven项目 2.配置 Tomcat 服务器 3.先测试一下该空项目 4.注入 maven 依赖 5.创建项目的包结构 6.编写基础 ...
随机推荐
- HiveSQL 工作实战总结
记录一些工作中有意思的统计指标,做过一些简化方便大家阅读,记录如有错误,欢迎在评论区提问讨论~ 问题类型 连续问题 两种思路 第一种:日期减去一列数字得出日期相同,主要是通过row_number窗口函 ...
- 如何简单实现suno-api账号保活
本文由 ChatMoney团队出品 简介 之前的一个简易的项目suno-api.是使用cookie来获取suno-token发起请求的,之前写的简单,并没有做cookie保活,在运行一段时间后cook ...
- C#.NET 逐行读取TXT文本
C#.NET 逐行读取TXT文本 using System; using System.IO; class Program { static void Main() { string filePath ...
- Android Studio 编译报错:download fastutil-7.2.0.jar
引用:https://www.cnblogs.com/caoxinyu/p/10568462.html build.gradle 可能有多个,一般在app 节点,默认里面不包含buildscript, ...
- 使用 ClickHouse 做日志分析
原作:Monika Singh & Pradeep Chhetri 这是我们在 Monitorama 2022 上发表的演讲的改编稿.您可以在此处找到包含演讲者笔记的幻灯片和此处的视频. 当 ...
- 怎么实现鼠标移入第i个li则对应显示第i个div,默认显示第一个LI
html 部分 <ul> <li>菜单1</li> <li>菜单2</li> <li>菜单3</li> <li ...
- STP选举规则
STP Election Process 802.1D STP 802.1D是第一个生成树版本,不支持VLAN.选举过程如下 选择一个根桥 选择根端口 选择指定端口 选择关闭端口(剩下的最后一个端 ...
- Prometheus监控系统(三)Prometheus与Grafana集成
1. Prometheus和Grafana集成 Grafana是一款采用Go语言编写的开源应用,主要用于大规模指标数据的可视化展现,是网络架构和应用分析中最流行的时序数据展示工具.目前已支持绝大部分常 ...
- 基于MCU的SD卡fat文件系统读写移植
背景 https://blog.csdn.net/huang20083200056/article/details/78508490 SD卡(Secure Digital Memory Card)具有 ...
- SNAT,DNAT以及REDIRECT转发详解
最近负责的其中一个项目的服务器集群出现了点网络方面的问题,在处理过程当中又涉及到了防火墙相关的知识和命令,想着有一段时间没有复习这部分内容了,于是借着此次机会复写了下顺便将本次复习的一些内容以博客的形 ...