完成时间:2021年1月16日
- 项目背景
- 课程设计目标
- 一、需求分析
- 二、概念设计
- 三、逻辑结构设计
- 四、基于JAVA语言的客户端程序实现
- 五、实现JDBC + PostgreSQL数据库
- 五、实践总结与体会
- 六、附录:代码、语句和命令等汇总(可以另外附加项目文件)
项目背景
美团模拟系统
线上订餐风靡初期,许多人们通过电话订餐,这种方式虽然可省去到店里的路途时间和等待时间,但会经常出现各种问题,例如:订餐后,饭店人员由于太过忙碌没有看清楚顾客的要求,或是当外卖送达后,顾客消失不见……等等一系列的问题。由此可见,使用手机应用程序进行线上订餐才能及时更新和查看外卖情况,才能同时保障顾客与商家的利益。
随着互联网技术的快速发展,互联网上诞生出这种便捷的订餐形式,也是电子商务应用的全新体现;从另一个侧面来看,网上订餐还起到了帮助推进电子商务的普及和应用进程的作用,网上订餐的形式,同时也在帮助加速电子商务应用的步伐。
随着时代发展的日益加快,我们身边每天都在发生日新月异的变化。不论在哪个行业里,用户几个大的根本需求永远不会变,比如说像省钱、懒。省钱”这个需求美团团购已经做到,现在该轮到“懒”这个需求。外卖一个就足够满足“懒”的需求——吃饭不出门。
目前《美团外卖》几乎垄断了国内外卖行业,在国内除了“饿了么”这位最大的竞争对手以外,可以说是没有有力的竞争对手存在了,综上,我们小组选择了设计模拟美团外卖的数据库课程设计,通过将现实生活中的问题转化成实际的数据库模型,再加以优化。
课程设计目标
- 学习和掌握主流数据库设计CASE工具的使用。
- 加深对数据库基础知识的理解,熟练掌握数据库的规范设计过程,训练和提高数据库设计的技能,实现理论与实践的结合。
- 了解系统开发过程中组织管理、质量评审方法和数据库性能优化措施,提高职业化水平。
- 学会自主性学习、研究性探索、汇报演示以及技术文档的撰写,促进研究能力、协作能力和创新能力的提高。
- 设计美团系统E-R模型图,设计模拟《美团外卖》的数据库系统,实现增、删、改、查四大基础功能。
- 设计触发器实现客户下订单、商家出餐等功能
一、需求分析
1.1 顾客需求分析
顾客功能模块分为信息管理、线上订餐、外卖查询、评价四大功能。(1) 信息部分包括个人信息即接收地址、签收姓名、联系电话;(2)线上订餐包括选择菜品、选择接收地址、选择签收人、备注信息以及付款方式;(3)外卖查询包括查询当前外卖定位情况,查询骑手电话。(4) 评价包括对骑手评价和对商家评价,分五个星级进行总评。
1.2 商家需求分析
商家功能模块分为信息管理、订餐确认、发出配送请求三个部分。(1)信息管理包括商店信息查询、修改、菜品种类以及价格管理; (2)订餐确认部分包括订餐、出餐、确认收餐;(3)发出配送请求包括向骑手发出配送的出发点和目的地、配送费、规定配送时间。
1.3 骑手需求分析
骑手功能模块分为信息管理、订单配送、超时检测三个部分。(1)信息管理包括骑手个人信息查询、修改;(2)订单配送包括确认当前订单状态,同时自动向顾客和商家发出自身定位。(3)超时检测包括系统检测每个订单完成时间,若超时则进行相应的处罚。
1.4 管理员需求分析
管理员功能模块分为用户信息管理、系统维护两个部分。(1) 用户信息管理包括顾客注册、商家注册、客户以及商家订单记录查询。
1.5 性能需求分析
该系统在性能功能上应达到如下需求:
(1)操作简单、界面友好,完全控件式地页面布局,使得菜品等信息地录入工作更简便,选择菜品只需通过鼠标;
(2)对常见地类似网站地管理地各个方面;
(3)基本信息录入、浏览、删除、修改、搜索等方面都大体实现;
(4)对客户预定餐饮信息地处理(包括录入、删除)将立即在主页地对应栏目显示出来;(5)达到“即时发布、即时见效”地功能;
(6)系统运行应该快速、稳定、高效和可靠;
(7)在结构上应具有很好地可扩展性,便于将来地功能扩展和维护。
二、概念设计
2.1 设计原则
(1)开放性、可扩充性、可靠性原则——开放系统是生产各种计算机产品普遍遵循地原则,遵循这种标准地产品都符合一些公共地、可以相互操作地标准,能够融洽地在一起工作.开放系统使得各种类型地网络和系统互连简单、标准统一,容易扩展升级.从而适应广大用户需求地多变性和产品地更新换代;
(2)良好地用户操作界面——用户操作界面美观、方便、实用,使用户能在较短地时间内掌握其使用方法。
(3)实用性原则——任何系统地设计都要考虑其实用性,系统开发地目地是为了实现业务处理自动化、规范化,提高工作效率,减轻工作人员地劳动强度,减少开支;
(4)工作平台——适用于不同地网络平台。
2.2 系统组织结构分析与设计
结合美团外卖APP应用与现在的外卖行业发展情况来对美团外卖系统进行分析如下,首先将其分为六个必不可少的模块,即信息管理、信息发布、意见反馈、食品管理、订单管理、送餐管理模块,其中每个模块分为三个子模块,如下图所示。
图2.2 系统组织结构图
考虑到此模拟系统共有四大群体,分别是客户、商家、骑手、管理员,接下来是针对四大群体的系统组织结构设计。
2.2.1 客户群体的系统组织结构
图2.2.1 客户程序设计框图
2.2.2 商家群体的系统组织结构
图2.2.2商家程序设计框图
2.2.3 骑手群体的系统组织结构
图2.2.3骑手系统组织设计框图
2.2.4 管理员群体的系统组织结构
图2.2.4管理员系统组织设计框图
2.2.5 系统的业务流程分析
图2.2.5 美团外卖服务系统业务流程图
2.3 系统E-R图设计
根据实际情况,我们区分出了五个实体,分别是客户、商家、骑手、菜品、管理员,其中最为关键的是管理员,它可管理其他除订单外所有实体,而订单只能由商家和客户管理。
图2.3 ER模型图
2.4 数据流图设计
图2.4 数据流图
2.5 数据字典
2.5.1 客户(client)
表2.5.1 客户数据字典图
2.5.2 管理员(admin)
表2.5.2 管理员数据字典图
2.5.3 商家(provider)
表2.5.3 商家数据字典图
2.5.4 菜品(dish)
表2.5.4 菜品数据字典图
2.5.5 订单(order)
表2.5.5 订单数据字典图
2.5.6 骑手(rider)
表2.5.6 骑手数据字典图
2.5.7 管理员操作(admin_x_x)
表2.5.7 管理员操作数据字典图
三、逻辑结构设计
3.1 E-R图向关系初步转换
其中加粗的为主键、加p的为外键。)
客户(客户编号,客户昵称,客户联系电话,客户地址,密码,状态)
管理员(管理员编号,管理员名称,密码)
菜品(菜品编号,菜品名称,菜品价格,菜品折扣)
商家(商家编号,商家状态,商家地址,商家联系电话号码,信誉星级)
骑手(骑手编号,骑手联系方式,配送费,配送状态,骑手实名制昵称)
订单(订单编号,p客户编号,p菜品编号,p商家编号,p骑手编号,订单时间,数量,状态)
四、基于JAVA语言的客户端程序实现
对于3.1中系统组织结构分析与设计的内容,现对JAVA程序分为八大模块,即: (1)客户登录窗口、(2) 客户操作主窗口(3) 商家登录窗口(4) 商家操作主窗口 (5) 骑手登录窗口 (6) 骑手操作主窗口(6) 商家操作主窗口 (7) 管理员登录窗口 (8) 管理员操作主窗口。接下来将通过代码对重要模块进行详细的撰述。
4.1 客户登录窗口
为了设计出更友好的交互界面,我们采用了Swing包中的JFrame窗口、Jpanel面板、JButton按钮、JLayeredPane分层面板、
CLIENT_LOAD.JAVA程序代码如下:
import java.awt.event.*;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class CLIENT_LOAD extends JFrame {
private int WIDTH = 400; // 窗口宽度
private int HEIGHT = 270; // 窗口高度
private int WIDTH_next = 400; //下个窗口的宽度
private int HEIGHT_next = 270; //下个窗口的高度
private String USER = ""; // 记录账号
private String PASSWORD = ""; // 记录密码
JLabel label = new JLabel();
JLabel title = new JLabel("美 团 外 卖 模 拟 系 统");
/*--------定义其他窗口----------*/
FRAME_WAR frame_war = new FRAME_WAR(); //警告提示的窗口
/*--------定义面板-------------*/
JLayeredPane jpanel_main = new JLayeredPane(); //分层面板
JPanel jpanel_background = new JPanel(); //专门放背景图的面板
/*-------定义按钮-----------*/
button login = new button("登 陆");
/*-------定义两个输入框-------*/
textUser user = new textUser("");
textPassword password = new textPassword("");
/*-------定义多选框---------*/
checkOne remember = new checkOne("记住密码");
/*-----------定义标签-----------*/
JButton b_register = new JButton("注册账号");
JButton b_adminLogin = new JButton("管理员登录");
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
CLIENT_LOAD() {
super("美团外卖模拟系统 ");
init();
}
public void init() {
this.setResizable(false);
initFrame();
initBackground();
initPanel();
initButton();
initText();
initCheckBox();
try {
initFile();
} catch (IOException e) {
e.printStackTrace();
}
this.setVisible(true);
}
public void initPanel() {
jpanel_main.setBounds(0, 0, WIDTH, HEIGHT);
jpanel_background.setBounds(0, -5, WIDTH, HEIGHT);
jpanel_main.add(jpanel_background, JLayeredPane.DEFAULT_LAYER);
jpanel_background.add(label);
/*-------- 除了背景图片,其他组件添加时候要加上JLayeredPane.MODAL_LAYER*/
jpanel_main.add(login, JLayeredPane.MODAL_LAYER);
jpanel_main.add(user, JLayeredPane.MODAL_LAYER);
jpanel_main.add(password, JLayeredPane.MODAL_LAYER);
jpanel_main.add(remember, JLayeredPane.MODAL_LAYER);
jpanel_main.add(b_register, JLayeredPane.MODAL_LAYER);
jpanel_main.add(b_adminLogin, JLayeredPane.MODAL_LAYER);
jpanel_main.add(title, JLayeredPane.MODAL_LAYER);
}
public void initButton() {
login.setActionCommand("登录"); // 设置按钮监听命令
b_register.setActionCommand("注册"); // 设置按钮监听命令
login.setBounds((WIDTH / 2) - (login.width / 2), 200, login.width,
login.height); //设置按钮位置
login.setBackground(Color.ORANGE); //设置背景颜色
login.setFont(new Font("幼圆", Font.BOLD, 15)); //设置 按钮标题字体
login.setFocusPainted(false); //去掉标题边框
login.setBorderPainted(false); //去掉按钮边框
login.addActionListener(new BottonMonitor());
b_register.addActionListener(new BottonMonitor());
b_register.setBounds(30, 200, 100, 25);
b_register.setFont(new Font("幼圆", 1, 14));
b_adminLogin.setFont(new Font("幼圆", 1, 14));
b_adminLogin.setBounds(270, 200, 110, 25);
b_adminLogin.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
new ADMIN_LOAD();
dispose();
}
});
}
public void initText() {
JLabel l_user = new JLabel("账号:");
JLabel l_pw = new JLabel("密码:");
jpanel_main.add(l_user, JLayeredPane.MODAL_LAYER);
jpanel_main.add(l_pw, JLayeredPane.MODAL_LAYER);
l_user.setFont(new Font("幼圆", Font.BOLD, 16));
l_pw.setFont(new Font("幼圆", Font.BOLD, 16));
l_pw.setBounds((WIDTH / 2) - 100, (HEIGHT / 2) - 25, 45, 40);
l_user.setBounds((WIDTH / 2) - 100, (HEIGHT / 2) - 65, 45, 40);
l_user.setForeground(new Color(252, 150, 25, 208));
l_pw.setForeground(new Color(252, 150, 25, 208));
user.setBounds((WIDTH / 2) - 60, (HEIGHT / 2) - 60, password.width,
password.height);
password.setBounds((WIDTH / 2) - 60, (HEIGHT / 2) - 20, user.width,
user.height);
user.setOpaque(false);
password.setOpaque(false); //设置为透明
}
public void initCheckBox() {
remember.setBounds(200, 145, remember.width, remember.height);
remember.setOpaque(false);
}
public void initFile() throws IOException {
File file = new File("D:" + File.separator + "log.txt");
if (!file.getParentFile().exists()) { //父路径不存在
file.getParentFile().mkdirs(); //创建所有的父路径
}
RandomAccessFile raf = new RandomAccessFile(file, "rw"); // 读写模式打开
String[] cmd = raf.readLine().split("--");
for (int i = 0; i < cmd.length; ++i) {
if (cmd[i].equals("USER")) {
if ((cmd[i + 1].length() >= 4) && (cmd[i + 1].length() <= 10)) {
user.setText(cmd[i + 1]);
USER = cmd[i + 1];
}
} else if (cmd[i].equals("PASSWORD")) {
if ((cmd[i + 1].length() >= 4) && (cmd[i + 1].length() <= 10)) {
password.setText(cmd[i + 1]);
USER = cmd[i + 1];
}
}
}
}
public void initFrame() {
this.setSize(WIDTH, HEIGHT);
//this.setBounds(X,Y,WIDTH,HEIGHT);
this.setLocationRelativeTo(null); //窗口居中
this.setResizable(false);
this.add(jpanel_main);
}
public void initBackground() {
/*------设置背景图片------*/
label.setIcon(new ImageIcon("src/pic/loadBackground.gif"));
label.setBounds(0, 0, WIDTH, HEIGHT);
title.setBounds(65, 10, (WIDTH * 3) / 2, HEIGHT / 4);
/*--------设置标题-------*/
title.setFont(new Font("幼圆", Font.BOLD, 25));
title.setForeground(Color.blue);
}
/*-----更新下个窗口的大小信息-----*/
public void UpdateFrame(int NextWidth, int NextHeight) {
WIDTH = WIDTH_next;
HEIGHT = HEIGHT_next;
WIDTH_next = NextWidth;
HEIGHT_next = NextHeight;
}
public String getTime() {
return df.format(new Date());
}
class checkMore extends JCheckBox {
int X = WIDTH;
int Y = HEIGHT;
int width = 80;
int height = 35;
checkMore(String name) {
super(name);
}
}
class checkOne extends JRadioButton {
int X = WIDTH;
int Y = HEIGHT;
int width = 80;
int height = 35;
checkOne(String name) {
super(name);
}
}
class button extends JButton {
public int width = 100;
public int height = 30;
public button(String name) {
super(name);
}
}
class textUser extends JTextField {
public int width = 150;
public int height = 30;
public int X;
public int Y;
public textUser(String name) {
super(name);
this.setFont(new Font("黑体", Font.BOLD, 15));
}
}
class textPassword extends JPasswordField {
public int width = 150;
public int height = 30;
public int X;
public int Y;
public textPassword(String name) {
super(name);
Font font = this.getFont();
this.setFont(new Font(font.getName(), font.getStyle(), 20));
}
}
/*---------重写按钮被单击监听事件------------*/
class BottonMonitor implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
String name = e.getActionCommand();
if (name.equals("注册")) {
new CLIENT_REGISTER();
}
if (name.equals("登录")) {
/*------------记住密码操作-------------*/
if (remember.isSelected() == true) {
}
try {
if (PSQL_JDBC.select_client("c" + user.getText(),
new String(password.getPassword()))) {
frame_war.open(false);
PSQL_JDBC.setOlineClient();
System.out.println(getTime() + " 用户[" + user.getText() +
"]登录成功!");
dispose();
} else {
frame_war.open(true);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (name.equals("找回密码")) {
}
}
}
}
CLIENT_LOAD.java程序运行结果图:
图4.1 客户登陆窗口运行图
4.2 客户操作主窗口
针对客户的主要三大操作:订餐、查询订单、修改信息,我们借助JAVA中的JTabbedPanel面板,将一个窗口分为三个小模块,分别为buy, select, my,与美团外卖APP小程序对应的是三个界面,订餐、查询订单、我的信息。以下是CLIANET_MAIN.java程序代码:
package com;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class CLIENT_MAIN extends JFrame {
static DATA d = new DATA();
private int width = 460;
private int height = 600;
private int nowShop = 0;
JTabbedPane tp_menu = new JTabbedPane(JTabbedPane.BOTTOM);
JLayeredPane lp_buy = new JLayeredPane();
JLayeredPane lp_select = new JLayeredPane();
JLayeredPane lp_my = new JLayeredPane();
PANEL_SCROLL menu_shop = new PANEL_SCROLL(20, 20, 420, 0, 200, 440, 310); //定义一个可容纳20个按钮的滚动面板
CLIENT_MODIFY frame_modify = new CLIENT_MODIFY(getX(), getY(), getWidth(),
getHeight());
CLIENT_UPDATE frame_update = new CLIENT_UPDATE();
CLIENT_MINE p_mine = new CLIENT_MINE();
JLabel l_background = new JLabel();
JTabbedPane[] p_shops = new JTabbedPane[20];
PANEL_SCROLL[][] menu_dish = new PANEL_SCROLL[20][5]; // shops_menu[i][j] : 第i个商铺的第j个菜单属性
int shop_num = 6;
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //设置日期格式
CLIENT_MAIN() {
this.setResizable(false);
init();
tp_menu.add("首页", lp_buy);
tp_menu.add("订单", lp_select);
tp_menu.add("我的", lp_my);
tp_menu.add("退出登录", new JPanel());
tp_menu.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
int i = tp_menu.getSelectedIndex();
if (i == 3) {
new CLIENT_LOAD();
dispose();
}
}
});
tp_menu.setSelectedIndex(0);
this.setLocationRelativeTo(null); // 窗口默认居中
setVisible(true);
}
public void init() {
this.init_Button();
init_Panel();
init_Head();
loadShops();
loadDishs();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
frame_modify.goBack(getX(), getY(), getWidth(),
getHeight());
frame_update.goBack(getX(), getY());
}
}
}).start();
frame_update.addButtonAction(p_mine,
new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
p_mine.update();
}
});
loadMain();
setSize(width, height);
add(tp_menu);
}
/* 可按具体的宽高设置背景图片 */
public static ImageIcon _getIcon(String _filename, int _width, int _height) {
ImageIcon image = new ImageIcon(_filename); //实例化ImageIcon 对象
Image img = image.getImage();
image.setImage(image.getImage()
.getScaledInstance(_width, _height,
Image.SCALE_DEFAULT));
return image;
}
public void loadShops() {
for (int i = 0; i < shop_num; ++i)
menu_shop.addButton(d.getShopName(i),
"src/pic/shop" + (i + 1) + ".jpg", 150, 100, -1); //加入店铺
}
public void init_Button() {
/* 给每个商铺按钮分配空间 */
for (int i = 0; i < d.getShopNum(); ++i)
for (int j = 0; j < d.getShopMenuNum(i); ++j)
menu_dish[i][j] = new PANEL_SCROLL(20, 15,
d.getDishHeight(i, j), -40, 200, 440, 310);
/* 给每个按钮添加监听事件 用于实现点开店铺后进入另一个面板*/
for (int i = 0; i < menu_shop.getMaxNum(); ++i) {
menu_shop.addButtonAction(i, new Listener_toShop());
}
/* 设置 菜单各个属性按钮的 格式 */
for (int i = 0; i < d.getShopNum(); ++i) {
for (int j = 0; j < d.getShopMenuNum(i); ++j) {
for (int k = 0; k < d.getDishNum(i, j); ++k) {
menu_dish[i][j].setButtonTitle(k, d.getShopMenu(i, j));
menu_dish[i][j].setButtonSize(k, 120, 220);
}
}
}
p_mine.addButtonAction(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
frame_update.open();
}
});
}
public void addDish(int _dishNum, int _fileNum, String _dishName) {
}
public void init_Panel() {
tp_menu.setFont(new Font("幼圆", Font.BOLD, 30));
tp_menu.setBackground(new Color(236, 189, 14));
int i;
int j;
for (i = 0; i < d.getShopNum(); ++i) {
p_shops[i] = new JTabbedPane(JTabbedPane.LEFT);
p_shops[i].setFont(new Font("幼圆", Font.BOLD, 20));
p_shops[i].setBounds(0, 200, width - 20, height - 290);
for (j = 0; j < d.getShopMenuNum(i); ++j)
p_shops[i].add(d.getShopMenu(i, j), menu_dish[i][j]);
p_shops[i].add("返回", new JPanel());
p_shops[i].setSelectedIndex(0);
p_shops[i].setBackground(new Color(219, 148, 7));
p_shops[i].addChangeListener(new Listener_toMain());
}
}
public void loadDishs() {
for (int i = 0; i < d.getShopNum(); ++i) {
for (int j = 0; j < d.getShopMenuNum(i); ++j) {
for (int k = 0; k < d.getDishNum(i, j); ++k) {
menu_dish[i][j].addDish(frame_modify,
d.getDishName(i, j, k),
"src/pic/shop" + (i + 1) + "_" + (j + 1) + "_" +
(k + 1) + ".jpg", 210, 140, d.getPrice(i, j, k), i, j);
}
}
}
}
public void loadMain() {
l_background.setIcon(_getIcon("src/pic/background.jpg", width, 220));
lp_buy.removeAll();
lp_buy.add(l_background, 1);
lp_buy.add(menu_shop, 0);
lp_my.add(p_mine, 0);
}
public String getTime() {
return df.format(new Date());
}
public void init_Head() {
l_background.setIcon(_getIcon("src/pic/background.jpg", width, 220));
l_background.setBounds(0, 0, width, 220);
}
public void init_Dish() {
for (int i = 0; i < d.getShopNum(); ++i) {
p_shops[i].setSelectedIndex(0);
}
}
class Listener_toShop implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
int num = Integer.valueOf(cmd);
nowShop = num;
System.out.println(getTime() + " 退出了 [首页]");
lp_buy.removeAll();
init_Dish();
lp_buy.add(p_shops[num], 0);
lp_buy.add(l_background, 1);
l_background.setIcon(_getIcon("src/pic/shop" + (num + 1) + ".jpg",
width, 220));
System.out.println(getTime() + " 进入了 [" + d.getShopName(num) +
"] 店铺");
}
}
class Listener_toMain implements ChangeListener {
@Override
public void stateChanged(ChangeEvent e) {
int chose = p_shops[nowShop].getSelectedIndex();
if (chose == d.getShopMenuNum(nowShop)) {
System.out.println(getTime() + " 退出了 [" +
d.getShopName(nowShop) + "] 店铺");
frame_modify.close();
loadMain();
System.out.println(getTime() + " 进入了 [首页]");
} else {
System.out.println(getTime() + " 选择了 " + "[" +
d.getMenuName(nowShop, chose) + "]");
}
}
}
}
下面是CLIENT_MAIN.java程序运行的效果图:
图4.2.1 客户主窗口程序运行图
图4.2.2 客户主窗口中“我的“窗口运行图
五、实现JDBC + PostgreSQL数据库
5.1 准备PostgresSQL数据库环境
课程设计环境: windows10操作系统、PostgreSQL 13数据库服务
postgreSQL官网: https://www.postgresql.org/
PostgreSQL windows版本下载网址: https://www.postgresql.org/download/windows/
下载成功后进行安装,在安装过程中需设置密码,安装成功后配置系统环境变量。
5.1.1 配置环境变量
步骤1: 此电脑 -> 属性 -> 高级系统设置 -> 环境变量,在系统变量的窗口中新建一个变量存psql的安装地址。
图5.1.1.1 postgresql环境变量配置步骤图1
接下来进入系统变量中的Path,双击打开后新建
%PostgreSQL_HOME%\bin、%PostgreSQL_HOME%\lib、%PostgreSQL_HOME%\data,接着确定即可。
图5.1.1.2 postgresql环境变量配置步骤图2
5.1.2 两种方式进入psql数据库
1.使用SQL shell工具
首次连接直接连接默认地址、默认端口、默认database即可,回车四次后需输入用户postgres(默认)的口令,口令即为安装时设置的密码。
图5.1.2.1 通过shell连接psql图
2.使用pgAdmin4工具
该工具会在浏览器中打开,初次进入需要输入口令,和安装时设置的密码一致。
图5.1.2.2 使用pgAdmin4 工具在浏览器中进入psql数据库
5.2 在pgAdmin4中创建MT数据库
为更好的实现课程设计项目,我们将要所有的数据表存储在自己创建的数据库内便于区别默认的数据库postgres,也可以在sql shell中输入指令: create database MT; 来创建数据库。
图 5.2 在pgAdmin5 中创建数据库
5.3 在sql shell中创建数据表
根据第二部分概念设计中内容,需要创建的表有如下9个。
图 5.3 在psql中创建的表
5.3.1 创建客户信息表
客户(客户编号,昵称,密码,联系方式,状态,地址,账号余额),对于客户的信息表为区分于商家、骑手、管理员,所以需要在ID上加一个主键约束,另外增加一个check约束,限制id的形式为c开头的字符串。其中状态1表示为在线,0表示为不在线。
CREATE TABLE client (
c_id character varying(9) PRIMARY KEY NOT NULL CHECK (c_id LIKE 'c%'),
c_name character varying(20) NOT NULL,
c_pw character varying(20) NOT NULL,
c_tel character varying(11) NOT NULL CHECK (c_tel LIKE '1%') UNIQUE,
c_state int CHECK (c_state IN (1, 0)),
c_add character varying(20) NOT NULL,
c_money float CHECK (c_money >= 0)
);
5.3.2 创建商家信息表
商家(商家编号,密码,联系方式,店铺名称, 地址,信誉星级,状态,账号余额),其中状态分为三种,working ——营业中,relax—— 休息, gone ——停业
1 create table provider(
2 p_id character varying(9) primary key not null check(p_id like 'p%'),
3 p_pw character varying(20) not null,
4 p_tel character varying(20) not null unique check(p_tel like '1%'),
5 p_name character varying(20) not null,
6 p_add character varying(11) not null unique,
7 p_lev int,
8 p_state character varying(15) check(p_state in ('working','relax','gone')),
9 p_money float check (p_money >= 0)
10 );
5.3.3 创建骑手信息表
骑手(骑手编号,密码,名字,联系电话,地址,配送星级,配送状态,账号余额),其中配送状态和商家一样分为三种: Working——配送中,relax——休息, gone——下线
CREATE TABLE rider (
r_id character varying(10) PRIMARY KEY NOT NULL CHECK (r_id LIKE 'r%'),
r_pw character varying(20) NOT NULL,
r_name character varying(10) NOT NULL,
r_tel character varying(20) NOT NULL UNIQUE CHECK (r_tel LIKE '1%'),
r_add character varying(11) NOT NULL UNIQUE,
r_lev int,
r_state character varying(15) CHECK (r_state IN ('working', 'relax', 'gone')),
r_money float CHECK (r_money >= 0)
);
5.3.4 创建管理员信息表
管理员(管理员编号,名称,密码,联系电话,状态),其中状态表示是否在线:1——在线,0——不在线
CREATE TABLE admin (
a_id character varying(9) PRIMARY KEY NOT NULL CHECK (a_id LIKE 'a%'),
a_name character varying(20) NOT NULL,
a_pw character varying(20) NOT NULL,
a_tel character varying(11) NOT NULL CHECK (a_tel LIKE '1%') UNIQUE,
a_state int CHECK (a_state IN (1, 0))
);
5.3.5 创建菜品信息表
菜品(菜品编号,名称,价格,折扣,提供商编号,剩余量,出售量)
CREATE TABLE dish (
d_id int PRIMARY KEY NOT NULL,
d_name character varying(30) NOT NULL,
d_price float(2) NOT NULL,
d_discount float(2),
d_pid character varying(9) NOT NULL,
FOREIGN KEY (d_pid) REFERENCES provider (p_id),
d_stock int NOT NULL CHECK (d_stock >= 0),
d_sales int NOT NULL
);
5.3.6 创建订单信息表
订单(订单编号,客户id,骑手id,菜品id,商家id,时间,数量,客户地址,状态)
CREATE TABLE ord (
o_id int PRIMARY KEY NOT NULL,
o_cid character varying(9),
FOREIGN KEY (o_cid) REFERENCES client (c_id),
o_rid character varying(9),
FOREIGN KEY (o_rid) REFERENCES rider (r_id),
o_did int,
FOREIGN KEY (o_did) REFERENCES dish (d_id),
o_pid character varying(9),
FOREIGN KEY (o_pid) REFERENCES provider (p_id),
o_time date,
o_num int NOT NULL CHECK (o_num > 0),
o_cadd character varying(50) NOT NULL,
o_state character varying(15) CHECK (o_state IN ('wait', 'accept', 'cancel'))
);
5.3.7 创建管理客户信息表
管理客户(管理员编号,客户编号,时间,操作类别)
CREATE TABLE admin_client (
a_c_aid character varying(9),
FOREIGN KEY (a_c_aid) REFERENCES admin (a_id),
a_c_cid character varying(9),
FOREIGN KEY (a_c_cid) REFERENCES client (c_id),
a_c_time date,
PRIMARY KEY (a_c_aid, a_c_cid, a_c_time),
a_c_operation character varying(15) CHECK (a_c_operation IN ('update', 'insert', 'delete'))
);
5.3.8 创建管理商店信息表
管理商家(管理员编号,商家编号,时间,操作类别)
CREATE TABLE admin_provider (
a_p_aid character varying(9),
FOREIGN KEY (a_p_aid) REFERENCES admin (a_id),
a_p_pid character varying(9),
FOREIGN KEY (a_p_pid) REFERENCES provider (p_id),
a_p_time date,
PRIMARY KEY (a_p_aid, a_p_pid, a_p_time),
a_p_operation character varying(15) CHECK (a_p_operation IN ('allow', 'delete'))
);
5.3.9 创建管理骑手信息表
管理骑手(管理员编号,骑手编号,时间,操作类别)
CREATE TABLE admin_rider (
a_r_aid character varying(9),
FOREIGN KEY (a_r_aid) REFERENCES admin (a_id),
a_r_rid character varying(9),
FOREIGN KEY (a_r_rid) REFERENCES rider (r_id),
a_r_time date,
PRIMARY KEY (a_r_aid, a_r_rid, a_r_time),
a_r_operation character varying(15) CHECK (a_r_operation IN ('update', 'insert', 'delete'))
);
5.4 在IDEA中连接JAVA 与 postgreSQL数据库
5.4.1 根据图中的步骤在IDEA中导入数据库相关的jar包
图5.4.1 操作步骤1
图5.4.2 操作步骤2
图5.4.3 操作步骤3
图5.4.4操作步骤4
5.5 增——以注册功能为例实现JDBC向psql表中插入数据的功能
5.5.1 核心代码1: 注册按钮的监听事件
此监听事件实现的主要功能是在客户点击注册按钮后,将文本框里的内容传到PSQL_JDBC类中,在通过该check() 方法判断注册信息是否符合规则,如果符合则通过insert_client方法,插入数据到client 客户表中。
b_ok.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if(check()) {
PSQL_JDBC.insert_client(userid, username, password, address, tel);
JOptionPane.showMessageDialog(panel, "恭喜您!注册成功!", "消息提示:",JOptionPane.WARNING_MESSAGE); //弹出信息框
} else JOptionPane.showMessageDialog(null, "抱歉!注册失败,请再次检查您输入的信息是否符合规范", "消息提示:",JOptionPane.ERROR_MESSAGE); //弹出信息框
}
});
5.5.2 核心代码2: 连接postgresql数据库
public static void getConn() {
String driver = "org.postgresql.Driver";
//String url = "jdbc:postgresql://10.5.14.70:5432/u19211870125";
String url = "jdbc:postgresql://10.5.14.70:5432/u19211870125";
String username = "u19211870125";
String password = "centos@123";
Connection a = null;
try {
Class.forName(driver); // classLoader,加载对应驱动
a = (Connection) DriverManager.getConnection(url, username, password);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
conn = a;
}
5.5.3 核心代码3: 通过JAVA向数据库表插入数据
public int insert(Client client) {
if(conn == null) getConn();
int i = 0;
String sql = "insert into client (c_id,c_name,c_pw,c_tel,c_state,c_add,c_money) values(?,?,?,?,?,?,?)";
PreparedStatement pstmt;
try {
pstmt = (PreparedStatement) conn.prepareStatement(sql);
pstmt.setString(1, client.getId());
pstmt.setString(2, client.getName());
pstmt.setString(3, client.getPw());
pstmt.setString(4, client.getTel());
pstmt.setInt(5, client.getState());
pstmt.setString(6, client.getAdd());
pstmt.setDouble(7, client.getMoney());
i = pstmt.executeUpdate();
pstmt.close();
//conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
return i;
}
5.6 删——以管理员并发测试为例实现JDBC删除psql中表的数据
在管理员操作主窗口中对”删除所有客户”的按钮添加了监听事件,即被单击时会执行的代码内容,代码如下:
private class buttonListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
int cmd = Integer.valueOf(e.getActionCommand());
switch (cmd){
case 0:
insert_client(1000); //插入1000k个客户
break;
case 1:
insert_riders(1000); //插入1000k个骑手
break;
case 2:
insert_client(10); //插入10k个客户
break;
case 3:
insert_riders(10); //插入10k个骑手
break;
case 4:
System.out.println(getTime() + " 成功清除 " + PSQL_JDBC.clear_client() + " 个客户!");
break;
case 5:
System.out.println(getTime() + " 成功清除 " + PSQL_JDBC.clear_rider() + " 个骑手!");
break;
case 6:
insert_client(1); //插入1k个客户
break;
case 7:
insert_riders(1); //插入1k个骑手
break;
}
}
}
在上述代码中,根据程序的UI设计主要是在第5、6个按钮被单击后会执行删除操作。在PSQL_JDBC.java程序中,删除的方法如下:
public static int clear_client() {
if(!operation_client.isOK()){return -1; }
int i = operation_client._getRow("client");
String sql = "delete from client" ;
PreparedStatement pstmt;
try {
pstmt = (PreparedStatement) operation_client.conn.prepareStatement(sql);
pstmt.executeUpdate();
pstmt.close();
//operation_client.conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
return i;
}
该方法通过借助PreparedStatement类 成功执行sql语句 delete from client,并且最后会返回删除的总个数。
5.7 改——以统计在线人数功能为例实现对表的数据进行修改
在CLIENT_LOAD.java程序中即客户登录界面,当登录按钮被单击后,调用JDBC类查询密码是否正确,如果正确则允许登陆,同时调用方法setOnlineClient(),作用为设置当前登陆的客户的c_state属性为1,即在线的状态。
try {
if(PSQL_JDBC.select_client("c" + user.getText(), new String(password.getPassword()))){
frame_war.open(false);
PSQL_JDBC.setOnlineClient();
System.out.println(getTime() + " 用户[" + user.getText() + "]登录成功!");
dispose();
}
else frame_war.open(true);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
上面的代码主要是对登陆按钮进行了监听,接下来是具体实现更改操作,setType属性主要负责更新client的表中,属性为type,客户编号为id,修改成text值。以此我们可以更方便的实现下一次的更改操作。
public void setType(String type, String id, String text){
if(conn == null) getConn();
String sql = "update client set "+ type + "=" + text + " where c_id = \'" + id + "\'";
PreparedStatement pstmt;
try {
pstmt = (PreparedStatement) conn.prepareStatement(sql);
pstmt.executeUpdate();
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void setOnlineClient(){
op.setType("c_state",client.getId(),"1");
}
5.8 查——查询某一个表中的所有数据
public Integer getAll(String table) {
if(conn == null){ return -1;}
String sql = "select * from " + table;
PreparedStatement pstmt;
try {
pstmt = (PreparedStatement)conn.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery();
int col = rs.getMetaData().getColumnCount();
System.out.println("============================");
while (rs.next()) {
for (int i = 1; i <= col; i++) {
System.out.print(rs.getString(i) + "\t");
if ((i == 2) && (rs.getString(i).length() < 8)) {
System.out.print("\t");
}
}
System.out.println("");
}
System.out.println("============================");
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
5.9 功能模块——客户注册与登陆
对于客户来说,注册和登陆是必要的流程,据此我们设计的程序中包含了此功能,实质上是对数据表的基本操作,比如注册功能实现的是在client表中插入数据,而登陆功能则是在数据表中查询客户的编号和密码是否匹配。
图5.9.1 客户注册账号界面图
如上图所示,当点击“确认注册”后,JAVA程序将sql语句select count(*) from where c_id = ‘cycc’; 传入psql数据库中,如果返回的结果中有1那么就提示注册失败,请用户重新输入。
图5.9.2 客户登陆界面图
对于登陆界面,比如输入的账号是”cycc”,点击登陆按钮后,JAVA程序将sql语句select c_pw from client where c_id = ‘ccycc’; 传入psql数据库中,如果返回的pw字符串与当前密码框输入的字符串相同的话则成功登陆。接下来是两个功能成功测试的动态效果图。
图5.9.3 实现注册+登陆功能的动态运行图
5.10 功能模块——客户点餐
对于美团用户来说,点餐可以说是必要的功能了,虽然时间有限,但我们依然能实现GUI层次的点餐,接下来是该模块的核心代码。
CLIENT_MODIFY.java程序代码:
package com;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class CLIENT_MODIFY extends JFrame {
PANEL_SCROLL p = new PANEL_SCROLL(20,15,400,-40,200,440,310);
private int width = 300, height = 600;
private int money = 0;
private int []dishSum = new int [30]; //存购物车按钮的累计个数
private int []dishNum = new int [30]; //存购物车第i个按钮的id号
private int shopNum,shopMenuNum;
JLabel l_showMoney = new JLabel(" 总价: ");
JButton b_clear = new JButton("清空清单");
JButton b_buy = new JButton("确认购买");
CLIENT_MODIFY(int x, int y, int w, int h){
this.setResizable(false);
setBounds(x + w, y, width, height);
p.setBounds(0,0, width, height);
p.setMaxScorll(1200);
p.setPanelLocation(-100, getY());
p.setButtonTextPosition(false);
p.setPanelLayout(new GridLayout(20,1));
l_showMoney.setFont(new Font("幼圆", Font.BOLD,30));
b_clear.setFont(new Font("幼圆", Font.BOLD,30));
b_buy.setFont(new Font("幼圆", Font.BOLD,30));
b_clear.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
p.setVisible(false);
clearDish();
p.removePanelAll();
p.addToPanel(b_clear);
p.addToPanel(b_buy);
p.addToPanel(l_showMoney);
money = 0;
l_showMoney.setText(" 总计: " + getPrice() + " 元");
p.setVisible(true);
}
});
b_buy.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if(getPrice() <= PSQL_JDBC.getClientA("id", new String(PSQL_JDBC.client.getId()))){
}
}
});
p.addToPanel(b_clear);
p.addToPanel(b_buy);
p.addToPanel(l_showMoney);
add(p);
}
public void open(){setVisible(true); }
public void close(){setVisible(false);}
public void goBack(int x, int y,int w, int h){setBounds(x + w - 15 , y, width, height);}
public void addPrice(int i){money += i;}
public int getPrice(){return money;}
public boolean canAddDish(){return p.getButtonNum() < 5; }
public void setShopNum(int a) {shopNum = a;}
public void setShopMenuNum(int a){shopMenuNum = a;}
public void addSameDish(int i){
dishSum[dishNum[i]]++;
}
public int getDishNum(int i){
if(dishNum[i] == 0) dishNum[i] = p.getButtonNum();
return dishSum[dishNum[i]];
}
public void updateDish(int i, String title){
addSameDish(i);
p.setButtonTitle(dishNum[i] - 1, title + " X " + dishSum[dishNum[i]]);
}
//判断第i个菜肴是否已经在购物车中
public boolean DishIsExisted(int i){ return !(dishNum[i] == 0 && dishSum[dishNum[i]] == 0); }
public int setDishNum(int i){
dishNum[i] = p.getButtonNum() + 1;
dishSum[dishNum[i]] = 1;
return 1;
}
public void clearDish(){
for(int i = 0; i < p.getButtonNum(); ++i) {
dishNum[i] = 0;
dishSum[dishNum[i]] = 0;
}
}
}
在该模块中若要实现点餐的功能,需要判断客户的账号余额是否足够,在足够的情况下才会发起订单,接着商家可以接受到订单,否则就点餐失败,那么接下来给出获取某个客户账号余额的关键代码:
public double getClientA(String type, String id){
if(conn == null) getConn();
double i = 0;
String sql = "select "+ type +" from client where c_id = " + "\'" + id+"\'";
PreparedStatement pstmt;
try {
pstmt = (PreparedStatement) conn.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery();
while(rs.next())
i = Double.valueOf(rs.getString(1));
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
return i;
}
在上述代码基础下调用getClientA("c_money",client.getId())就可实现查询客户余额。
图5.10 点餐测试Gif图
5.11 功能模块——管理员添加百万客户
该功能模块可以很好的测试postgreSQL数据库的性能,不过在添加百万客户的同时需要有产生随机字符串的方法,接下来是相关代码:
public static String getRandomString(int length){
String str="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
Random random=new Random();
StringBuffer s=new StringBuffer();
for(int i=0;i<length;i++){
int number=random.nextInt(62);
s.append(str.charAt(number));
}
return s.toString();
}
public static String getRandomTel(){
String str="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ00123456789";
Random random = new Random();
StringBuffer s = new StringBuffer();
s.append(1);
for(int i=0;i<10;i++){
int number=random.nextInt(62);
s.append(str.charAt(number));
}
return s.toString();
}
public void insert_client(int sum){
String name[] = new String[1000];
String tel[] = new String[1000];
String id[] = new String[1000];
String sql[] = new String[1000];
int num = 0;
for(int k = 1; k <= sum;++k) {
for (int i = 0; i < 1000;++num, ++i) {
name[i] = getRandomString(5);
id[i] = getRandomString(5);
tel[i] = getRandomTel();
sql[i] = "insert into client (c_id,c_name,c_pw,c_tel,c_state,c_add,c_money) values(\'c"+ id[i] +"\',\'"+name[k] +"\',\'123456\',\'" + tel[i] + "\',0,\'wenzhoudaxue\',0);";
}
PSQL_JDBC.insert_clients(sql);
System.out.println(getTime() + " 第 " + k + " 次操作: 插入 " + num + " 个客户 已完成!");
}
}
在上述代码的基础上能获取到100W个随机数据,但是没有判断重复的机制,因为在数据量很大的情况下,同时判断是否重复会给服务器增加负担,所以采取的策略便是100W个数据分成1000次连续插入1000个数据,这样能保证有效的插入也能防止一次性插入太多而卡顿。接下来是jdbc类中实现插入功能的关键代码:
public int inserts(String []sql) {
if(conn == null) getConn();
int i = 0;
Statement pstmt;
try {
pstmt = conn.createStatement();
for(int k = 0; k < sql.length;++k)
pstmt.addBatch(sql[k]);
pstmt.executeBatch();
pstmt.close();
//conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
return i;
}
图5.11 添加百万客户功能测试运行动态图
5.12 触发器设计
5.12.1 保证客户的余额始终大于0
在整个模拟系统中,当客户进行订餐后,数据库系统将对客户的余额进行查询,同时会更新余额,此时就需要保证余额一定要大于0,这个小功能可以用sql语句来实现,sql代码如下:
CREATE FUNCTION update_client_money()
returns trigger as $$
begin
update client set c_money = 0 where c_money < 0;
return old;
end;$$
language plpgsql;
create triiger U_C_M
after update on client
for each row execute procedure update_client_money();
5.12.2 保证订单的id是递增的序列
由于各个商家以及各个客户是m对n的关系,所以肯定会产生许多不同的订单,此时为了更好的区分不同的订单,则采取设置主键o_id为递增序列的形式,首先创建一个序列,接着创建对应的执行函数、还有触发器。sql代码如下:
create sequence s_ord
start with 1
increment by 1
create function insert_ord()
returns trigger as $$
begin
select s_ord.nextval into new.o_id from dual;
return new;
end;$$
language plpgsql;
CREATE TRIGGER I_O
before insert on ord
for each row execute procedure insert_ord();
5.12.3 添加新订单
对于新订单的产生,我们设想当客户的余额有减少的时候,就说明客户在客户端进行了点餐,与此同时可以添加一个触发器对客户表client的update操作进行触发,触发事件则插入新的订单到ord表中,代码如下:
create function insert_ord2()
returns trigger as $$
begin
insert into ord(o_cid, o_num, o_cadd) values (old.c_id, 1, old.c_add);
return new;
end;$$
language plpgsql;
CREATE TRIGGER I_NewO
after update of c_money on client
for each row execute procedure insert_ord2();
五、实践总结与体会
本次课程设计主要是模拟《美团外卖》程序来设计数据库系统,在经过一学期对《数据库原理与应用》的学习,从最开始了解数据库概念在到后来的数据库系统设计,我们感受到了当代数据库存在的必要性以及应用广泛性,通过不断地学习与讨论,首先是需求分析、可行性分析,接着又是概念设计、逻辑结构设计、最后就是实现,包括基于JAVA的客户端实现以及JDBC+postgreSQL的实现,成功尝试了用JAVA程序连接psql数据库,并且最终实现了简要的数据库系统。
设计过程中,我们也会遇到各种各样的问题,首先是关于ER模型的建立,起初我们设计的模型还存在许多问题,在多次向老师请教之后才有了正确的方向,我们小组每位成员都承担着重要的角色,整个过程中,我们通过积极配合、积极讨论、积极学习来推进课程设计的发展,在这个过程中我们学到了团队合作,并收获了许多数据库系统设计的相关知识,尝试了使用PowerDesigner工具、postgreSQL工具、IDEA编辑器,并初次通过psql连接到了校园数据库系统里分配给我们的数据库。
优点:
(1) 使用Java语言与postgresql数据库连接,通过java程序对数据库进行了增、删、改、查的操作。
(2) 管理员登陆简便,只要账号和密码输入正确就可在后台管理客户账号。
(3) 设计的数据流图、字典逻辑清晰、简洁明了。
(4) 程序界面设计友好,比如客户、管理员窗口可进行互相转换。
缺点:
(1) 使用到的触发器较少,对于数据库原理与应用课程设计,偏向的角度应该为数据库方面的学习,而我们大多时间花费在了JAVA程序设计上,由于是初次使用JAVA连接postgreSQL数据库,所以总是会出现各种各样的问题。
(2)在设计关系模型时,我们没有推算到更高的范式,由于属性错综复杂,经过小组多次讨论,决定以当前最合理的关系模型为主,若忽略部分属性,最多可达到3NF范式的要求,但是要达到更高范式就需要对关系模型进一步的分析了,所以有的属性是多余的存在。
(3) java程序功能较少,大多时候还得通过后台连接数据库进行操作。
(4) 缺少数据库维护的功能,由于时间有限,我们没有使用到数据库中的视图、模式、触发器,以此来实现数据库维护,以及事务日志也还未准备。
任务分配情况:
A = 需求分析
B = 可行性分析
C = 概念设计
D = 逻辑结构设计
E = 基于JAVA的客户端程序实现
F = 实现JDBC + psql数据库
G = 撰写课程设计报告
H = 修改、完善报告
I = 测试JAVA程序、调试功能
J = 制作汇报PPT
K = 修改、完善汇报PPT
本人(ABCDEFGK)
六、附录:代码、语句和命令等汇总(可以另外附加项目文件)
项目文件在同一个文件夹内
1.创建客户表
create table client(
c_id character varying(9) primary key not null check(c_id like 'c%'),
c_name character varying(20) not null,
c_pw character varying(20) not null,
c_tel character varying(11) not null check (c_tel like '1%') unique,
c_state int check (c_state in (1,0)),
c_add character varying(20) not null,
c_money float check (c_money >= 0)
);
2.创建商家表
create table provider(
p_id character varying(9) primary key not null check(p_id like 'p%'),
p_pw character varying(20) not null,
p_tel character varying(20) not null unique check(p_tel like '1%'),
p_name character varying(20) not null,
p_add character varying(11) not null unique,
p_lev int,
p_state character varying(15) check(p_state in ('working','relax','gone')),
p_money float check (p_money >= 0)
);
3.创建骑手表
create table rider(
r_id character varying(9) primary key not null check(r_id like 'r%'),
r_pw character varying(20) not null,
r_name character varying(10) not null,
r_tel character varying(20) not null unique check(r_tel like '1%'),
r_add character varying(11) not null,
r_lev int,
r_state character varying(15) check(r_state in ('working','relax','gone')),
r_money float check(r_money >= 0)
);
4.创建管理员表
create table admin(
a_id character varying(9) primary key not null check(a_id like 'a%'),
a_name character varying(20) not null,
a_pw character varying(20) not null,
a_tel character varying(11) not null check (a_tel like '1%') unique,
a_state int check (a_state in (1,0))
);
5.创建菜品表
create table dish(
d_id int primary key not null,
d_name character varying(30) not null,
d_price float(2) not null,
d_discount float(2),
d_pid character varying(9) not null,
foreign key(d_pid) references provider(p_id),
d_stock int not null check (d_stock >= 0),
d_sales int not null
);
6.创建订单
create table ord(
o_id int primary key not null,
o_cid character varying(9),
foreign key(o_cid) references client(c_id),
o_rid character varying(9),
foreign key(o_rid) references rider(r_id),
o_did int,
foreign key(o_did) references dish (d_id),
o_pid character varying(9),
foreign key(o_pid) references provider(p_id),
o_time date,
o_num int not null check(o_num > 0),
o_cadd character varying(50) not null,
o_state character varying(15) check (o_state in ('wait','accept','cancel')),
);
7.管理客户
create table admin_client(
a_c_aid character varying(9),
foreign key (a_c_aid) references admin(a_id),
a_c_cid character varying(9),
foreign key (a_c_cid) references client(c_id),
a_c_time date,
primary key (a_c_aid, a_c_cid, a_c_time),
a_c_operation character varying(15) check (a_c_operation in('update','insert','delete'))
);
8.管理商店
create table admin_provider(
a_p_aid character varying(9),
foreign key (a_p_aid) references admin(a_id),
a_p_pid character varying(9),
foreign key (a_p_pid) references provider(p_id),
a_p_time date,
primary key (a_p_aid, a_p_pid, a_p_time),
a_p_operation character varying(15) check (a_p_operation in('allow','delete'))
);
9.管理骑手
create table admin_rider(
a_r_aid character varying(9),
foreign key (a_r_aid) references admin(a_id),
a_r_rid character varying(9),
foreign key (a_r_rid) references rider(r_id),
a_r_time date,
primary key (a_r_aid, a_r_rid, a_r_time),
a_r_operation character varying(15) check (a_r_operation in('update','insert','delete'))
);
1.删除约束
alter table {TABLENAME} drop constraint if EXISTS {CHECKNAME};
2.删除客户的money约束
alter table client drop constraint if EXISTS client_c_money_check
3.级联删除执行函数
DROP FUNCTION {NAME}() CASCADE;
4.删除某个表中的触发器
drop trigger {triggerName} on {tableName};
5.设置序列下个的值
select setval('{NAME}', 0);
触发器设计的sql指令
--------------------------------------------
【功能:当客户的余额小于0时设置为0】
--------------------------------------------
-> 1. 创建触发的执行函数
CREATE FUNCTION update_client_money()
returns trigger as $$
begin
update client set c_money = 0 where c_money < 0;
return old;
end;$$
language plpgsql;
-> 2. 创建触发器 U_C_M
create triiger U_C_M
after update on client
for each row execute procedure update_client_money();
--------------------------------------------
【功能:插入订单时,订单id号自动递增】
--------------------------------------------
-> 1.创建订单递增序列
create sequence s_ord
start with 1
increment by 1
--------------------------------------------
-> 2.创建触发的执行函数
create function insert_ord()
returns trigger as $$
begin
select s_ord.nextval into new.o_id from dual;
return new;
end;$$
language plpgsql;
--------------------------------------------
--> 3. 创建触发器 I_O
CREATE TRIGGER I_O
before insert on ord
for each row execute procedure insert_ord();
--------------------------------------------
【功能:客户余额减少时,添加订单】
-> 1.创建触发的执行函数
create function insert_ord2()
returns trigger as $$
begin
insert into ord(o_cid, o_num, o_cadd) values (old.c_id, 1, old.c_add);
return new;
end;$$
language plpgsql;
-> 2.创建触发器
CREATE TRIGGER I_NewO
after update of c_money on client
for each row execute procedure insert_ord2();