【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.编写基础 ...
随机推荐
- 随机化 base64 加密
随机化 base64 加密 项目原址:Jeefy / jtim-enc · GitLab 技术 采用双重随机化技术. 第一重随机化是利用固定的随机种子改变 base64 解码的映射数组. 第二重随机化 ...
- WIn32 C++ 消息处理函数 问题
这个消息处理这个 Winproc 这个 接收到网络信息 在自己的函数用完后可以选择向系统路由传递这个网络消息接收到的数据原型 你处理完,系统也处理,不想让系统处理可以不将接受到的那几个变量啊数据啊,就 ...
- EF EntityFramework 强制从数据库中取数据,而不是上下文
场景:插入了一条数据到数据库,这条数据会有其它程序修改,接着程序想获取最新数据.此时不加额外处理,取的仍是旧的. t_task ta = new t_task(); ta.item_id = item ...
- Scrapy框架(七)--中间件及Selenium应用
中间件 下载中间件(Downloader Middlewares) 位于scrapy引擎和下载器之间的一层组件. 作用:批量拦截到整个工程中所有的请求和响应 - 拦截请求: - UA伪装:proces ...
- 工程数学 实验5-MATLAB最优化工具箱的使用
(1)线性规划应用案例的求解 1.基本要求 通过一个农业生产计划优化安排的实例求解,培养学生解决实际线性规划问题的初步能力:熟悉线性规划的建模过程:掌握Matlab优化工具箱中线性规划函数的调用. 2 ...
- Mysql RC/RR隔离原理和区别 不可重复读和可重复读
Mysql RC/RR隔离原理和区别 不可重复读和可重复读 mysql四种隔离级别:1.未提交读(READ UNCOMMITED)脏读2.已提交读 (READ COMMITED)简称(RC) 不可重复 ...
- 没有 Git,如何下载 Gitee 代码?
目录 没有 Git,如何下载 Gitee 代码? 注册 Gitee 账号 下载代码压缩包 没有 Git,如何下载 Gitee 代码? 鉴于看我博客的人很多都是大学本科生.非 CS 专业,大部分人都不会 ...
- Stable Diffusion 生成个性图片指南
在当今人工智能领域,midjourney无疑是生成图片的王者,但是苦于付费才能使用,今天我就给大家分享一下midjourney平替stable diffusion,实现本地生成不逊色于midjourn ...
- HiAI Foundation开发平台,加速端侧AI应用的智能革命
如果您是一名开发者,正在寻找一种高效.灵活且易于使用的端侧AI开发框架,那么HarmonyOS SDKHiAI Foundation服务(HiAI Foundation Kit)就是您的理想选择. 作 ...
- 广播变量的使用-----通过ip查询属于哪个省份
1,为什么要使用广播变量? 举一个简单的例子,我们要处理一份log文件,里面有ip地址. 20090121000132095572000|125.213.100.123|show.51.com|/sh ...