这个是用J2EE最开是练手项目做的一个无线点餐的项目,项目虽然小,但是简单的增删改查都有了,并且包含了前后台的项目,总的来说还是一个比较全的项目,用来练手非常不错,比较推荐新手试试这个项目,可以从中成长的非常多,废话不多说,先从后台开始说,一开始学Java,对JDBC直接操作数据库这块还是比较难理解的,这里我做了一个表,可以清晰的看出,使用Java编程语言是如何操作数据库的中间的parpareStatement就是个中转的东西,可以很方便的用来对数据库进行操作。
接下来 就是分层了,一开始说实话,我是不太明白分层的作用是做什么的,所用的东西放到一个main方法里不好么,其实所有的类都可以放到一个main,方法里,但是编译速度,和编译的效果都会大打折扣效果可以说是非常的不好,如果分层了,系统的可扩展性和可维护性都可以说是大大的增强。
这个点餐项目的分层结构也是比较合理的,先有餐桌出发,在进行的菜系,菜品,最后到订单,用户等,都是符合现实中的情况的
接下来,我来说一下我做这个点餐系统的需求,正常的开发流程,都是从需求分析-->概念设计--->详细设计-->编码设计-->测试维护等,其实最主要的还是一开始的需求分析,只有明白了自己要做什么东西,这个代码就很好写了,因为大部分的增删改查的套路模板都是固定的,只有熟悉其中的一个,剩下的大部分都复制粘贴
我要做什么——需求说明:
1、项目运行展示首页,将页面搭建起来(必须要先登录才可以进点餐系统首页)。
2、左侧餐桌管理
2.1增加
2.2删除、激活
2.3查:根据餐桌的名字、使用状态、是否已删除查询餐桌
3、左侧菜系管理
3.1增加
3.2删除、激活
3.3修改
3.4查:根据菜系的名字查询菜系
4、左侧菜品管理
4.1增加
4.2删除、激活
4.3修改
4.4查:根据菜品的名字查询菜品
5、登录和注册
5.1 登录拦截器——需1、2、3、4内的功能都需登录才可操作
5.2 记住密码 cookie
5.3 注册
怎么做?——技术选型:
请求 解析请求消息
浏览器 -------> 服务器 ---------------> 调用servlet<-------- <---------------返回消息 生成响应消息
分层的目的要注意降低开发过程中的耦合性
前端:css、javascript、jquery(js框架)
后端:jsp、Servlet(接入层)、jdbc
数据库:mysql
服务器:tomcat8.0
jsp的实质是servlet,而servlet是后端的技术,那么jsp应该属于后端
前台:呈现给用户的视觉和基本操作。包括前端和后端
后台:用户浏览网页时,用户看不到的后台数据跑动。包括前端和后端
前端:用户能够直观看到的内容都是前端做的,包括HTML、CSS,JavaScript等
后端:用户不能直接看到的内容都是后端做的,jsp、Servlet(接入层)、jdbc等
餐桌
CREATE TABLE `tb_dinner_table` (
`id` int(11) NOT NULL auto_increment,
`table_Name` varchar(20) default NULL COMMENT '餐桌名称',
`table_status` int(11) default '0' COMMENT '0 未使用 1正在使用',
`begin_use_date` datetime default NULL COMMENT '餐桌开始占用', 订单付款-保存付款时间,即客户用完餐的时间
`create_date` datetime default NULL COMMENT '餐桌的创建时间',
`update_date` datetime default NULL COMMENT '餐桌信息的修改的时间',
`update_user_id` int(11) default NULL COMMENT '餐桌信息的最后修改人',
`disabled` int(11) default '0' COMMENT '1为删除 0未删除',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
公司开发一般都是逻辑删除,通过一个字段的值去判断该行数据是否已经删除
菜品
CREATE TABLE `tb_food` (
`id` int(11) NOT NULL auto_increment,
`food_name` varchar(20) default NULL COMMENT '菜名',
`foodType_id` int(11) default NULL COMMENT '菜品类型id',
`price` double default NULL COMMENT '价格',
`discount` double default '1' COMMENT '折扣',
`remark` varchar(200) default NULL COMMENT '备注',
`img` varchar(100) default NULL COMMENT '菜的图片',
`create_date` datetime default NULL COMMENT '菜品创建时间',
`update_date` datetime default NULL COMMENT '菜品信息更新时间',
`disabled` int(11) default '0' COMMENT '0 未删 1已删',
PRIMARY KEY (`id`),
KEY `fk_food_foodType_id` (`foodType_id`),
CONSTRAINT `fk_food_foodType_id` FOREIGN KEY (`foodType_id`) REFERENCES `tb_food_type` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
多表关联查询:两个表进行左连接查询时,先找到表1的第一条记录,然后从头到尾扫描表2,将符合连接条件的记录与表1第一条记录连接成结果,
如表2没有匹配的结果,则该条查询的结果中表2的所有字段的数据为null;
数据结构更清晰,提高效率
菜品类型
CREATE TABLE `tb_food_type` (
`id` int(11) NOT NULL auto_increment,
`type_name` varchar(20) default NULL COMMENT '菜品类型名称',
`create_date` datetime default NULL COMMENT '菜品类型创建时间',
`update_date` datetime default NULL COMMENT '菜品类型更新时间',
`disabled` int(11) default '0' COMMENT '0未删 1已删',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
订单
CREATE TABLE `tb_order` (
`id` int(11) NOT NULL auto_increment,
`order_code` varchar(255) default NULL COMMENT '订单编码',
`table_id` int(11) default NULL COMMENT '订单所属餐桌',
`total_Price` double default NULL COMMENT '总价',
`order_Status` int(11) default '0' COMMENT '0已下单未付款 1已付款',
`order_Date` datetime default NULL COMMENT '下单时间',
`pay_date` datetime default NULL COMMENT '付款时间',
`update_date` datetime default NULL COMMENT '订单更新时间',
`disabled` int(11) default '0' COMMENT '0未删 1已删',
PRIMARY KEY (`id`),
KEY `order_table_id` (`table_id`),
CONSTRAINT `order_table_id` FOREIGN KEY (`table_id`) REFERENCES `tb_dinner_table` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
订单明细
CREATE TABLE `tb_order_detail` (
`id` int(11) NOT NULL auto_increment,
`orderId` int(11) default NULL COMMENT '所属订单id',
`food_id` int(11) default NULL COMMENT '菜品id',
`buyNum` int(11) default NULL COMMENT '够买数量',
`discount` double default NULL COMMENT '用户购买时的折扣',
`disabled` int(11) default '0' COMMENT '0 表示正常 1表示已删除',
PRIMARY KEY (`id`),
KEY `orderDetail_order_id` (`orderId`),
KEY `orderDetail_food_id` (`food_id`),
CONSTRAINT `orderDetail_food_id` FOREIGN KEY (`food_id`) REFERENCES `tb_food` (`id`),
CONSTRAINT `orderDetail_order_id` FOREIGN KEY (`orderId`) REFERENCES `tb_order` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
用户
CREATE TABLE `tb_user` (
`ID` int(11) NOT NULL auto_increment,
`LOGIN_NAME` varchar(50) default NULL COMMENT '登录名',
`PASSWORD` varchar(50) default NULL COMMENT '密码',
`EMAIL` varchar(50) default NULL COMMENT '邮箱',
`PHONE` varchar(20) default NULL COMMENT '电话',
`CREATE_DATE` datetime default NULL COMMENT '用户创建时间',
`DISABLED` tinyint(1) default '0' COMMENT '0 表示正常 1表示已删除',
PRIMARY KEY (`ID`),
UNIQUE KEY `LOGIN_NAME` (`LOGIN_NAME`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
项目搭建
a、创建web工程并初始化数据库
b、准备配置文件以及jar包
jar包:数据库驱动、c3p0,jstl相关的两个jar包
客户端操作数据库步骤
① 获取连接(数据库地址 用户名 密码)
② 准备SQL语句
③ 执行SQL
④ 获取执行后的结果
c、项目分层,包名(公司域名反写+项目名+包的作用)
接入层:org.wdl.hotel.sys.servlet
服务层:org.wdl.hotel.sys.service
持久层:org.wdl.hotel.sys.dao
实体层:org.wdl.hotel.sys.bean
工具类:org.wdl.hotel.sys.util
页面信息简介:
1)index.html首页,又分为上下左右页面
2)login.html登陆页面
3)register.html注册页面
4)dinnerTableList.html餐桌列表页面
5)dinnerTableAdd.html 餐桌添加页面
6)foodTypeList.html 菜系列表页面
7)foodTypeAdd.html 菜系添加页面
8)foodTypeUpdate.html 菜系更新页面
9)foodList.html 菜品列表页面
10)foodAdd.html 菜品添加页面
11)foodUpdate.html 菜品更新页面
12)orderList.html 订单列表页面
引入其他jsp页面
<%@ include file="common.jsp" %> 引入当前jsp所在的目录中的其他jsp
用于包含一个静态的资源,比如html文件,js文件、css样式文件等,
会将该文件中的内容拿到当前页面,然后一起进行编译
jsp:include:用于动态引入一个 JSP 页面,是编译后的页面,只要编译后的结果
· 展示首页
① jsp代码一般放在WEB-INF下面,因为WEB-INF中的内容是受保护的
原因:因为WebContent下面的内容可以直接访问,不安全
为什么先写接口,在写实现类?
不建立接口也能用,但是建立接口使程序更加灵活可规范开发
就像有一个接口animal,里面有个方法eat,然后你每一个接口animal的实现类都需要重写eat方法
继承只能单一继承 接口却可以多实现
面向接口开发。多人分模块开发时,写service(业务层)的人将接口定义好提交到SVN,其它层的人直接可以
调用接口方法,而写service层的人也可以通过实现类写具体方法逻辑。达到多人同时开发。
JDBC的事务
当你需要一次执行多条SQL语句时,可以使用事务。
通俗一点说,就是,如果这几条SQL语句全部执行成功,则才对数据库进行一次更新,
如果有一条SQL语句执行失败,则这几条SQL语句全部不进行执行,这时就需要用到事务。
Map集合
!=null 是判断Map是否为null(即是否new分配了内存空间,跟map中的键值对没有关系)
isEmpty()方法判断Map是否有内容(即new分配内存空间后,是否put了键值对,如果没有put就返回true,否则返回false)
项目的登录名和密码 怎么保存到 浏览器
浏览器 怎么将用户名和密码传递给 项目
★ Cookie是Web服务器发送到浏览器的简短文本信息,以后在访问同一个Web站点或域时浏览器就会毫无更改地返回该文本信息。用户可以决定是否接受Cookie。(数据存储在本地,降低服务器压力)
★ 一个Cookie由以下内容组成:
a. 名称;b.值;c. 一些操作属性:路径或所在域、有效期以及版本号。
★ cookie,服务器与浏览器交流的过程:
1.服务器返回的响应头中包含了Set-Cookie响应头,浏览器把该响应头中的内容以key-value对的方式保存。
1.1.服务端创建一个Cookie对象
1.2.把Cookie对象添加到响应对象中
增加一个Set-Cookie响应头
2.浏览器在访问服务器的时候,如果有该服务器对应的cookie,则把cookie读取出来,放到Cookie请求头中。
2.1.检查有没有当前服务器对应的Cookie
2.2.如果有,读取Cookie以键值对的方式在请求头中发送到服务器,增加一个Cookie请求头
★ Set-Cookie详解
name 必需。规定 cookie 的名称。
value 必需。规定 cookie 的值。
expire 可选。规定 cookie 的有效期。
path 可选。规定 cookie 的服务器路径。如果path=/Hotel,只有访问/Hotel下面的网页时,cookie才被发送。
★ cookie值保存中文可能会出现乱码
cookie的值如果是中文,需要在保存的时候进行编码,使用URLEncoder.encode()方法。
获取保存在cookie中的值时,使用URLDecoder.decode()方法来进行解码。
保存到cookie时先使用URLEncoder.encode(password, "utf-8")编码再保存到cookie,
读取出来的时候URLDecoder.decode() 解码。UTF-8把所有语言都统一到一套编码里
★ 以.action结尾的请求必须要登录才可以放行
以.do结尾的请求不需要登录就可放行
这个是项目的前台页面,当然访问地址因为我的Tomcat8080端口被占用了,所以我更新为了9999,这里建议大家用MyEclipse导入项目,可能会比较快一点,
前台的访问地址:http://localhost:9999/ZGZHote/app/index.do
前面的餐桌的显示显示的是未预定的餐桌信息,如果已经有用户预定了餐桌,那么该餐桌就不会显示出来
这个地方的餐桌状态使用的是AJAX对后台发发送了请求进行查询的,
当我们进来之后就可以进行点餐了,其中比较靠谱的是,你必须要先进行占位才能点餐,没有进行座位的话是无法进行点餐的,这点也符合现实中的情况,必须要有位子才能进行点餐。进行占位后,菜品就可以依次加入购物车了。
可以看出,这个加入购物车得这个状态得数量用JS控制,也是可以动态得增加得,加完之后,下明得数量就会进行直接得变化,总的来说还是比较适合这个系统得项目得。加入购物车之后我们开始进行下单操作,就可以达到结算得要求了。
这里得订单号是随机生成得,不会重复。这样就可以生成多笔订单而不会出现覆盖得情况。最后进行结算,该笔订单就会清空,并且对应得餐桌状态值也会进行修改。
下来是这个系统得后台管理系统---->访问地址是:http://localhost:9999/ZGZHote/sys/index.action
这里面可以实现对餐桌和菜系,菜品,订单得管理
菜系管理:
菜品管理:
订单管理:
其中得图片上传也是可以修改得,当然开发这个系统也遇到了一些问题,这里记录一些
(需求设计—1.数据库分析外键冲突)
根据需求分析,参考相关类目下的主要内容,分析结果,提炼得出主要的信息,在分析后根据需求,得出E-R图,并分析,主外建关系,根据这些图,在分析出表的字段的类型,采用何种类型,每个类型占用多少长度等,共需要几个表。在做这个阶段过程中,是比较费时间的因为在开发过程中,数据库的概要设计是基础部分,在分析表与表之间的关系的时候,很容易出错,有时候主外键关系没有理清,在进行后面的联合查询到的时候会出现很多错误,都是有外键冲突引起的,在设计的时候最麻烦的就是tb_order这个表和tb_order_detail这两个表最容易出现冲突,设计的时候也是非了很大劲,因为要根据订单总表去查询订单详情表,订单详情表里,有要显示菜品的名字,和餐桌的名字,这个又是一个才联合,在建这个表的时候,我在tb_order订单总表里设置了table_id这个字段,可以根据订单总表找到餐桌名字,而在tb_order_detail这个表里就需要两个外键了,因为订单详情表要显示菜品名和订单的详情,这个就需要在和订单ID和菜品ID关联起来,这个当时一开始是一个外键,后来发现在查看订单详情的时候只显示价格,没有菜品名字,后来也是发现一开始这个表就设计错了,导致了后面一系列的错误。在这个阶段中,我们主要确定了开发这个项目的需求,以及在设计这个项目中数据的字段的命名方面做出来,方便后面的实体类进行数据的存储。在选型方面,我们采用了B/S架构进行,没有采用C/S架构,主要原因是在做 窗体进行绘制的时候,窗体可以支持自定义的控件以及在数据的显示形式上面表现的比较单一,所以我们采用了B/S架构让浏览器做为数据的选型。并且采用了MVC的思想进行数据的分层,降低耦合,提高了各个模块之间的独立性,可以极大的提高编码的运行效率.
(页面选型—2.后台页面前台页面确定 CSS/JS路径错误)
在数据库设计完了之后,我们先开始了,后台的页面设计,所有的管理员的界面基本都类似,都是采用了frameSet这种方式在一个页面中显示独立的两块内容, 因为模块中的各个需求已经明确确定了,针对,菜品,菜系,订单,餐桌等的增改查,这里我没有进行删除,而且巧妙的运用了每个表中的一个disable字段进行了逻辑删除,为1就是删除,否则就是未删,这些在页面的绘制方面,我们也采用了统一的布局,采用了frameset框架集进行了分块展示,在这个方面,处理的比较简单,样式处理及布局方面都没有过太多的设计,在每个需要展示的页面中,都做了统一的样式,这样后台的页面的整体风格就显的比较统一了,在这个阶段中,我们根据了后端中,小frame中的选型列表,然后展现的页面,在这个部分中,因为涉及到表单的提交,在这个阶段过程中,我们就把每个表达的每个名字,已经input文本框中的每个字段的名字及时的确定了下来,方便后面的Serverlt编码中获取表单中的值进行处理,在后端操作数据库的操作中,因为Servelt要对数据库进行基本操作,其值最后也要在前端页面中要表示出来,所以这个命名必须的严格规范,之前设计的时候没有注意到这一点,导致后面做的时候变量名越来越来,自己做的什么都是设置玩后就忘了,要不就是重名的问题,所以这个后台后端的变量名也是根据前端的表单每个input的name值一并确定了下来.还有一个问题就是路径的问题,因为一开始在做HTML的时候没有什么问题,但是将HTML 转成JSP的时候,问题就出现了CSS,JS无效,其问题就是路径错误,我在网页项目中运用的是相对路径,但是跑到Web项目中相对路径无效,导致CSS布局无法体现出来,最后也是在前面加了PageConten.requestPat,定位成绝对路径,就把这个问题解决了,当然这个里面也用到了EL 表达式,如果路径错了后面前台页面就是错乱的布局.
后端逻辑—3.数据库操作 中文乱码404错误)
后台页面设计完之后,我们就开始着手做,后端部分的编码实现部分了,因为后台页面的表单的名字已经确定了下来,下面要做的就是,将表单提交给Tmocat服务器,服务器在去调用Servlet控制层,Servlet控制层,在调Sevice业务逻辑层,业务逻辑层在去调用持久层,因为前面两个阶段的内容已经把要实现和定义基本实现完了;在编码过程中,经过测试的时候也出现了一些问题,我在开发系统的本机过程中,可以正常运行,但是在换了一个环境后,程序会出现莫名其妙的BUG,后来发现了,其主要问题就是图片的路径问题,导致了字符串截取长度异常,所以会报500错误,后来发现在读取这个图片的时候,每次加载的时候也是这种问题,一开始的时候要后端要上传文件到upload这个文件,如果没有这个文件就新建一个这个文件,而后我吧这个语句放到了监听器里面,这样程序一运行就执行和这个程序,就解决了没有这个文件夹图片显示不出来,报500错误的问题,到底还是路径的问题,在上传图片的时候,这个问题也是比较麻烦的,如果上传同名图片会出现覆盖的问题所以我针对这个问题设置 了uuid这个是Java的唯一编码,这样就可以避免重复的问题了,在这个阶段中最主要的就是图片上传的问题,已经在前台前端中显示图片的问题,还有一个问题就是这个里面会出现中文乱码问题,当我在文本框输入值的时候会莫名其妙的变成乱码字符,一开始我用的是TomcaT7.0当换了8.0之后,问题还是没有解决,主要我表单的提交方式是GET 提交换成了POST提交之后,在后端的代码中设置req.setCharEncoding(“UTF-8”)之后这个乱码问题就解决了,之前一直以为是数据库的问题,发现这个也是在后端层面的问题.
(后端业务—4数据库操作 添加操作同名报错)
在执行添加操作的时候,不论是菜品,菜系,餐桌等等,在这些添加的过程中,按照正常的逻辑是不应该出现重名的,但是在设置的主键是表的ID ,我在数据库表设计的时候没有设计两个主键,所以在这个里面采用了AJAX这种异步的刷新方式,通过JS代码调用Servlet查询数据库,是否存在这个名字,存在就提示菜名已存在,请重新输入,当然这个异步刷新因为一开始我吧asyn这个值默认是true也就是异步刷新,但是根本出不来我想要的那种提示错误,菜品名不能提交到数据库里面,后来我发现了这种的只能用同步刷新,代码执行的同时,我设置了一个flag标志位,只有数据库返回为false 函数才继续往下执行,当为异步的时候,这个函数还没执行完,直接就跑去执行调用表单的操作了,导致这个功能就没有调用到,这个问题解决了之后,就可以实现了不能输同名菜品等问题,当然在注册里面也是用的这个方式。
(后端业务-5数据库操作 登录注册异常)
在前台的登录和后台的登录中,我都加了判断,但是执行的时候,JS 函数没执行判断我在JS 中一开始用的手机号等邮箱的检验,由于用手加的IF 判断这种比较老的方法,在执行的时候根本无效,所以在网上搜了正则,但是在用的时候同样也是出了问题,equals 和==的问题,equasl是对比字符的内容,==是对比的字符的地址,这个有导致了正则表达失效,并且在调用正则的时候也出现了路径的问题,最后这个问题也是得到了解决,其问题还是调用的方式出错了.
(后端业务—6购物车设计 存储异常)
购物车的设计是我在这个做这个项目中遇到的最难的问题,因为他的查询就是很费解的一个问题,要根据订单,去找餐桌,在根据订单要找到订单明细,订单明细要有菜品的名称和数量,但是这个数量我没有保存的数据库里,而是保存到session里面这个里面有有问题了,如果保存到数据库里,这个菜品的数量就是固定写死了,要不是就是频繁的对数据库执行更新操作,后来我发现了用Map
开发中的问题:
1. 菜系管理:
如果删除某个菜系:
先判断当前的菜系有没有被菜品引用,如果有不能删除!
如果当前菜系没有被菜品引用,可以删除!
2. 菜品更新时候,图片显示
存储时候,存储图片的名称;
显示时候, 拼接:
相对目录/图片名称
/项目名/目录/*.jpg
注意:
图片名称不能有特殊字符;这里我用的是UUID的方法解决的
图片名称不能太长
3. 编码
--数据库编码指定,必须要是Tomcat8.0且请求方式为Post请求才行
-程序要处理编码
GET/POST
-指定在创建连接的时候,想数据库发送sql语句采用的编码
jdbc:mysql:///hotel?useUnicode=true&characterEncoding=utf8
代码下载地址:https://download.csdn.net/download/zgz102928/12118018