基于Apache-DButils以及Druid(德鲁伊)与数据库交互实现的一个项目:满汉楼
每博一文案
张小贤曾说过: 你不过是做自己喜欢做的事,过自己喜欢过的生活。
若有人因为你喜欢做的事而觉得恶心和取笑你,那是他们的事。是呀,生活是苦,但也处处有光。
如果因别人无心的言语,而胡思乱想,因遇到不顺心的事而垂头丧气,那么无谓的烦恼
就会占据本就位置不大的心,让我们变得更难快乐。因此,真正智慧的人,学会了自己哄自己开心,
毕竟总有一阵风会吹走所有的烦恼,当下的一些困惑。今年以后回头看看,所谓烦恼亦不过如此。
宇宙何其浩瀚,星辰何其美妙。你何其的努力和善良。在这千姿百态的大千世界里,你值得更好的人。
也应该去做更棒的事,正如蔡澜先生所说,把生活的素质提高,今天活得比昨天快乐,明天又要比今天快乐,
就此而已。这就是人生的意义。活下去的真谛。失散的人,总有一天会在临夏重逢,错过的事终会以另外的方式
补偿。世事洪荒沧溟万里。走过去了,便上清水静,云淡风轻。余生学会自己,哄自己开心。
种自己的花,爱自己的宇宙。
—————— 一禅心灵庙语
文章目录
- 基于Apache-DButils以及Druid(德鲁伊)与数据库交互实现的一个项目:满汉楼
- 每博一文案
- 0. 创建项目的注意事项
- 1. 需求分析
- 2. 程序架构图创建
- 3. 具体代码实现
- 3.1 准备工作(导入相关的`jar`)
- 3.2 uitls (包) 相关工具类的实现
- 3.2.1 JDBCUtilsByDruid<T\>
- 3.2.2 Utility
- 3.3 javaBean (包) 有关ORM数据表的映射
- 3.3.1 class Employee
- 3.3.2 class Dining
- 3.3.3 class Menu
- 3.3.4 class Bill
- 3.4 dao (包)
- 3.4.1 EmployeeDAO
- 3.4.2 DiningDAO
- 3.4.3 MenuDAO
- 3.4.4 BillDAO
- 3.5 service (包)
- 3.5.1 EmployeeService
- 3.5.2 DiningService
- 3.5.3 MenuService
- 3.5.4 BillService
- 3.6 view (包)
- 3.6.1 MhlView
- 3.6.2 DiningView
- 3.6.3 MenuView
- 3.6.4 BillView
- 4. 总结:
- 5. 最后:
0. 创建项目的注意事项
- 首先我们要明白一点就是不存在完完全全没有
BUG
的项目,就像人无完人一样 我们只能不断地对项目进行优化,减少BUG 的出现,但是我们好像并不能完全的消除所有的BUG - 而消除bug 的最好的方式就是运行测试+调试
- 而我们在运行测试+调试 的时候,需要我们控制一定的量 ,我们需要把握住这个量 ,这一点是十分重要的,这样有助于我们快速的发现问题,也就是bug 的所在,从而提高我们的效率,减少我们的bug 的出现
- 具体的方法,我个人有以下建议:
- 首先我们需要对项目进行合理的分类,那个类,那个文件实现什么样的功能: 比如:一个类是用于定义什么类型,方法的 ,另一个类是用于什么头main 的,再另外一个类是用于对于什么功能上的实现的 ,合理的分类,可以提高我们项目的可读性,让我们自身不会因为自己随着代码量的增加,对应功能上的增加而导致我们越敲越乱,越写越蒙
- 对于量上我们可以,每实现了两三个 功能就运行测试看看,是否存在错误,如果存在出现了错误,我们可以在一定的量(范围内),快速的找出哪里出现了问题,从而对它进行调试 ,解决问题,而不是,把项目整体写完了或者是已经实现了好几十个功能,才开始运行测试项目,结果一运行,bug,警告 冒出一大坨,我们先不说,让不让人,抓头发,就是找出问题,恐怕都需要不时间上的浪费吧
- 对于代码上的注释,没事多写明白一点,或许有一天,你会感想曾经,那个写了注释的自己或同事
- 对于bug 不要害怕,一点一点的调试看看,找出问题的所在,慢慢来,要有耐心,要明白一点现在bug 写的多,以后bug 跟你说拜拜,现在bug ,不解决,bug 天天对你说 明天见
1. 需求分析
我们想要实现如下些功能:
- 一个简单的登入界面
- 登录满汉楼
- 退出满汉楼
- 登录成功后,一个后台的管理界面:
- 显示餐桌的状态
预定成功的餐桌状态:
点餐,吃饭的餐桌状态
- 预定餐桌
- 显示所有菜品
- 点餐服务
- 查看所有账单
- 结账
- 取消预定
- 退出满汉楼
2. 程序架构图创建
对应一个项目的创建,我们需要先拟定好,架构图,分好主干
,架构图拟定好了,主干清晰了,这样无论你怎么写,都不会太偏离主干 ,写的代码才不会乱,犯浑。干起来也不会太累。
我们需要分层处理,不同层,干不同的事情,分好包,再分好对应的类来。各司其职,就像 TCP七层协议一样,那一层该干什么就做什么事情,其他层的业务你不用管。如下是满汉楼的的 架构图
架构图 拟定好了,我们先根据架构图, 进行一个包类的划分分类
- view 界面:存放有关界面实现的代码
- sericve 业务:存放有关处理界面上对应功能的业务代码
- dao 存放处理业务中的 sql语句,以及其他功能代码
- javabean 存放有关 MySQL数据库创建的数据表中 ORM 映射的数据表对应的类
- utils 存放有关对应需要的工具类
我们根据架构图,向定义好,主干部分,再一点一点的添加细节
注意我们拟定架构时,是从上往下的,但是具体代码实现的时候是从下往上的去实现的
3. 具体代码实现
已经拟定好了架构图,下面我们就只需要,根据主干,编写,一点一点的添加细节,当你把所有的枝叶的细节编写好以后,基本上该项目就完成了。
3.1 准备工作(导入相关的jar
)
因为我们该项目是基于 Apache-DButils以及Druid(德鲁伊)
实现的,所以我们需要导入相关的 jar
包
如下:
commons-dbutils-1.3.jar
:Apache-dbutils 封装的工具类的包druid-1.1.10.jar
: Druid(德鲁伊) 数据库连接池mysql-connector-j-8.0.31.jar
: MySQL8.0
上述相关的jar
,我都托管到了 Github,Gitee 当中的了,如有需要的大家可以,自行下载
Mysql数据库驱动 · 彩虹海/软件和配置 - 码云 - 开源中国 (gitee.com)
software-and-configuration/Mysql数据库驱动 at master · China-Rainbow-sea/software-and-configuration (github.com)
最后是有关 Druild(德鲁伊)
数据库连接池的配置文件的编写,在src目录下创建一个名为druid.properties
的文件名 ,其中具体的参数编写如下:
driverClassName=com.mysql.cj.jdbc.Driver
#?rewriteBatchedStatements=true 批量处理打开
url=jdbc:mysql://localhost:3306/mhl?rewriteBatchedStatements=true
username=root
password=MySQL123
#初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
initialSize=10
#最小连接池数量
minIdle=5
#最大连接池数量
maxActive=20
#获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
maxWait=5000
#属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
filters=wall
#不要加空格以及;分号,因为无论是空格还是分号都会被识别成对象匹配。防止匹配失败
其中其他的配置参数,大家可以移步 🔜🔜🔜 (1条消息) c3p0,DBCP,Druid(德鲁伊)数据库连接池_ChinaRainbowSea的博客-CSDN博客 参考
3.2 uitls (包) 相关工具类的实现
uitls 该包下存放:所有有关工具类的代码实现
3.2.1 JDBCUtilsByDruid<T>
该类定义的是一个abstract
抽象类,主要是: 创建 Druid (德鲁伊)数据库连接池,利用Apache-DButils中的 QueryRunner类方法ResultSetHandler接口,对数据表的一个增,删,改,查的操作的封装
想要进一步了解 Apache-DButils和Druid (德鲁伊)
的,可以移动到 🔜🔜🔜 c3p0,DBCP,Druid(德鲁伊)数据库连接池_ChinaRainbowSea的博客-CSDN博客 以及 Apache-DBUtils实现CRUD操作_ChinaRainbowSea的博客-CSDN博客
具体代码如下:
package Blogs.blogs5.com.RainbowSea.mhl.utils;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.apache.commons.dbutils.DbUtils;
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 javax.sql.DataSource;
import java.io.File;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.Properties;
/**
* 通过数据表的增,删,改,差的封装处理
* 使用 druid数据库连接池,dbutils工具类
* 定义 为 abstract 抽象类,不可 new
* @param <T> 泛型
*/
public abstract class JDBCUtilsByDruid<T> {
// 数据库连接池对象
private static DataSource dataSource = null;
// 定义 dbutils 工具类当中的 QueryRunner 对象执行sql语句
private QueryRunner queryRunner = new QueryRunner(); // 作为类属性存在
// 一个应用一个数据库连接池(数据库连接池比作生产连接的工厂,)static 静态代码块和类一起加载到内存当中,
// 仅仅执行一次,所有对象共用
static {
try {
// 读取对应目录下的配置文件信息
FileInputStream is = new FileInputStream(new File("src/druid.properties"));
Properties properties = new Properties();
properties.load(is); // 以简单的线性格式读取属性列表(关键字/元素对)
// 传入读取到配置文件的对象,创建 druid 数据库连接池
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
throw new RuntimeException(e); // 将编译异常转换为运行异常抛出
}
}
/**
* 获取到所创建的druid数据库连接池,其中的一个连接对象
* @return Connection
*/
private static Connection getDruidConnection() {
try {
return dataSource.getConnection();
} catch (SQLException e) {
throw new RuntimeException(e); // 将编译异常转换为运行异常抛出
}
}
/**
* 对数据表进行增删改操作
* @param sql 要执行是sql语句
* @param args 可变参数(填充占位符),可以不传参数,但不要传null,防止null引用
* @return int 影响数据库的行数
*/
public int update(String sql,Object...args) {
// 获取到 druid 数据库连接池其中的一个连接对象
Connection connection = getDruidConnection();
// 通过 dbutils工具类QueryRunner获取到操作数据库的对象,并执行sql语句
try {
int count = queryRunner.update(connection, sql, args);
return count;
} catch (SQLException e) {
throw new RuntimeException(e); // 将编译异常转换为运行异常抛出
} finally {
// 关闭资源,归还连接
close(connection,null,null);
}
}
/**
* 处理单条记录的查询结果集:
* 使用 dbutils中的ResultSetHandler的BeanHandler(把当前select查询到的第一行的记录,存储到javabean实例中(ORM映射)对象)
* @param sql 执行的sql语句
* @param clazz 泛型:javaBean实例对象(ORM映射)
* @param args 可变参数(填充占位符)
* @return 泛型:返回的javaBean实例对象(ORM映射)
*/
public T selectSingle(String sql,Class<T> clazz,Object...args) {
// 获取从 druid 数据库连接池中获取到其中的一个连接对象
Connection connection = getDruidConnection();
try {
// 创建 javaBean 实例的存储方式,
BeanHandler<T> beanHandler = new BeanHandler<T>(clazz);
// 执行使用 dbutils中的QueryRunner类中封装的 query方法执行sql语句,以及处理select
T t = queryRunner.query(connection, sql, beanHandler, args);
return t;
// 可以将 BeanHandler 存储方式和 QueryRunner.query执行sql语句一体化
// T t2 = queryRunner.query(connection, sql, new BeanHandler<T>(clazz), args);
} catch (SQLException e) {
throw new RuntimeException(e); // 将编译异常转换为运行异常抛出
} finally {
// 关闭资源,归还连接
close(connection,null,null);
}
}
/**
* 查询数据表中多条记录,
* 使用dbutils中的ResultSetHandler的BeanListHandler(查询所有记录,每一行记录存储到javabean实例对象(ORM映射)中,
* 再将每个JavaBean实例对象存储到 List链表当中)
* @param sql 执行的sql语句
* @param clazz 泛型,JavaBean类
* @param args 可变参数(填充占位符),可以不传参数,但不要传null
* @return List<T> 泛型链表
*/
public List<T> selectMany(String sql, Class<T> clazz, Object...args) {
// 获取到druid 数据库连接池中的一个连接对象
Connection connection = getDruidConnection();
try {
// 创建存储select结果集的形式
BeanListHandler<T> beanListHandler = new BeanListHandler<T>(clazz);
// 使用dbutils工具类的/QueryRunner.query()方法执行sql语句,并处理select
List<T> list = queryRunner.query(connection, sql, beanListHandler, args);
return list;
// 可以将 BeanListHandler<T> 存储方式和 QueryRunner.query执行sql语句一体化
// List<T> list1 = queryRunner.query(connection, sql, new BeanListHandler<T>(clazz), args);
} catch (SQLException e) {
throw new RuntimeException(e); // 将编译异常转换为运行异常抛出
} finally {
// 关闭资源,归还连接
close(connection,null,null);
}
}
/**
* 查询特殊的结果 比如 count(),sum()
* 使用dbutils中的ResultSetHandler的ScalarHandler(查询单个特殊的对象)
* @param sql 执行的sql语句
* @param args 可变参数(填充占位符),可以不传参数,但不要传null
* @return Object 查询单个特殊的对象比如:count(),sum()
*/
public Object selectSum(String sql,Object...args) {
// 获取druid数据库连接池的其中的一个连接对象
Connection connection = getDruidConnection();
try {
// 定义存储方式为: ScalarHandler
ScalarHandler scalarHandler = new ScalarHandler();
// 直接使用 dbutils中的QueryRunner()中的query,执行sql并处理select
Object o = queryRunner.query(connection, sql, scalarHandler, args);
return o;
// 可以将 ScalarHandler 存储方式和 QueryRunner.query执行sql语句一体化
// Object O2 = queryRunner.query(connection, sql, new ScalarHandler(), args);
} catch (SQLException e) {
throw new RuntimeException(e); // 将编译异常转换为运行异常抛出
} finally {
// 关闭资源,归还连接
close(connection,null,null);
}
}
/**
* 关闭连接,关闭资源("数据库连接池归还连接")
* 最晚使用的资源,最先关闭
* 使用 DbUtils.closeQuietly() 封装的静态方法
* @param connection 连接
* @param statement 操作
* @param resultSet 查询
*/
public static void close(Connection connection, Statement statement, ResultSet resultSet) {
// 关闭处理 select 查询结果集资源
DbUtils.closeQuietly(resultSet);
// 关闭操作数据库对象资源
DbUtils.closeQuietly(statement);
// 关闭连接,归还连接
DbUtils.closeQuietly(connection);
}
}
3.2.2 Utility
该类主要是: 用来对用户输入的内容的处理,并且能够按照程序员的需求,得到用户的控制台输入,比如: 输入的必须是整数类型,不可以是字符串。
具体代码实现如下:
package Blogs.blogs5.com.RainbowSea.mhl.utils;
/**
工具类的作用:
处理各种情况的用户输入,并且能够按照程序员的需求,得到用户的控制台输入。
*/
import java.util.Scanner;
/**
*/
public class Utility {
//静态属性。。。
private static Scanner scanner = new Scanner(System.in);
/**
* 功能:读取键盘输入的一个菜单选项,值:1——5的范围
* @return 1——5
*/
public static char readMenuSelection() {
char c;
for (; ; ) {
String str = readKeyBoard(1, false);//包含一个字符的字符串
c = str.charAt(0);//将字符串转换成字符char类型
if (c != '1' && c != '2' &&
c != '3' && c != '4' && c != '5') {
System.out.print("选择错误,请重新输入:");
} else break;
}
return c;
}
/**
* 功能:读取键盘输入的一个字符
* @return 一个字符
*/
public static char readChar() {
String str = readKeyBoard(1, false);//就是一个字符
return str.charAt(0);
}
/**
* 功能:读取键盘输入的一个字符,如果直接按回车,则返回指定的默认值;否则返回输入的那个字符
* @param defaultValue 指定的默认值
* @return 默认值或输入的字符
*/
public static char readChar(char defaultValue) {
String str = readKeyBoard(1, true);//要么是空字符串,要么是一个字符
return (str.length() == 0) ? defaultValue : str.charAt(0);
}
/**
* 功能:读取键盘输入的整型,长度小于2位
* @return 整数
*/
public static int readInt() {
int n;
for (; ; ) {
String str = readKeyBoard(2, false);//一个整数,长度<=2位
try {
n = Integer.parseInt(str);//将字符串转换成整数
break;
} catch (NumberFormatException e) {
System.out.print("数字输入错误,请重新输入:");
}
}
return n;
}
/**
* 功能:读取键盘输入的 整数或默认值,如果直接回车,则返回默认值,否则返回输入的整数
* @param defaultValue 指定的默认值
* @return 整数或默认值
*/
public static int readInt(int defaultValue) {
int n;
for (; ; ) {
String str = readKeyBoard(10, true);
if (str.equals("")) {
return defaultValue;
}
//异常处理...
try {
n = Integer.parseInt(str);
break;
} catch (NumberFormatException e) {
System.out.print("数字输入错误,请重新输入:");
}
}
return n;
}
/**
* 功能:读取键盘输入的指定长度的字符串
* @param limit 限制的长度
* @return 指定长度的字符串
*/
public static String readString(int limit) {
return readKeyBoard(limit, false);
}
/**
* 功能:读取键盘输入的指定长度的字符串或默认值,如果直接回车,返回默认值,否则返回字符串
* @param limit 限制的长度
* @param defaultValue 指定的默认值
* @return 指定长度的字符串
*/
public static String readString(int limit, String defaultValue) {
String str = readKeyBoard(limit, true);
return str.equals("")? defaultValue : str;
}
/**
* 功能:读取键盘输入的确认选项,Y或N
* 将小的功能,封装到一个方法中.
* @return Y或N
*/
public static char readConfirmSelection() {
char c;
for (; ; ) {//无限循环
//在这里,将接受到字符,转成了大写字母
//y => Y n=>N
String str = readKeyBoard(1, false).toUpperCase(); // toUpperCase()小写转为大写字母
c = str.charAt(0);
if (c == 'Y' || c == 'N') {
break;
} else {
System.out.print("选择错误,请重新输入:");
}
}
return c;
}
/**
* 功能: 读取一个字符串
* @param limit 读取的长度
* @param blankReturn 如果为true ,表示 可以读空字符串。
* 如果为false表示 不能读空字符串。
*
* 如果输入为空,或者输入大于limit的长度,就会提示重新输入。
* @return
*/
private static String readKeyBoard(int limit, boolean blankReturn) {
//定义了字符串
String line = "";
//scanner.hasNextLine() 判断有没有下一行
while (scanner.hasNextLine()) {
line = scanner.nextLine();//读取这一行
//如果line.length=0, 即用户没有输入任何内容,直接回车
if (line.length() == 0) {
if (blankReturn) return line;//如果blankReturn=true,可以返回空串
else continue; //如果blankReturn=false,不接受空串,必须输入内容
}
//如果用户输入的内容大于了 limit,就提示重写输入
//如果用户如的内容 >0 <= limit ,我就接受
if (line.length() < 1 || line.length() > limit) {
System.out.print("输入长度(不能大于" + limit + ")错误,请重新输入:");
continue;
}
break;
}
return line;
}
}
3.3 javaBean (包) 有关ORM数据表的映射
javaBean 包下存放:所有有关 数据表的 javaBean 实例类,数据表的 ORM 映射。
ORM映射:
- 一张数据表 对应一个Java当中的类
- 数据表中的一行记录 对应Java当中的一个对象
- 数据表中的字段 对应Java当中的一个 属性
首先我们需要一个关于 满汉楼这个项目的 数据库 名为 mhls
,执行下面的 sql 语句,创建该数据库
CREATE DATABASE mhls;
3.3.1 class Employee
首先我们创建名为 employee
员工表:含有(编号主键,员工编号,员工姓名,员工密码,员工的职务),其中员工密码使用 md5()
函数加密。注意md5()加密的信息,需要通过md5()解密才能查询到
执行如下 SQL 语句,创建 employee 数据表
CREATE TABLE employee (
id INT PRIMARY KEY AUTO_INCREMENT, # 主键编号
employee_id INT NOT NULL DEFAULT 0, # 员工编号 default 默认值
employee_name VARCHAR(30) NOT NULL DEFAULT '', # 员工名
employee_pwd CHAR(32) NOT NULL DEFAULT '', # 密码,定义为 char(32) 用于后面的md5加密需要
employee_job VARCHAR(30) NOT NULL DEFAULT '' # 员工职务
)
同时为该 employee 数据表插入相关数据如下:执行如下 SQL 语句
# 为 employee 员工表插入信息
INSERT INTO employee(employee_id,employee_name,employee_pwd,employee_job)
VALUES(20221100,'路明非',MD5('112358'),'经理'),
(20221101,'路明泽',MD5('985'),'副总经理'),
(20221102,'楚子航',MD5('211'),'厨师'),
(20221104,'凯撒',MD5('666'),'收银员'),
(20221105,'芬格尔',MD5('123'),'服务员');
补充 : md5()
函数加密
如下被 md5()加密的信息,如果直接使用插入时 md5()加密的内容,是无法查询到结果了,这里我们 WHERE employee_pwd = '985'
条件查询,如下可以看到查询不到结果
SELECT *
FROM employee
WHERE employee_pwd = '985'; # 没有使用 md5()解密查询不到
而我们再使用上 md5()
函数解密后,再查询,就可以查询到结果了。,如下
SELECT *
FROM employee
WHERE employee_pwd = MD5('985'); # 使用 md5()解密后,查询得到
根据 employee 员工表结构创建对应的 javaBean 实例,注意数据库的命名格式是下划线风格,而Java当中是小驼峰风格,两者是不同的,在处理 select 查询结果集上,使用上别名,对应上Java中的属性名 。不然会赋值失败。
如下
具体代码如下:
注意定义的 javaBean 实例类中必须要含有 无参构造器
, 以及相关的set\get的方法
,因为 Apache-dbutils 的底层需要使用其中无参构造器以及set/get 方法进行赋值,取值的操作
package Blogs.blogs5.com.RainbowSea.mhl.javaBean;
/**
* 对应 employee 员工数据表
*/
public class Employee {
private int id; // 主键编号
private int employeeId; // 员工ID
private String employeeName; // 员工姓名
private String employeePwd; // 员工密码
private String employeeJod; // 员工的职务
public Employee() {
// 无参构造器必须要有,用于 apche-dbutils 底层的调用
}
public Employee(int id, int employeeId, String employeeName, String employeePwd, String employeeJod) {
this.id = id;
this.employeeId = employeeId;
this.employeeName = employeeName;
this.employeePwd = employeePwd;
this.employeeJod = employeeJod;
}
// set/get 同样必须要有用于apche-dbtuils底层的反射的调用赋值,
public void setId(int id) {
this.id = id;
}
public int getId() {
return this.id;
}
public int getEmployeeId() {
return employeeId;
}
public void setEmployeeId(int employeeId) {
this.employeeId = employeeId;
}
public String getEmployeeName() {
return employeeName;
}
public void setEmployeeName(String employeeName) {
this.employeeName = employeeName;
}
public String getEmployeePwd() {
return employeePwd;
}
public void setEmployeePwd(String employeePwd) {
this.employeePwd = employeePwd;
}
public String getEmployeeJod() {
return employeeJod;
}
public void setEmployeeJod(String employeeJod) {
this.employeeJod = employeeJod;
}
}
3.3.2 class Dining
首先创建一个名为 dining (餐桌的数据表),含有 (餐桌编号,餐桌的状态,用户名,用户的联系电话)
执行如下 sql 语句
# 创建餐桌表
CREATE TABLE dining (
dining_id INT PRIMARY KEY AUTO_INCREMENT, # 餐桌位
dining_state VARCHAR(20) NOT NULL DEFAULT '', # 餐桌的状态
order_name VARCHAR(20) NOT NULL DEFAULT '', # 用户名
order_tel VARCHAR(20) NOT NULL DEFAULT '' # 用户的电话
);
为餐桌表插入数据,执行如下 sql语句的代码
# 为餐桌表插入数据
INSERT INTO dining(dining_state,order_name,order_tel)
VALUES ('空','',''),
('空','',''),
('空','',''),
('空','',''),
('空','',''),
('空','','');
根据如上 dining 数据表结构,创建与之相关 javaBean 实例类。注意数据库命名格式和Java中的不同
package Blogs.blogs5.com.RainbowSea.mhl.javaBean;
/**
* 对应 dining 餐卓数据表
*/
public class Dining {
private int diningId; // 餐桌位的编号
private String diningState; // 餐桌的状态
private String orderName; // 预定该餐桌的用户名
private String orderTel; // 预定该餐桌的联系电话
public Dining() {
//必须 创建无参构造器,用于apche-dbutils底层的使用
}
public Dining(int diningId, String diningState, String orderName, String orderTel) {
this.diningId = diningId;
this.diningState = diningState;
this.orderName = orderName;
this.orderTel = orderTel;
}
// 必须创建ser/get 用于apche-dutils底层的反射赋值,取值
public int getDiningId() {
return diningId;
}
public void setDiningId(int diningId) {
this.diningId = diningId;
}
public String getDiningState() {
return diningState;
}
public void setDiningState(String diningState) {
this.diningState = diningState;
}
public String getOrderName() {
return orderName;
}
public void setOrderName(String orderName) {
this.orderName = orderName;
}
public String getOrderTel() {
return orderTel;
}
public void setOrderTel(String orderTel) {
this.orderTel = orderTel;
}
@Override
public String toString() {
return diningId +
"\t\t\t" + diningState +
"\t\t" + orderName +
"\t\t" + orderTel;
}
}
3.3.3 class Menu
创建一个 名为 menu
的菜谱 的数据表,该数据表含有 (菜的编号主键,菜名,菜的种类,菜的单价),
执行如下 SQL 语句创建
# 创建 菜谱数据表
CREATE TABLE menu(
menu_id INT PRIMARY KEY AUTO_INCREMENT, # 菜名编号
menu_name VARCHAR(50) NOT NULL DEFAULT '', # 菜名
menu_type VARCHAR(30) NOT NULL DEFAULT '', # 菜的种类
menu_price DOUBLE NOT NULL DEFAULT 0 # 该菜的价格
);
创建好后,为该 menu 菜谱数据表,插入一些数据,执行如下 SQL 语句
# 为菜谱插入数据
INSERT INTO menu(menu_name,menu_type,menu_price)
VALUES('八宝饭','主食',10),
('叉烧包','主食',20),
('宫保鸡丁','热菜',30),
('山药鱼','凉菜',14),
('银丝卷','甜食',9),
('水煮鱼','热菜',26),
('甲鱼汤','汤菜',100);
根据该 menu 数据表结构,创建定义 相关的 javaBean 实例类(注意MySQL数据库命名格式与Java的不同)
如下代码:
package Blogs.blogs5.com.RainbowSea.mhl.javaBean;
/**
* 对应menu 菜谱数据表
*/
public class Menu {
private int menuId; // 菜的编号,主键
private String menuName; // 菜名
private String menuType; // 菜的种类
private double menuPrice; // 菜的单价
public Menu() {
// 定义无参构造器,用于apache-dbuitls的底层调用
}
public Menu(int menuId, String menuName, String menuType, double menuPrice) {
this.menuId = menuId;
this.menuName = menuName;
this.menuType = menuType;
this.menuPrice = menuPrice;
}
// 定义 set/get 用于 apache-dbutils底层的反射赋值,取值操作
public int getMenuId() {
return menuId;
}
public void setMenuId(int menuId) {
this.menuId = menuId;
}
public String getMenuName() {
return menuName;
}
public void setMenuName(String menuName) {
this.menuName = menuName;
}
public String getMenuType() {
return menuType;
}
public void setMenuType(String menuType) {
this.menuType = menuType;
}
public double getMenuPrice() {
return menuPrice;
}
public void setMenuPrice(double menuPrice) {
this.menuPrice = menuPrice;
}
@Override
public String toString() {
return menuId +
"\t\t\t" + menuName +
"\t\t" + menuType +
"\t\t"+ menuPrice;
}
}
3.3.4 class Bill
创建一个名为 bill
的 账单的数据表,其中有(账单的编号,主键,账单号,生成的订单日期,餐桌位,菜的编号,菜名,菜的个数,当前菜的总价(数量*单价),当前餐桌账单的状态 '未支付,支付方式 ) ,执行如下 SQL 语句创建
# 创建账单数据表
CREATE TABLE bill(
id INT PRIMARY KEY AUTO_INCREMENT, # 账单的编号,主键
bill_id VARCHAR(50) NOT NULL UNIQUE DEFAULT '', # 账单号,unique 唯一约束,可以根据自己的uuid规则定义
bill_date DATETIME NOT NULL, # 生成的订单日期
dining_id INT NOT NULL DEFAULT 0, # 餐桌位
menu_id INT NOT NULL DEFAULT 0, # 菜的编号
menu_name VARCHAR(50) NOT NULL DEFAULT '', # 菜名
menu_nums INT NOT NULL DEFAULT 0, # 菜的个数
menu_money DOUBLE NOT NULL DEFAULT 0, # 当前菜的总价(数量*单价)
bill_state VARCHAR(30) NOT NULL DEFAULT '' # 当前餐桌账单的状态 '未支付,支付方式 微信/支付宝/现金/银行卡'
);
根据 bill 账单数据表创建 相关的 javaBean 实例类,这里我们,多附加上一个菜的单价值属性
,通过多表查询的方式 。对于多表连接查询,映射 javaBean 的具体方法讲解。大家可以移动到 🔜🔜🔜 Apache-DButils以及Druid(德鲁伊) 多表连接查询的解决方案:两种_ChinaRainbowSea的博客-CSDN博客 了解一下。
需要注意的是 MySQL数据库中的 datetime
日期时间类型,在Java当中的 date
无法兼容,所以这里对于上面的 datetime 日期时间类型,我们使用Java当中的String 字符串替换
package Blogs.blogs5.com.RainbowSea.mhl.javaBean;
/**
* 对应 bill 账单数据表
*/
public class Bill {
private int id; // 账单ID,主键
private String billId; // 账单号
private String billDate; // 账单生成时间,数据库中datetime与Java中的date无法兼容
private int diningId; // 餐桌位
private int menuId; // 菜编号
private String menuName; // 菜名
private int menuNums; // 菜的个数
private double menuMoney; // 当前菜的总价(数量*单价)
private String billState; // 当前账单的状态(未结账,微信/支付宝/银行卡/现金)
private double menuPrice; // 当前菜的单价,额外定义的用于多表查询
public Bill() {
//必须 创建无参构造器,用于apche-dbutils底层的使用
}
public Bill(int id, String billId, String billDate, int diningId, int menuId, String menuName, int menuNums, double menuMoney, String billState, double menuPrice) {
this.id = id;
this.billId = billId;
this.billDate = billDate;
this.diningId = diningId;
this.menuId = menuId;
this.menuName = menuName;
this.menuNums = menuNums;
this.menuMoney = menuMoney;
this.billState = billState;
this.menuPrice = menuPrice;
}
// 必须创建ser/get 用于apche-dutils底层的反射赋值,取值
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getBillId() {
return billId;
}
public void setBillId(String billId) {
this.billId = billId;
}
public String getBillDate() {
return billDate;
}
public void setBillDate(String billDate) {
this.billDate = billDate;
}
public int getDiningId() {
return diningId;
}
public void setDiningId(int diningId) {
this.diningId = diningId;
}
public int getMenuId() {
return menuId;
}
public void setMenuId(int menuId) {
this.menuId = menuId;
}
public String getMenuName() {
return menuName;
}
public void setMenuName(String menuName) {
this.menuName = menuName;
}
public int getMenuNums() {
return menuNums;
}
public void setMenuNums(int menuNums) {
this.menuNums = menuNums;
}
public double getMenuMoney() {
return menuMoney;
}
public void setMenuMoney(double menuMoney) {
this.menuMoney = menuMoney;
}
public String getBillState() {
return billState;
}
public void setBillState(String billState) {
this.billState = billState;
}
public double getMenuPrice() {
return menuPrice;
}
public void setMenuPrice(double menuPrice) {
this.menuPrice = menuPrice;
}
@Override
public String toString() {
return id +
"\t\t" + billDate +
"\t\t\t" + diningId +
"\t\t\t" + menuId +
"\t\t\t" + menuName +
"\t\t" + menuNums +
"\t\t\t" + menuPrice +
"\t\t" + menuMoney +
"\t\t" + billState;
}
}
3.4 dao (包)
dao 该包存放:所有有关数据表中的 DAO ‘增删改查’ 的实现代码
3.4.1 EmployeeDAO
EmployeeDAO 通过继承 JDBCUtilsByDruid<T>
抽象类,调用其中父类中的方法可以对 employ员工表
数据表进行“增删改查”的操作。
其中子类独有的方法也可以自行添加上去。
package Blogs.blogs5.com.RainbowSea.mhl.dao;
import Blogs.blogs5.com.RainbowSea.mhl.javaBean.Employee;
import Blogs.blogs5.com.RainbowSea.mhl.utils.JDBCUtilsByDruid;
/**
* 通过继承 JDBCUtilsByDruid<T> 抽象类方法,调用其中的方法
* 对 employee (员工)数据表进行一个基本的增删改查的操作,
* 也可以附加其他子类独有的方法
*/
public class EmployeeDAO extends JDBCUtilsByDruid<Employee> {
}
3.4.2 DiningDAO
DiningDAO 通过继承 JDBCUtilsByDruid<T>
抽象类,调用其中父类中的方法可以对 dining 餐桌表
数据表进行“增删改查”的操作。
其中子类独有的方法也可以自行添加上去。
package Blogs.blogs5.com.RainbowSea.mhl.dao;
import Blogs.blogs5.com.RainbowSea.mhl.javaBean.Dining;
import Blogs.blogs5.com.RainbowSea.mhl.utils.JDBCUtilsByDruid;
/**
* 继承JDBCUtilsByDruid<T> 父类,通过调用其父类中的方法
* 对 dining数据表进行一个“增删改查 ”操作
*/
public class DiningDAO extends JDBCUtilsByDruid<Dining> {
// 其他子类独有的方法可以自行添加
}
3.4.3 MenuDAO
MenuDAO 通过继承 JDBCUtilsByDruid<T>
抽象类,调用其中父类中的方法可以对 menu 餐桌表
数据表进行“增删改查”的操作。
其中子类独有的方法也可以自行添加上去。
package Blogs.blogs5.com.RainbowSea.mhl.dao;
import Blogs.blogs5.com.RainbowSea.mhl.javaBean.Menu;
import Blogs.blogs5.com.RainbowSea.mhl.utils.JDBCUtilsByDruid;
/**
* 通过继承 父类 JDBCUtilsByDruid<T> 调用其中父类的方法
* 对 menu 菜谱数据表进行一个“增删改查”的操作
*/
public class MenuDAO extends JDBCUtilsByDruid<Menu> {
}
3.4.4 BillDAO
BillDAO 通过继承 JDBCUtilsByDruid<T>
抽象类,调用其中父类中的方法可以对 bill 账单表
数据表进行“增删改查”的操作。
其中子类独有的方法也可以自行添加上去。
package Blogs.blogs5.com.RainbowSea.mhl.dao;
import Blogs.blogs5.com.RainbowSea.mhl.javaBean.Bill;
import Blogs.blogs5.com.RainbowSea.mhl.utils.JDBCUtilsByDruid;
/**
* 继承JDBCUtilsByDruid<T> 父类,通过调用其父类中的方法
* 对 bill 账单数据表进行一个“增删改查 ”操作
*/
public class BillDAO extends JDBCUtilsByDruid<Bill> {
//其中子类独有的方法也可以自行添加上去。
}
3.5 service (包)
service 该包存放:所有有关不同数据表的业务处理的代码实现。
3.5.1 EmployeeService
EmployeeService 该类作用统一处理有关 employee 员工数据表的一些业务的操作。所有有关 employee 员工表的操作都在这里实现:
实现如下业务:
- 验证满汉楼的登录操作:通过用户名和密码,查询其中的用户信息
需要注意的是: 这里我们插入 employee 员工数据表的密码是被 md5()
函数加密的,如果直接输入密码是无法查询到了,需要通过 md5()
函数的进一步解密,才可以查询到
package Blogs.blogs5.com.RainbowSea.mhl.Service;
import Blogs.blogs5.com.RainbowSea.mhl.dao.EmployeeDAO;
import Blogs.blogs5.com.RainbowSea.mhl.javaBean.Employee;
/**
* 处理有关 employee 员工数据表的业务,统一都在这里实现
*/
public class EmployeeService {
// 创建EmployeeDAO 实例对象,用于调用其中的方法
private EmployeeDAO employeeDAO = new EmployeeDAO();
/**
* 通过 employee_name 员工的姓名,employee_pwd 员工的密码
* 查询 该员工是否存在
* 注意 pwd密码被 md5()函数加密了,查询时同样需要使用 md5()解密
* @param employeeName 员工姓名
* @param employeePwd 员工密码()
* @return 存在返回 Employee javaBean 实例对象,不存在返回 null
*/
public Employee getEmployeeNameByPwd(String employeeName, String employeePwd) {
String sql = "select employee_name as employeeName, employee_id as employeeId " + //注意使用空格分隔,不要成一句话了
" from employee where employee_name = ? and employee_pwd = md5(?)"; // 占位符不要加单引号,不然就成字符串了
Employee employee = employeeDAO.selectSingle(sql, Employee.class, employeeName, employeePwd);
return employee;
}
}
3.5.2 DiningService
DiningService 该类作用统一处理有关 dining 餐桌数据表的一些业务的操作。所有有关 dining 餐桌表
的业务操作都在这里实现:
实现如下业务:
- 查询显示所有餐桌的餐桌位,以及餐桌状态 (注意使用上别名,因为数据库中的命名格式与Java的命名格式不同,赋值的属性名不同,会导致赋值失败)
- 通过查询餐桌位,判断返回餐桌 javaBean
- 预定餐桌,更新餐桌的状态
- 点餐服务,修改餐桌的状态,为“就餐中”
- 通过餐桌编号,将对应餐桌初始化为,最初无人,预定/就餐的状态
- 通过餐桌编号,查询该餐桌是否为 “已预定” 的状态
package Blogs.blogs5.com.RainbowSea.mhl.Service;
import Blogs.blogs5.com.RainbowSea.mhl.dao.BillDAO;
import Blogs.blogs5.com.RainbowSea.mhl.dao.DiningDAO;
import Blogs.blogs5.com.RainbowSea.mhl.javaBean.Dining;
import java.util.List;
/**
* 处理有关 dining 餐桌表的业务,统一在这里处理
*/
public class DiningService {
private DiningDAO diningDAO = new DiningDAO(); // 作为类属性存在。
/**
* 查询显示所有餐桌的餐桌位,以及餐桌状态
* @return List<Dining>
*/
public List<Dining> allDining() {
// 注意别名上的使用
String sql = "select dining_id as diningId,dining_state as diningState, " +
"order_name as orderName,order_tel as orderTel " +
"from dining"; // 测试一下
List<Dining> list = diningDAO.selectMany(sql, Dining.class);
return list;
}
/**
* 通过查询餐桌位,判断返回餐桌 javaBean
* @param diningId 餐桌位
* @return Dining 查询到返回javaBean实例对象,查询不到返回null
*/
public Dining getDiningById(int diningId) {
String sql = "select dining_id as diningId,dining_state as diningState, " +
"order_name as orderName,order_tel as orderTel " +
"from dining where dining_id = ?"; // 测试一下
Dining dining = diningDAO.selectSingle(sql, Dining.class, diningId);
return dining;
}
/**
* 预定餐桌,更新餐桌的状态
* @param diningId 餐桌位
* @param orderName 预定餐桌的用户名
* @param orderTel 预定餐桌的联系电话
* @return boolean
*/
public boolean updateDining(int diningId,String orderName,String orderTel) {
String sql = "update dining set order_name = ?,order_tel = ?,dining_state = ? " +
"where dining_id = ?"; // 测试一下
int update = diningDAO.update(sql, orderName, orderTel, "已预定", diningId);
return update > 0;
}
/**
* 点餐服务,修改餐桌的状态,为“就餐中”
* @param diningId
* @return boolean
*/
public boolean setStateById(int diningId) {
String sql = "update dining set dining_state = ? where dining_id = ?"; // 测一测
int update = diningDAO.update(sql, "就餐中", diningId);
return update > 0;
}
/**
* 通过餐桌编号,将对应餐桌初始化为,最初无人,预定/就餐的状态
* @param diningId 餐桌编号
* @return boolean 成功 true,失败false
*/
public boolean billInit(int diningId) {
String sql = "update dining set dining_state = ?,order_name = '',order_tel = '' where dining_id = ?"; // 测一测
int update = diningDAO.update(sql, "空",diningId);
return update > 0;
}
/**
* 通过餐桌编号,查询该餐桌是否为 "已预定" 的状态
* @param diningId 餐桌编号
* @return 是返回 true,不是返回 false
*/
public boolean getDiningByIdAndState(int diningId) {
String sql = "select dining_state as diningState from dining where dining_id = ? and dining_state = ?"; // 测一测
Dining dining = diningDAO.selectSingle(sql, Dining.class,diningId, "已预定");
return dining != null;
}
}
3.5.3 MenuService
MenuService 该类作用统一处理有关 menu 菜谱的一些业务的操作。所有有关 menu 菜谱数据表
的业务操作都在这里实现:
实现如下业务:
- 查询所有 menu 数据表中的信息(注意别名上的使用)
- 通过菜品号,查询该菜信息,从而判断出该菜品号是否存在,不存在返回 null,存在返回 Menu
package Blogs.blogs5.com.RainbowSea.mhl.Service;
import Blogs.blogs5.com.RainbowSea.mhl.dao.MenuDAO;
import Blogs.blogs5.com.RainbowSea.mhl.javaBean.Menu;
import java.util.List;
/**
* 处理有关 菜谱数据表的业务逻辑,统一在这里处理
*/
public class MenuService {
private MenuDAO menuDAO = new MenuDAO();
/**
* 查询所有 menu 数据表中的信息
* @return List<Menu>
*/
public List<Menu> allMenu() {
String sql = "select menu_id as menuId,menu_name as menuName,menu_type as menuType,menu_price as menuPrice " +
"from menu";
List<Menu> menuList = menuDAO.selectMany(sql, Menu.class); // 可变参数可以不传参数,但不要传null,防止null引用
return menuList;
}
/**
* 通过菜品号,查询该菜,从而判断出该菜品号是否存在,不存在返回 null,存在返回 Menu
* @param menuId 菜品号
* @return Menu
*/
public Menu getMenuById(int menuId) {
String sql = "select menu_id as menuId,menu_name as menuName,menu_type as menuType,menu_price as menuPrice " +
"from menu where menu_id = ?";
Menu menu = menuDAO.selectSingle(sql, Menu.class, menuId);
return menu;
}
}
3.5.4 BillService
BillService 该类作用统一处理有关 bill 账单数据表的一些业务的操作。所有有关 bill餐桌数据表
的业务操作都在这里实现:
实现如下业务:
- 点餐服务,生成对应账单,插入到 bill 数据表中
- 查询显示所有的账单信息(注意别名上的使用,以及这里使用多表查询的相关 javaBean 实例)
- 根据 餐桌编号,判断账单中对应的 状态是否为 “未结账”,是返回 true,不是返回 false
- 通过餐桌编号,显示对应餐桌 “未结账” 的账单
- 计算对应餐桌编号的 “未结账”的消费总金额
- 通过餐桌编号更新,"未结账“的状态修改为 “支付方式”
package Blogs.blogs5.com.RainbowSea.mhl.Service;
import Blogs.blogs5.com.RainbowSea.mhl.dao.BillDAO;
import Blogs.blogs5.com.RainbowSea.mhl.javaBean.Bill;
import Blogs.blogs5.com.RainbowSea.mhl.javaBean.Menu;
import java.util.List;
import java.util.UUID;
/**
* 统一处理有关 bill 账单数据表的业务
*/
public class BillService {
private BillDAO billDAO = new BillDAO();
private MenuService menuService = new MenuService();
/**
* 点餐服务,生成对应账单,插入到 bill 数据表中
* @param diningId // 餐桌位
* @param menuId // 菜品号
* @param menuNums // 该菜的数量
* @return boolean 成功返回true,否则 false
*/
public boolean billMenu(int diningId,int menuId,int menuNums) {
// 1. 首先 使用UUID 作为订单号
String billId = UUID.randomUUID().toString(); // 随机生成一个“唯一”的数值,用作订单号
// 通过菜品编号获取到菜的单价,以及菜名
Menu menu = menuService.getMenuById(menuId);
String menuName = menu.getMenuName(); // 菜名
double menuPrice = menu.getMenuPrice(); // 菜的单价
// 插入数据,生成账单
String sql = "insert into bill(bill_id,bill_date,dining_id,menu_id,menu_name,menu_nums,menu_money," +
"bill_state) " +
"values(?,now(),?,?,?,?,?,'未结账')"; // 测一测
int update = billDAO.update(sql, billId, diningId, menuId, menuName, menuNums, menuNums * menuPrice);
return update > 0;
}
/**
* 查询显示所有的账单信息
* @return List<Bill>
*/
public List<Bill> allBill() {
String sql = "SELECT id,bill_id AS billId,bill_date AS billDate,dining_id AS diningId,b.`menu_id` AS menuId, " +
"b.`menu_name` AS menuName, menu_nums AS menuNums,menu_money AS menuMoney,m.`menu_price` AS menuPrice, " +
"bill_state AS billState " +
"FROM bill b " +
"JOIN menu m " +
"ON b.`menu_id` = m.`menu_id`"; // 测一测
List<Bill> billList = billDAO.selectMany(sql, Bill.class);
return billList;
}
/**
* 根据 餐桌编号,判断账单中对应的 状态是否为 "未结账",是返回 true,不是返回 false
* @param diningId 餐桌编号
* @return boolean 是返回 true,不是返回 false
*/
public boolean getBillState(int diningId) {
String sql = "select bill_state as billState from bill where dining_id = ? and bill_state = ?"; // 测一测
Bill bill = billDAO.selectSingle(sql, Bill.class, diningId, "未结账");
return bill != null;
}
/**
* 通过餐桌编号,显示对应餐桌 "未结账" 的账单
* @param diningId 餐桌编号
* @return List<Bill>
*/
public List<Bill> getBillByDiningId(int diningId) {
String sql = "SELECT id,bill_id AS billId,bill_date AS billDate,dining_id AS diningId,b.`menu_id` AS menuId, " +
"b.`menu_name` AS menuName, menu_nums AS menuNums,menu_money AS menuMoney,m.`menu_price` AS menuPrice, " +
"bill_state AS billState " +
"FROM bill b " +
"JOIN menu m " +
"ON b.`menu_id` = m.`menu_id` " +
"WHERE b.`bill_state` = '未结账' and b.`dining_id` = ?"; // 测一测
List<Bill> billList = billDAO.selectMany(sql, Bill.class,diningId);
return billList;
}
/**
* 计算对应餐桌编号的 “未结账”的消费总金额
* @param diningId 餐桌编号
* @return double
*/
public double sumMoney(int diningId) {
String sql = "SELECT SUM(menu_money) FROM bill WHERE dining_id = ? AND bill_state = ?"; // 测一测
double sum = (double)billDAO.selectSum(sql, diningId, "未结账");
return sum;
}
/**
* 通过餐桌编号更新,"未结账“的状态修改为 "支付方式"
* @param diningId 餐桌编号
* @param payMode 支付方式
* @return boolean
*/
public boolean setBillStateBydiningId(int diningId,String payMode) {
String sql = "update bill set bill_state = ? where dining_id = ? and bill_state = ?";
int update = billDAO.update(sql, payMode, diningId, "未结账");
return update > 0;
}
}
3.6 view (包)
view 该包存放:所有有关界面显示 的代码实现
3.6.1 MhlView
MhlView 是主的界面显示的处理代码实现:main
方法的就定义在这里
实现如下界面的功能:
- 满汉楼的登录界面的验证
主要思路: 通过输入用户名,以及用户密码,去 employee 数据表中查询,是否能查询到记录,能,则登录,不能,则说明用户名/密码错误。通过定义死循环,无限输入,
- 二级界面的选择
主要思路: 对于二级界面的功能上的选择,通过通过类 Utility 中的方法进行校验,不要防止用户输入不合法的内容,比如中文,通过 Utility 工具类中的方法进行限定校验。需要注意的是 这里的 switch()
中的选项是 字符串 ,不是数值。通过改变,循环条件的变量,到达退出满汉楼的效果。
package Blogs.blogs5.com.RainbowSea.mhl.view;
import Blogs.blogs5.com.RainbowSea.mhl.Service.EmployeeService;
import Blogs.blogs5.com.RainbowSea.mhl.javaBean.Employee;
import Blogs.blogs5.com.RainbowSea.mhl.utils.Utility;
public class MhlView {
private boolean loop = true;
// 创建 EmployeeService 实例,调用其中的方法
private EmployeeService employeeService = new EmployeeService(); // 作为类属性存在
private DiningView diningView = new DiningView();
private MenuView menuView = new MenuView();
private BillView billView = new BillView();
public static void main(String[] args) {
MhlView mhlView = new MhlView(); // 实例化对象,静态方法调用非静态的方法
mhlView.mainMenu();
}
public void mainMenu() {
String key = ""; // 用户输入的选择,
while(loop) {
System.out.println("================= 【满汉楼】 ================= ");
System.out.println(" 1. 登录满汉楼");
System.out.println(" 0. 退出满汉楼");
System.out.print("请输入你的选择: ");
key = Utility.readString(1);
switch(key) { // 注意是:字符串
case "1" :
System.out.print("请输入用户名: ");
String name = Utility.readString(30);
System.out.print("请输入密码: ");
String pwd = Utility.readString(32);
Employee employee = employeeService.getEmployeeNameByPwd(name, pwd);
if(employee != null) {
System.out.println("【登录成功】");
while(loop) {
System.out.println("===================== 满汉楼【"+employee.getEmployeeName()+"】 " +
"===================== ");
System.out.println(" 1.显示餐桌状态");
System.out.println(" 2.预定餐桌");
System.out.println(" 3.显示所有的菜品");
System.out.println(" 4.点餐服务");
System.out.println(" 5.查看所有账单");
System.out.println(" 6.结账");
System.out.println(" 7.取消预定");
System.out.println(" 0.退出满汉楼");
System.out.print("请输入您的选择: ");
key = Utility.readString(1);
switch(key) { // 注意是字符串
case "1" :
diningView.showDining();
break;
case "2" :
diningView.tableDining();
break;
case "3" :
menuView.showMenu();
break;
case "4":
billView.orderMenu();
break;
case "5":
billView.allBill();
break;
case "6" :
billView.payBill();
break;
case "7":
diningView.cancelReserve();
break;
case "0":
System.out.println("退出满汉楼");
loop = false; // 修改循环的结束条件
break;
default:
System.out.println("你的选择有误,请重新输入");
break;
}
}
} else {
System.out.println("【用户名/密码错误/该用户不存在,请重新登录】!!!");
}
break;
case "0":
System.out.println("【退出满汉楼】");
loop = false; // 修改循环条件,结束死循环
break;
default :
System.out.println("您的选择错误,请重新选择!!!");
break;
}
}
}
}
3.6.2 DiningView
DiningView 该类是处理有关 餐桌
的界面显示的代码实现:
实现如下界面的功能:
- 显示餐桌状态
通过从 dining 数据表中查询到的结果集,存储到链表中,最后遍历链表,显示餐桌所有信息
- 预定餐桌
根据输入的餐桌号,进行一个预定
1. 首先需要判断该餐桌位是否存在,存在才可以预定,以及只有当“餐桌状态为"空"才可以预定,如果该想要预定的餐桌是处于 "就餐中/已预定"的状态是无法预定的
2. 最后一个Y/N的确认预定,Y 表示确认,N表示 取消,通过 Utility 工具类中的方法限定校验用户输入的内容只能是 Y/N ,忽略大小写,Utility 工具类的方法会自动进行一个大小写转换,小写的会自动转换为大写。
3. 输入预定人的名字,以及预定人的电话 信息进行预定
4. 预定修改 dining 餐桌数据表中对应的餐桌号中的信息(餐桌的状态改为’已预定’),以及预定人的用户名/联系电话信息。
- 取消餐桌的预定
根据餐桌编号,取消预定的餐桌
- 先判断餐桌编号是否存在,存在才可以取消预定
- 再判断该餐桌编号的状态是否是 “已预定”,只有是该状态的餐桌才可以 取消预定 ,其他的像 ”就餐中“ ,"空"的状态的是无法取消预定的,因为就餐中了,说明你都已经吃上了,还想取消预定,不想给钱,那是不可能的。
- 最后的确认是否Y/N 取消预定,更新 dining餐桌为初始状态,所谓的初始状态就是,没有人预定,没有人就餐的状态。
package Blogs.blogs5.com.RainbowSea.mhl.view;
import Blogs.blogs5.com.RainbowSea.mhl.Service.DiningService;
import Blogs.blogs5.com.RainbowSea.mhl.javaBean.Dining;
import Blogs.blogs5.com.RainbowSea.mhl.utils.Utility;
import java.util.List;
public class DiningView {
private DiningService diningService = new DiningService();
/**
* 显示餐桌状态
*/
public void showDining() {
System.out.println("餐桌位\t\t餐桌状态\t\t预定人\t\t联系电话");
List<Dining> list = diningService.allDining();
// 遍历链表方式一:
for(Dining d : list) {
System.out.println(d);
}
System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
// 遍历链表方式二:
// list.forEach(System.out::println);
}
/**
* 预定餐桌
*/
public void tableDining(){
/*
根据输入的餐桌号,进行一个预定
1. 首先需要判断该餐桌位是否存在,存在才可以预定,以及只有当“餐桌状态为"空"才可以预定
2. 最后一个Y/N的确认预定
3. 输入预定人的名字,以及预定人的电话 信息进行预定
4. 预定修改 dining 餐桌数据表中对应的餐桌号中的信息(餐桌的状态改为'已预定')
*/
System.out.print("请输入你要预定的餐桌位(-1退出): ");
int diningId = Utility.readInt();
if(diningId == -1) {
System.out.println("【退出餐桌位】");
return ; // 退出该方法
}
// 判断选择的餐桌位是否存在
Dining dining = diningService.getDiningById(diningId);
if(dining == null) {
System.out.println("【您选择的餐桌位不存在,请重新选择】");
return ;
}
// 判断该餐桌位是否为 "空"的状态
if(!"空".equals(dining.getDiningState())) {
System.out.println("【该餐桌位已经被预定了/已经有人正在就餐了,暂时无法预定】");
return ; // 提出该方法
}
System.out.print("是否确认预定Y/N: ");
char key = Utility.readChar();
if(key == 'N') {
System.out.println("【退出预定】");
return ;
}
// 走到这里说明,真的可以预定,输入信息,更新餐桌状态
System.out.print("预定人的名字:");
String orderName = Utility.readString(50); // 该方法让其输入的必须是规定长度的字符
System.out.print("预定人的电话: ");
String orderTel = Utility.readString(12); // 规定12 个字符
// 更新餐桌状态,以及填充信息
if(diningService.updateDining(diningId,orderName,orderTel)) {
System.out.println("【预定成功】");
} else {
System.out.println("【预定失败】");
}
}
/*
取消餐桌的预定
*/
public void cancelReserve() {
// 根据餐桌编号,取消预定的餐桌
// 1.先判断餐桌编号是否存在
// 2. 只有为已预定的餐桌可以取消,正在就餐中/还未预定,无法取消预定
// 最后的确认是否Y/N 取消预定,更新 dining餐桌为初始状态
System.out.print("请输入你要取消预定的餐桌位(-1表示退出): ");
int diningId = Utility.readInt();
if(diningId == -1) {
System.out.println("【退出】");
return ;
}
// 判断餐桌编号是否存在
if(diningService.getDiningById(diningId) == null) {
System.out.println("【该餐桌编号不存在,请重新选择】");
return ;
}
// 只有为 '已预定的餐桌才可以取消预定'
if(!diningService.getDiningByIdAndState(diningId)) {
System.out.println("【该餐桌为:正在就餐中/还未预定,无法取消预定】");
return;
}
System.out.print("确认是否取消预定【Y/N】: ");
char key = Utility.readConfirmSelection();
if(key == 'N') {
System.out.println("退出");
return;
}
// 走到这说明可以取消预定了,更新初始化餐桌 dining 为无人的状态
if(diningService.billInit(diningId)) {
System.out.println("【取消预定成功】");
} else {
System.out.println("【取消预定失败】");
}
}
}
3.6.3 MenuView
MenuView 该类是处理有关 菜谱
的界面显示的代码实现:
实现如下界面功能:
- 显示所有菜谱信息
通过从 menu 数据表中查询到的结果集,存储到链表中,最后遍历链表,显示菜谱的所有信息
package Blogs.blogs5.com.RainbowSea.mhl.view;
import Blogs.blogs5.com.RainbowSea.mhl.Service.MenuService;
import Blogs.blogs5.com.RainbowSea.mhl.javaBean.Menu;
import java.util.List;
public class MenuView {
private MenuService menuService = new MenuService();
/**
* 显示所有菜谱信息
*/
public void showMenu() {
List<Menu> menuList = menuService.allMenu();
// 遍历链表的方式一:
System.out.println("菜品编号\t\t菜名\t\t菜的种类\t\t单价");
menuList.forEach(System.out::println);
// 遍历链表的方式二:
/*for(Menu m : menuList) {
System.out.println(m);
}
*/
}
}
3.6.4 BillView
BillView 该类是处理有关 账单
的界面显示的代码实现:
实现如下界面的功能:
- 点餐服务
通过输入的餐桌位,进行点餐
- 首先判断输入的餐桌位是否存在,以及输入的菜编号是否存在
- 中途输入-1 表示退出
- 以上信息都没有问题,就修改 dining 对应餐桌位上的状态改为 “就餐中”
- 以及将该点餐的账单信息插入到 bill 数据表当中
- 显示所有的账单信息
通过从 menu 数据表中查询到的结果集(注意别名上的使用,因为数据库的命名格式与Java的命名格式不同,当查询显示的字段名与Java类中 JavaBean 属性名不一致时,是无法赋值的。),存储到链表中,最后遍历链表,显示菜谱的所有信息
- 结账
根据餐桌位的编号,进行结账处理
- .先判断所结账的餐桌是否存在,判断该餐桌是否是“未结账”,只有是未结账状态的才能结账(已结账的信息不要再结账了)
- 显示对应餐桌 "未结账"的账单信息,并计算出总金额(已结账的不要计算到)
- 选择支付方式 (微信/支付宝/现金/银行卡)
- 最后确认是否支付: Y/N
- 更新 bill 账单数据表中对应餐桌编号的状态将 "未结账"修改为 支付方式(微信/支付宝/现金/银行卡)。
- 最后再更新该已经结账的餐桌信息,更新初始化为 餐桌最初无人,预定/就餐的状态
package Blogs.blogs5.com.RainbowSea.mhl.view;
import Blogs.blogs5.com.RainbowSea.mhl.Service.BillService;
import Blogs.blogs5.com.RainbowSea.mhl.Service.DiningService;
import Blogs.blogs5.com.RainbowSea.mhl.Service.MenuService;
import Blogs.blogs5.com.RainbowSea.mhl.javaBean.Bill;
import Blogs.blogs5.com.RainbowSea.mhl.utils.Utility;
import java.util.List;
public class BillView {
private DiningService diningService = new DiningService();
private MenuService menuService = new MenuService();
private BillService billService = new BillService();
/**
* 点餐服务
*/
public void orderMenu() {
// 1. 通过输入的餐桌位,进行点餐
// 2. 判断输入的餐桌位是否存在,以及输入的菜编号是否存在
// 中途输入-1 表示退出
// 以上信息都没有问题,就修改 dining 对应餐桌位上的状态改为 “就餐中”
// 以及 插入一条记录对应账单信息到 bill 中
System.out.println("================= 点餐服务 ================= ");
System.out.print("请输入点餐的餐桌号(-1 退出): ");
int diningId = Utility.readInt();
if(diningId == -1) {
System.out.println("【退出点餐】");
return; // 退出该方法
}
// 判断餐桌号是否合理存在
if(diningService.getDiningById(diningId) == null) {
System.out.println("【该餐桌号不存在,请重新选择】");
return ;
}
System.out.print("请输入点餐的菜品号(-1退出): ");
int menuId = Utility.readInt();
if(menuId == -1) {
System.out.println("【退出点餐】");
return; // 退出该方法
}
// 判断菜品号是否合理存在
if(menuService.getMenuById(menuId) == null) {
System.out.println("该菜品号不存在,请重新选择");
return ;
}
System.out.print("请输入菜品的数量(-1退出): ");
int menuNums = Utility.readInt();
if(menuNums == -1) {
System.out.println("【退出点餐】");
return; // 退出该方法
}
// 走到这里,说明上述参数没有问题,更新数据表
// 更新 dining 餐桌的状态改为 "就餐中",如果是直接就餐的,不用添加用户名和联系人''
// 其他的则直接修改餐桌的状态,
if(diningService.setStateById(diningId)) {
// 添加一条账单记录在 bill 账单表中
if(billService.billMenu(diningId,menuId,menuNums)) {
System.out.println("【点餐成功】");
} else {
System.out.println("【点餐失败】");
}
} else {
System.out.println("【点餐失败】");
return;
}
}
/**
* 显示所有的账单信息
*/
public void allBill() {
List<Bill> billList = billService.allBill();
System.out.println("\n编号\t\t日期\t\t\t\t\t餐桌号\t\t菜品号\t\t菜名\t\t数量\t\t单价\t\t总价\t\t状态");
// 遍历链表的方式一:
for(Bill b : billList) {
System.out.println(b);
}
System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
// 遍历链表的方式二
// billList.forEach(System.out::println);
}
/**
* 结账
*/
public void payBill() {
// 1.根据餐桌结账
// 2.先判断所结账的餐桌是否存在,判断该餐桌是否是“未结账”,只有是未结账状态的才能结账
// 3.显示对应餐桌 "未结账"的账单,并计算出总金额(未结账的不要计算)
// 4.选择支付方式,
// 5.最后确认是否支付: Y/N
// 6.更新餐桌的状态为 "空,无人预定使用的状态",以及更新 bill 表单的状态为支付方式
System.out.print("请选择要结账的餐桌号(-1退出): ");
int diningId = Utility.readInt();
// 判断餐桌是否存在
if(diningService.getDiningById(diningId) == null) {
System.out.println("【该餐桌不存在,请重新选择】");
return ;
}
if(!billService.getBillState(diningId)) {
System.out.println("【该餐位已经结账/无人就餐/已预定还未点餐!!!】");
return;
}
// 走到这说明,餐位存在,且未结账,显示该餐位账单信息,以及总金额(未结账的总金额)
System.out.println("\n编号\t\t日期\t\t\t\t\t餐桌号\t\t菜品号\t\t菜名\t\t数量\t\t单价\t\t总价\t\t状态");
List<Bill> billList = billService.getBillByDiningId(diningId);
// 遍历链表方式一:
for(Bill b : billList) {
System.out.println(b);
}
System.out.println("一共消费了: "+billService.sumMoney(diningId)+"¥");
System.out.print("你选择支付方式(支付宝/微信/银行卡/现金,回车表示退出): ");
String payMode = Utility.readString(20,""); // 说明如果回车,就返回
if("".equals(payMode)) { // 说明如果回车,就返回
System.out.println("【取消结账】");
return;
}
// 最后的 Y/N 确定
System.out.print("是否确认结账【Y/N】: ");
char key = Utility.readConfirmSelection(); // 不区分大小写,会自动转换为大写
if(key == 'N') {
System.out.println("【取消结账】");
return;
}
// 更新对应餐桌号的账单状态为: 未结账方式
if(! billService.setBillStateBydiningId(diningId,payMode)) {
System.out.println("【支付失败】");
return;
}
// 更新结账后,餐桌的状态为 "初始化,无人就餐无人预定的状态"
if(diningService.billInit(diningId)) {
System.out.println("【支付成功】");
} else {
System.out.println("【支付失败】");
}
}
}
4. 总结:
-
编写一个项目的时候,先拟定好架构 ,
架构拟定好了,分清了主干
。这样无论你怎么编写代码都不会太偏离主干。写的代码才不会乱,犯浑。干起来也不会太累。最后在分好层级,根据拟定好的架构图,紧跟着主干,一点一点的添加细节。当你把所有的细节都编写好后,项目也就基本上写好了。 -
对于Java当中的每一条 SQL 语句的编写,都放到 Mysql 中执行一下验证是否有错误,防止写错了。导致后面的执行出现异常。
-
数据库的命名风格是下划线风格 ,而Java的命名风格小驼峰 ,两个命名格式不同,就会导致,查询结果集的映射到 Java当中,无法对属性值,赋值,因为两个的变量名不同,无法找到匹配的属性进行赋值。所以我们在 select 查询显示上 使用别名 ,让查询显示的属性名与Java当中的属性一致。
-
创建数据表时,尽量避免
null
值的存在,定义上not null
非空约束,以及默认值可以为DEFAULT ''
空字符串。防止 null 带来的异常错误 -
注意SQL语句中的
md5()
加密函数,被 md5() 加密的信息,直接使用信息内容是无法查询到的,需要通过md5() 解密后才能查询到。如果要使用 md5() 函数的话,数据库中定义的数据类型要是char(32)
32个字节大小的。 -
在Java当中编写的 SQL 语句如果太长了,可以使用
+
字符串拼接的方式,分行简化,但是需要注意的是:+
拼接之间,附加上空格,不然拼接后,两个不同的关键字,可能就被拼接成了一个 关键字了。
String sql = "select * from where"+
"id = ? and name = ?" // 这里没有使用在最后拼接的位置附加上空格,拼接后就是一个关键字了。
// 下面是正确的方式:
String sql = "select * from where "+ // 拼接的位置使用上空格分隔
"id = ? and name = ?"
- 可变参数可以不传参数,但是不要传
null
,防止null引用报错 - Java当中的 UUID 的使用,随机生成一个“唯一”的数值。
// 1. 首先 使用UUID 作为订单号
String billId = UUID.randomUUID().toString(); // 随机生成一个“唯一”的数值,用作订单号
5. 最后:
限于自身水平,其中存在的错误,希望大家给予指教,韩信点兵——多多益善,谢谢大家,后会有期,有缘再见 !!!