基于Apache-DButils以及Druid(德鲁伊)与数据库交互实现的一个项目:满汉楼

导读:本篇文章讲解 基于Apache-DButils以及Druid(德鲁伊)与数据库交互实现的一个项目:满汉楼,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

基于Apache-DButils以及Druid(德鲁伊)与数据库交互实现的一个项目:满汉楼

在这里插入图片描述


每博一文案

张小贤曾说过: 你不过是做自己喜欢做的事,过自己喜欢过的生活。
若有人因为你喜欢做的事而觉得恶心和取笑你,那是他们的事。是呀,生活是苦,但也处处有光。
如果因别人无心的言语,而胡思乱想,因遇到不顺心的事而垂头丧气,那么无谓的烦恼
就会占据本就位置不大的心,让我们变得更难快乐。因此,真正智慧的人,学会了自己哄自己开心,
毕竟总有一阵风会吹走所有的烦恼,当下的一些困惑。今年以后回头看看,所谓烦恼亦不过如此。
宇宙何其浩瀚,星辰何其美妙。你何其的努力和善良。在这千姿百态的大千世界里,你值得更好的人。
也应该去做更棒的事,正如蔡澜先生所说,把生活的素质提高,今天活得比昨天快乐,明天又要比今天快乐,
就此而已。这就是人生的意义。活下去的真谛。失散的人,总有一天会在临夏重逢,错过的事终会以另外的方式
补偿。世事洪荒沧溟万里。走过去了,便上清水静,云淡风轻。余生学会自己,哄自己开心。
种自己的花,爱自己的宇宙。
                              ——————   一禅心灵庙语


0. 创建项目的注意事项

  • 首先我们要明白一点就是不存在完完全全没有 BUG的项目,就像人无完人一样 我们只能不断地对项目进行优化,减少BUG 的出现,但是我们好像并不能完全的消除所有的BUG
  • 而消除bug 的最好的方式就是运行测试+调试
  • 而我们在运行测试+调试 的时候,需要我们控制一定的量 ,我们需要把握住这个量 ,这一点是十分重要的,这样有助于我们快速的发现问题,也就是bug 的所在,从而提高我们的效率,减少我们的bug 的出现
  • 具体的方法,我个人有以下建议:
  1. 首先我们需要对项目进行合理的分类,那个类,那个文件实现什么样的功能: 比如:一个类是用于定义什么类型,方法的 ,另一个类是用于什么头main 的,再另外一个类是用于对于什么功能上的实现的 ,合理的分类,可以提高我们项目的可读性,让我们自身不会因为自己随着代码量的增加,对应功能上的增加而导致我们越敲越乱,越写越蒙
  2. 对于量上我们可以,每实现了两三个 功能就运行测试看看,是否存在错误,如果存在出现了错误,我们可以在一定的量(范围内),快速的找出哪里出现了问题,从而对它进行调试 ,解决问题,而不是,把项目整体写完了或者是已经实现了好几十个功能,才开始运行测试项目,结果一运行,bug,警告 冒出一大坨,我们先不说,让不让人,抓头发,就是找出问题,恐怕都需要不时间上的浪费吧
  3. 对于代码上的注释,没事多写明白一点,或许有一天,你会感想曾经,那个写了注释的自己或同事
  4. 对于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 数据表中查询,是否能查询到记录,能,则登录,不能,则说明用户名/密码错误。通过定义死循环,无限输入,
在这里插入图片描述
在这里插入图片描述
基于Apache-DButils以及Druid(德鲁伊)与数据库交互实现的一个项目:满汉楼

  • 二级界面的选择

主要思路: 对于二级界面的功能上的选择,通过通过类 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 餐桌数据表中对应的餐桌号中的信息(餐桌的状态改为’已预定’),以及预定人的用户名/联系电话信息。
基于Apache-DButils以及Druid(德鲁伊)与数据库交互实现的一个项目:满汉楼

  • 取消餐桌的预定

