这次我们来使用java完成满汉楼项目。
1.满汉楼项目介绍:
满汉楼项目实际上是一个类似于饭店点餐的系统,这里面包括了顾客预定餐桌,顾客点餐,和顾客结账等功能。
2.满汉楼项目分析
通过满汉楼项目,我们将该项目分成四个层次,由下至上分别是domain、Dao、service、view层,每一层分别完成不同的任务,通过分层,使得每一个层都对应一个具体的功能,这样写代码的时候就不那么容易紊乱。
2.1分层介绍
domain层:主要是用于当JDBC连接数据库时,由于我们不使用resultSet这个集合(因为resultSet一旦关闭了,我们通过数据库获取的内容也会消失),我们使用DBUtils提供的方法,将数据保存在集合中,在下面具体使用时我们会详细介绍
Dao层:该层主要时继承了BasicDao类的内容,能对数据库进行增删改查,在下面我们也会具体介绍。
service层:该层主要是使用Dao层的方法,结合在view中的具体使用
view层:该层是界面层,也就是用户能够看到的层,主要是使用service封装好的方法
2.2为什么要分层?
我相信有很多小伙伴都有这样的疑惑,实际上分层就是将不同的功能更具体化,domain层主要完成对数据库读取的数据进行保存,Dao层里面封装对数据库的增删改查,service层主要对Dao层的方法的具体使用,view层是对service层方法的具体使用,这样可以做到分工明确,提高效率
3.domain层
该层主要是用于在底层反射使用的,可能会有小伙伴有疑问,那么什么是反射呢?,我们都知道,在java源程序,也就是我们写的代码,会经过java编译器的处理,将其转换成java字节码文件,也就是.class文件,那么在类加载器就会读取这个转化的字节码文件,并将其实例化成一个Class对象,也就是说,实际上,每一个java文件都有一个对应的Class对象,这个Class对象实际上就包含了我们这个类的结构,像是一些属性之类的。那么这跟反射有什么关系呢,我们反射恰恰就是使用了这个原理,我们使用反射可以手动地将一个类的结构(包括该类的属性之类的)反射出来,并且将其实例化成一个对象,这就是反射。由于我们在连接数据库的时候,我们想要将我们从数据库读取的数据,存放在java中,那么我们就写一个类跟数据库的表的列一致,那么我们将数据库的数据读取出来的时候,我们就使用我们这个写的类反射出来,然后实例化,通过set方法,将数据添加到实例化的对象,然后将该对象添加到集合中,就可以放多条对象在这个集合中,就可以读取多行数据库的数据了。
3.1domain层的具体实现
在满汉楼项目中,我们一共需要4个domain分别是Employee,主要与数据库的雇员表相对应,用于存放员工信息
public class Employee {
private Integer id;
private String empId;
private String pwd;
private String name;
private String job;
public Employee(){//无参构造器,底层DbUtils需要使用
}
public Employee(Integer id, String empId, String pwd, String name, String job) {
this.id = id;
this.empId = empId;
this.pwd = pwd;
this.name = name;
this.job = job;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getEmpId() {
return empId;
}
public void setEmpId(String empId) {
this.empId = empId;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
}
当然我们是需要在数据库中,创建相对应的表的
CREATE TABLE employee(
id INT PRIMARY KEY AUTO_INCREMENT,#自增
empId VARCHAR(32),#员工号
pwd VARCHAR(32),#密码
NAME VARCHAR(32),#姓名
job VARCHAR(32))CHARSET=utf8#岗位
第二个,DiningTable,主要用于存放餐桌的状态(桌号,状态之类的)
private String orderTel;
public DiningTable(){//用于反射的无参构造器
}
public DiningTable(Integer id, String state, String orderName, String orderTel) {
this.id = id;
this.state = state;
this.orderName = orderName;
this.orderTel = orderTel;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getstate() {
return state;
}
public void setstate(String state) {
this.state = state;
}
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;
}
}
第三个foodmenu,主要用于点餐会用到,同样与数据库的foodmenu表相映射
public class foodmunu {
private Integer id;
private String name;
private String category;
private Double price;
public foodmunu(){//用于反射的无参构造器
}
public foodmunu(Integer id, String name, String category, Double price) {
this.id = id;
this.name = name;
this.category = category;
this.price = price;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
@Override
public String toString() {
return "foodmunu{" +
"id=" + id +
", name='" + name + '\'' +
", category='" + category + '\'' +
", price=" + price +
'}';
}
}
第四个,Bill类,主要用于提供账单等信息,与数据库的Bill相映射
public class Bill {
private Integer id;
private String billId;
private Integer foodmeunID;//菜品编号
private Integer nums;
private Double money;
private Integer diningtableId;
private Date billDate;
private String state;
public Bill(){//无参构造器
}
public Bill(Integer id, String billId, Integer foodmeunID, Integer nums, Double money, Integer diningtableId, Date billDate, String state) {
this.id = id;
this.billId = billId;
this.foodmeunID = foodmeunID;
this.nums = nums;
this.money = money;
this.diningtableId = diningtableId;
this.billDate = billDate;
this.state = state;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getBillId() {
return billId;
}
public void setBillId(String billId) {
this.billId = billId;
}
public Integer getFoodmeunID() {
return foodmeunID;
}
public void setFoodmeunID(Integer foodmeunID) {
this.foodmeunID = foodmeunID;
}
public Integer getNums() {
return nums;
}
public void setNums(Integer nums) {
this.nums = nums;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
public Integer getDiningtableId() {
return diningtableId;
}
public void setDiningtableId(Integer diningtableId) {
this.diningtableId = diningtableId;
}
public Date getBillDate() {
return billDate;
}
public void setBillDate(Date billDate) {
this.billDate = billDate;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
完成domain层后,我们就要开始写Dao层了,在写Dao层之前,我们需要完成BaiscDao,BasicDao主要是将我们对数据库增删改查的方法进行封装。这里我们使用到Druid连接池,配合DBUtils工具类完成对数据库的连接和操作。在这里我们还写了一共封住了Druid连接池的工具类
就是JDBCUtilsBYdruid,这里我们需要注意一下,我们通过连接池连接数据库和我们直接连接数据库需要的信息是一样的,需要url,user,pwd,driver的信息的,所以我们就将其放在properties配置文件里了,我们通过properties.load(new(FileInputStream)文件名)就可以读取到我们的配置文件的信息了
public class JDBCUtilsBYdruid {
public static DataSource ds ;
//从静态代码块完成ds的初始化
static{
Properties properties = new Properties();
try {
properties.load(new FileInputStream("src\\druid.properties"));
ds =DruidDataSourceFactory.createDataSource(properties);//在这里datasource已经读取了连接数据库的相关信息
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//使用datasource对数据库进行连接
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
public static void close(ResultSet resultSet,Statement statement,Connection connection ){
try {
if (resultSet!=null){
resultSet.close();
}
if (statement!=null){
statement.close();
}
if (connection!=null){
connection.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
public class BasicDao<T> {
private QueryRunner queryRunner=new QueryRunner();
//开发通用的dml方法,针对任意表
public int update(String sql,Object...parameters){
Connection connection=null;
try {
connection = JDBCUtilsBYdruid.getConnection();
int update = queryRunner.update(connection, sql, parameters);
return update;
} catch (SQLException e) {
throw new RuntimeException(e);//将编译异常转为运行异常->抛出
} finally {
JDBCUtilsBYdruid.close(null,null,connection);
}
}
/**
*
* @param sql 就是sql语句 可以有?
* @param clazz 传入一个类的Class对象,比如Actor.class
* @param parameters 可变参数,可以有多个
* @return 根据Actor.class 返回对应的Arraylist
*/
public List<T> queryMulti(String sql,Class<T> clazz,Object...parameters){
Connection connection=null;
try {
connection = JDBCUtilsBYdruid.getConnection();
//这个BeanListHandle实质上就是把我们的resultSet封装成一个Arraylist
return queryRunner.query(connection, sql, new BeanListHandler<T>(clazz), parameters);//返回的是一个集合
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JDBCUtilsBYdruid.close(null,null,connection);
}
}
//查询当行结果的通用方法
public T querySingle(String sql,Class<T> clazz,Object...parameters){
Connection connection=null;
try {
connection = JDBCUtilsBYdruid.getConnection();
return queryRunner.query(connection, sql, new BeanHandler<T>(clazz), parameters);//返回一个对象
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JDBCUtilsBYdruid.close(null,null,connection);//关闭连接
}
}
public Object queryScalar(String sql,Object...parameters){
Connection connection=null;
try {
connection = JDBCUtilsBYdruid.getConnection();
return queryRunner.query(connection, sql, new ScalarHandler(), parameters);//返回单个对象
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JDBCUtilsBYdruid.close(null,null,connection);
}
}
}
完成BasicDao后,其实我们可以发现,BasicDao这个类是有一个泛型的,其实这个主要是用于我们后面四个Dao的继承,四个Dao分别对应四个domain,就对应四个不同的类,所以在继承的时候,我们指定这个泛型的类型了,如果没有指定的话,我们通过从数据库返回的时候类型也肯定不一样,例如我们使用BillDao继承BasicDao,我们就指定我们Bill的类型,这样我们从数据库读取信息的时候,例如需要返回一行数据,那就会返回Bill对象,如果我们没有指定这个泛型的类型,返回的时候就会报错,或者把这些方法的返回类型都改成Object,但是我们的Bill、Employee又不是Object的子类,这里就会数据类型出错,而且我们使用泛型,还可以节省需要将返回的类型转换成我们需要类型的这个时间。
然后就到service层,service层就主要对Dao层已封装好的方法使用就可以了
EmployeeService
public class EmployeeService {
private EmployeeDao employeeDao=new EmployeeDao();
public Employee getEmployeeByIdAndPwd(String empID,String pwd){
//会返回一个对象
return employeeDao.querySingle("select * from employee where empId=? and pwd=md5(?) ", Employee.class, empID, pwd);
}
//在这里添加一个方法:增加雇员的信息
//个人思路:其实就是表中添加一条员工数据,调用EmployeeDao的方法update一条语句
public void addEmployee(String empId,String pwd,String name,String job){
int update = employeeDao.update("insert into employee values(null,?,?,?,?)", empId, pwd, name, job);
System.out.println(update>0 ?"执行成功":"执行失败");
}
//删除一条员工的数据
//个人思路:还是通过向表里删除一条员工的数据
public void deleteEmployee(String empId){
employeeDao.update("delete from employee where empId=? ",empId);
System.out.println("删除成功");
}
}
DiningTableService
public class DiningTableService {
private DiningTableDao diningTableDao=new DiningTableDao();
public List<DiningTable> getAllTable(){
diningTableDao = new DiningTableDao();
List<DiningTable> diningTables //返回的是一个Arraylist集合
= diningTableDao.queryMulti("select * from diningTable", DiningTable.class);
return diningTables;
}
//根据id查询对应的餐桌,如果返回null,则表明对应的餐桌不存在
public DiningTable getDiningTableById(int id){
//可以把sql语句放在查询分析器去测试一下,
return diningTableDao.querySingle("select * from diningTable where id=?",DiningTable.class,id);
}
//预定指定id的餐桌,并且传入预定人的姓名和电话,来修改餐桌的数据,
//我们可以根据id获取餐桌的信息,判断餐桌是否被预定,或者餐桌是否存在
public boolean orderTable(int id,String orderName,String orderTel){
if (getDiningTableById(id)==null){
System.out.println("该餐桌不存在");
return false;
}
if (getDiningTableById(id).getstate().equals("already booked")||getDiningTableById(id).getstate().equals("During meals")){
System.out.println("该桌已经被预定了或者正在就餐中");
return false;
}
int rows = diningTableDao.update
("update diningTable set state=?,orderName=?,orderTel=? where id=?", "already booked", orderName, orderTel, id);
System.out.println(rows>0 ?"预定成功":"预定失败");
return true;
}
//更新餐桌的状态的方法
public boolean changeState(int diningTableId,String state){
int update = diningTableDao.update("update diningTable set state =? where id=?", state, diningTableId);
return update>0;
}
//添加一个方法,修改餐桌的状态(姓名和电话),将餐桌状态设置为空闲状态
public boolean changeDiningTableToFree(int diningTableId,String state){
int update = diningTableDao.update("update diningTable set state =?,orderName='',orderTel='' where id=?", state, diningTableId);
return update>0;
}
}
foodmenuService
public class foodmunuService {
private foodmunuDao foodmunudao=new foodmunuDao();
public List<foodmunu> showAllFood(){
List<foodmunu> foodmunus = foodmunudao.queryMulti("select * from foodmunu", foodmunu.class);
return foodmunus;
}
//需要一个方法来根据菜品的ID获取菜品的价格
public foodmunu getFoodMenu(int foodmenuId){
return foodmunudao.querySingle("select * from foodmunu where id=?", foodmunu.class, foodmenuId);
}
}
BillService
public class BillService {
//定义BillDao属性
private BillDao billDao=new BillDao();
private foodmunuService foodmunuservice=new foodmunuService();//定义一个foodmunuService属性
private DiningTableService diningTableService=new DiningTableService();//定义一个DiningTableService属性
private MultiTableDao multiTableDao=new MultiTableDao();//定义一个MultTableDao属性,用于调用它的方法
//编写点餐的方法
public boolean orderMenu(int foodmeunID,int nums,int diningtableId){
String billID = UUID.randomUUID().toString();//随机生成一个单号UUID
//要计算money的金额
int rows = billDao.update("insert into Bill values(null,?,?,?,?,?,now(),'Not checked')",
billID, foodmeunID, nums, foodmunuservice.getFoodMenu(foodmeunID).getPrice()*nums, diningtableId);
if (!(rows>0)){
return false;
}
//更新对应餐桌的状态
if ((diningTableService.changeState(diningtableId,"During meals"))==false){
return false;
}
return true;
}
//显示账单,提供给view使用
public List<Bill> showBill(){
return billDao.queryMulti("select * from Bill ", Bill.class);
}
//用于多表查询
public List<MultiTableBean> showBill2(){
return multiTableDao.queryMulti("" +
"SELECT Bill.*,foodmunu.`name`,foodmunu.price " +
" FROM Bill,foodmunu " +
" WHERE Bill.`foodmeunId`=foodmunu.`id` ",MultiTableBean.class);
}
//根据餐桌号,查看某个餐桌是否有未结账的账单
public boolean hasPayBillByDiningTableId(int diningTableId){
Bill bill = billDao.querySingle("SELECT * FROM Bill WHERE diningtableID=? AND state='Not checked' LIMIT 0,1",
Bill.class, diningTableId);
if (bill==null){
System.out.println("该餐桌没有账单需要支付");
return false;
}else {
return true;
}
}
//完成结账(如果餐桌存在,并且餐桌有未结账的账单)
public boolean payBill(int diningTableId,String payMode){
//修改bill表
int update = billDao.update("update Bill set state=? where diningTableId=? and state='Not checked'",payMode,diningTableId);
if (update<0){//如果没有更新成功,则表示失败
System.out.println("修改失败");
return false;
}
//修改diningTable表()清空diningTable的 name和tel。
//注意:不要直接在这里操作,而应该调用diningTableService方法
boolean change = diningTableService.changeDiningTableToFree(diningTableId, "null");
if (change==false){
return false;
}
//过关斩将
return true;
}
}
最后就到view层,也就是界面层,该层最主要也就是调用service层封装好的方法了
public class MHLView {
private boolean loop=true;
private String key="";
//定义一个EmployeeService属性
private EmployeeService employeeService=new EmployeeService();
private DiningTableService diningTableService=new DiningTableService();
private foodmunuService foodmunuservice=new foodmunuService();//业务层
private BillService billService=new BillService();
public static void main(String[] args) {
MHLView mhlView = new MHLView();
mhlView.mainView();
}
public void addEmployee(){
Scanner scanner = new Scanner(System.in);
System.out.println("请输入添加员工的员工号");
String empId = scanner.next();
System.out.println("请输入添加员工登录密码");
String pwd = scanner.next();
System.out.println("请输入添加员工的姓名");
String name = scanner.next();
System.out.println("请输入添加员工的岗位");
String job = scanner.next();
employeeService.addEmployee(empId,pwd,name,job);
}
public void payBill(){
Scanner scanner = new Scanner(System.in);
System.out.println("结账服务");
System.out.println("请选择要结账的餐桌号(-1退出)");
int diningTableId = scanner.nextInt();
if (diningTableId==-1){
System.out.println("退出结账");
return;
}
System.out.println("请选择结账的方式(微信/支付宝/现金):");
String payMode = scanner.next();
System.out.println("是否结账");
String choice = scanner.next();
if (choice.equals("n")){
System.out.println("取消支付");
}
//首先要校验此餐桌是否存在
//先去校验该餐桌是否有账单需要支付
//再去完成支付账单的操作,因为如果该餐桌没有账单需要支付,你有对餐桌的数据操作,就会出错了
if (diningTableService.getDiningTableById(diningTableId)==null){
System.out.println("该餐桌不存在");
return;
}
if (billService.hasPayBillByDiningTableId(diningTableId)){
if (billService.payBill(diningTableId, "cash")){//调用payBill方法
System.out.println("支付成功");
}
}
}
//显示菜单
public void showAllBill(){
List<MultiTableBean> bills = billService.showBill2();
for (MultiTableBean bill : bills) {
System.out.println("编号\t\t菜品号\t菜品量\t\t金额\t\t桌号\t\t日期\t\t\t\t\t\t\t状态\t\t菜品名\t\t价格");
System.out.println(bill.getId()+"\t\t"+bill.getFoodmeunID()+"\t\t"+
bill.getNums()+"\t\t "+bill.getMoney()+"\t\t"+bill.getDiningtableId()+
"\t\t"+bill.getBillDate()+"\t\t"+bill.getState()+"\t"+bill.getname()+"\t\t"+bill.getprice());
}
System.out.println("显示完毕");
}
public void orderFoodMenu(){
Scanner scanner = new Scanner(System.in);
System.out.println("点餐服务");
System.out.println("请选择点餐的桌号:");
int diningTableId = scanner.nextInt();
DiningTable diningTableById = diningTableService.getDiningTableById(diningTableId);
if (diningTableById.getstate().equals("null")){//根据id得到一个diningTable,判断如果为null则不存在
System.out.println("该餐桌不存在");
return;
}
System.out.println("请选择要点菜品的编号");
int foodMenuId = scanner.nextInt();
//验证菜品编号是否正确
foodmunu foodMenu = foodmunuservice.getFoodMenu(foodMenuId);
if (foodMenu==null){
System.out.println("菜品号不存在");
return;
}
System.out.println("请选择菜品的数量");
int nums = scanner.nextInt();
System.out.println("是否要这个菜(y/n)");
String next = scanner.next();
if (next.equals("n")){
System.out.println("退出点餐服务");
return;
}
if (billService.orderMenu(foodMenuId, nums, diningTableId)){
System.out.println("添加成功");
}
}
//显示所有菜品
public void showAllFood(){
System.out.println("显示所有菜品");
List<foodmunu> foodmunus = foodmunuservice.showAllFood();
for (foodmunu foodmunu:foodmunus){
System.out.println("菜品编号\t\t菜品名\t\t类别\t\t价格\t\t");
System.out.println(foodmunu.getId()+"\t\t "+foodmunu.getName()+"\t\t"+
foodmunu.getCategory()+"\t\t"+foodmunu.getPrice());
}
System.out.println("==========菜品显示完毕==========");
}
//显示所有餐桌状态
public void showAllTable(){
System.out.println("显示餐桌状态");
List<DiningTable> allTable = diningTableService.getAllTable();
for (DiningTable diningTable:allTable){
System.out.println("餐桌编号 餐桌状态 ");
System.out.println(diningTable.getId()+"\t\t "+diningTable.getstate());
}
System.out.println("显示完毕");
}
//预定餐桌
public void orderTables(){
Scanner scanner = new Scanner(System.in);
System.out.println("预订餐桌");
System.out.println("请输入你要预定餐桌的编号1-3");
int id = scanner.nextInt();
System.out.println("你是否要预定(Y/N)");
String choice = scanner.next();
if (choice.equals("n")){
System.out.println("你已取消预定");
return;
}
System.out.println("请输入您的姓名");
String orderName = scanner.next();
System.out.println("请输入您的电话号码");
String orderTel = scanner.next();
diningTableService.orderTable(id,orderName,orderTel);
}
public void mainView(){
Scanner scanner = new Scanner(System.in);
while(loop){
System.out.println("=======满汉楼=======");
System.out.println("\t\t1.登录满汉楼");
System.out.println("\t\t2.退出满汉楼");
System.out.println("请输入你的选择");
key = scanner.next();
switch (key){
case "1":
System.out.println("登录满汉楼");
System.out.println("请输入员工号:");
String empId=scanner.next();
System.out.println("输入密码:");
String pwd=scanner.next();//这里要到数据库判断
Employee emp = employeeService.getEmployeeByIdAndPwd(empId, pwd);//返回一个对象
if(emp!=null){
System.out.println("登陆成功"+emp.getEmpId()+"\t"+emp.getName()+"\t"+emp.getJob());
//显示二级菜单,这里也要while循环
while(loop){
System.out.println("========满汉楼二级菜单=======");
System.out.println("\t\t1.显示餐桌状态");
System.out.println("\t\t2.预定餐桌");
System.out.println("\t\t3.显示所有菜品");
System.out.println("\t\t4.点餐服务");
System.out.println("\t\t5.查看账单");
System.out.println("\t\t6.结账");
System.out.println("\t\t7.添加员工");
System.out.println("\t\t9.退出满汉楼");
System.out.println("请输入你的选择");
key=scanner.next();
switch (key){
case "1":
showAllTable();//显示餐桌状态
break;
case "2":
orderTables();//预定餐桌
break;
case "3":
showAllFood();//显示所有菜品
break;
case "4":
orderFoodMenu();//点餐服务
break;
case "5":
showAllBill();
break;
case "6":
/*
1、对餐桌号进行校验
2、更新账单Bill的状态
3、更新diningTable的信息
4、不需要添加新表
*/
payBill();//支付账单
break;
case "7":
addEmployee();
break;
case "9":
System.out.println("退出满汉楼");
loop=false;
break;
default:
System.out.println("你的输入有误,请重新输入");
}
}
}else{
System.out.println("登陆失败");
}
break;
case "2":
System.out.println("退出满汉楼");
loop=false;
break;
default:
System.out.println("输入有误,请重新输入");
}
}
System.out.println("退出满汉楼系统");
}
}