根据餐桌编号,取消预定的餐桌

  1. 先判断餐桌编号是否存在,存在才可以取消预定
  2. 再判断该餐桌编号的状态是否是 “已预定”,只有是该状态的餐桌才可以 取消预定 ,其他的像 ”就餐中“ ,”空”的状态的是无法取消预定的,因为就餐中了,说明你都已经吃上了,还想取消预定,不想给钱,那是不可能的。
  3. 最后的确认是否Y/N 取消预定,更新 dining餐桌为初始状态,所谓的初始状态就是,没有人预定,没有人就餐的状态。
    基于Apache-DButils以及Druid(德鲁伊)与数据库交互实现的一个项目:满汉楼
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. 首先判断输入的餐桌位是否存在,以及输入的菜编号是否存在
  2. 中途输入-1 表示退出
  3. 以上信息都没有问题,就修改 dining 对应餐桌位上的状态改为 “就餐中”
  4. 以及将该点餐的账单信息插入到 bill 数据表当中
    基于Apache-DButils以及Druid(德鲁伊)与数据库交互实现的一个项目:满汉楼
  • 显示所有的账单信息

通过从 menu 数据表中查询到的结果集(注意别名上的使用,因为数据库的命名格式与Java的命名格式不同,当查询显示的字段名与Java类中 JavaBean 属性名不一致时,是无法赋值的。),存储到链表中,最后遍历链表,显示菜谱的所有信息

在这里插入图片描述

  • 结账

根据餐桌位的编号,进行结账处理

  1. .先判断所结账的餐桌是否存在,判断该餐桌是否是“未结账”,只有是未结账状态的才能结账(已结账的信息不要再结账了)
  2. 显示对应餐桌 “未结账”的账单信息,并计算出总金额(已结账的不要计算到)
  3. 选择支付方式 (微信/支付宝/现金/银行卡)
  4. 最后确认是否支付: Y/N
  5. 更新 bill 账单数据表中对应餐桌编号的状态将 “未结账”修改为 支付方式(微信/支付宝/现金/银行卡)。
  6. 最后再更新该已经结账的餐桌信息,更新初始化为 餐桌最初无人,预定/就餐的状态
    在这里插入图片描述
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. 总结:

  1. 编写一个项目的时候,先拟定好架构架构拟定好了,分清了主干。这样无论你怎么编写代码都不会太偏离主干。写的代码才不会乱,犯浑。干起来也不会太累。最后在分好层级,根据拟定好的架构图,紧跟着主干,一点一点的添加细节。当你把所有的细节都编写好后,项目也就基本上写好了。

  2. 对于Java当中的每一条 SQL 语句的编写,都放到 Mysql 中执行一下验证是否有错误,防止写错了。导致后面的执行出现异常。

  3. 数据库的命名风格是下划线风格 ,而Java的命名风格小驼峰 ,两个命名格式不同,就会导致,查询结果集的映射到 Java当中,无法对属性值,赋值,因为两个的变量名不同,无法找到匹配的属性进行赋值。所以我们在 select 查询显示上 使用别名 ,让查询显示的属性名与Java当中的属性一致。

  4. 创建数据表时,尽量避免 null 值的存在,定义上 not null 非空约束,以及默认值可以为 DEFAULT '' 空字符串。防止 null 带来的异常错误

  5. 注意SQL语句中的 md5() 加密函数,被 md5() 加密的信息,直接使用信息内容是无法查询到的,需要通过md5() 解密后才能查询到。如果要使用 md5() 函数的话,数据库中定义的数据类型要是 char(32) 32个字节大小的。

  6. 在Java当中编写的 SQL 语句如果太长了,可以使用 + 字符串拼接的方式,分行简化,但是需要注意的是: + 拼接之间,附加上空格,不然拼接后,两个不同的关键字,可能就被拼接成了一个 关键字了。

   String sql = "select * from where"+  
       "id = ? and name = ?" // 这里没有使用在最后拼接的位置附加上空格,拼接后就是一个关键字了。
   // 下面是正确的方式:
   String sql = "select * from where "+  // 拼接的位置使用上空格分隔
       "id = ? and name = ?" 
      
  1. 可变参数可以不传参数,但是不要传 null ,防止null引用报错
  2. Java当中的 UUID 的使用,随机生成一个“唯一”的数值。
 // 1. 首先 使用UUID 作为订单号
        String billId = UUID.randomUUID().toString();  // 随机生成一个“唯一”的数值,用作订单号

5. 最后:

限于自身水平,其中存在的错误,希望大家给予指教,韩信点兵——多多益善,谢谢大家,后会有期,有缘再见 !!!


在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由半码博客整理,本文链接:https://www.bmabk.com/index.php/post/83003.html

(0)
小半的头像小半

相关推荐

半码博客——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!